Автоматты қамтамасыз ету Yealink T19 + динамикалық мекенжай кітабы

Мен осы компанияға жұмысқа келгенімде менде IP құрылғыларының кейбір деректер базасы, жұлдызшасы бар бірнеше серверлер және FreeBPX түріндегі патч болды. Сонымен қатар, Samsung IDCS500 аналогтық АТС параллель жұмыс істеді және тұтастай алғанда компаниядағы негізгі байланыс жүйесі болды, IP телефония тек сату бөлімі үшін жұмыс істеді. Бәрі осылай пісірілетін еді, бірақ жақсы күндердің бірінде барлығын IP телефонияға көшіру туралы қаулы шығарылды, мерзімдер келісілді, құрал-жабдықтар сатып алынды және кәсіпорынды 21 ғасырға көшіру жоспары жүзеге асырыла бастады.
Мұндай жағдайда алаңдата бастайтын бірінші нәрсе - бұл қандай да бір жолмен басқаруды қажет ететін телефон аппараттарының тез өсіп келе жатқан саны, екінші қатты алаңдатқан нәрсе - телефон кітапшасы. Егер Endpoint Manager бізге бірінші нұсқада көмектесе алса (айтпақшы, ол FreePBX соңғы нұсқаларынан алынып тасталған), онда кітапқа қатысты кейбір сұрақтар туындады:

  • Біріншіден, пайдаланушылардың орны/өткімділігі үнемі өзгеріп тұрғанда оның дәлдігін қалай қамтамасыз етуге болады?
  • Екіншіден, телефондарды қалай толығымен иесіздандыруға болады. Әрдайым контакт атын толтырмайсыз ба?

Мәселе қызық болды, шешімі көп күттірмеді. Енді мен толық тізімді беремін, содан кейін оны ретімен қараймыз.

from scapy.all import sniff
from scapy.layers.inet import IP
import mysql.connector
import ldap
import getpass
import tftpy
import requests
import os
import time
from string import replace

def conn_ldap(login):
    ad = ldap.initialize('ldap://***.local')
    ad.simple_bind_s('voip@***.local', 'password')
    basedn = 'OU=IT,DC=***,DC=LOCAL'
    basedn_user = 'OU=***,OU=***,DC=***,DC=LOCAL'
    scope = ldap.SCOPE_SUBTREE
    filterexp = "(&(sAMAccountName=" + login + ")(ObjectClass=person))"
    filterexp2 = "(&(ObjectClass=organizationUnit))"
    attrlist = ['cn']
    attrlist2 = ['OU']
    search = ad.search_s(basedn, scope, filterexp, attrlist)
    adname = search[0][1]['cn'][0].decode('utf-8')
    if adname == ' ':
        search = ad.search_s(basedn_user, scope, filterexp2, attrlist2)
        for i in range(1, len(search)+1):
            group = search[i][1]['ou'][0]
            basedn_user2 = 'OU='+group+','+basedn_user
            search = ad.search_s(basedn_user2, scope, filterexp, attrlist)
            adname = search[0][1]['cn'][0].decode('utf-8')
            if adname != ' ':
                return adname
        adname = search[0][1]['cn'][0].decode('utf-8')
    ad.unbind_s()
    return adname


def tftp_file_change(config,place,adname,current_account,current_account_password):

    client = tftpy.TftpClient("192.168.0.3", 69)
    client.download('template.cfg', place)
    fileread = open(place, 'r')
    line = fileread.readlines()
    fileread.close()
    line[5] = (('account.1.label = ').encode('utf-8') + adname.encode('utf-8') + 'n')
    line[2] = (('account.1.auth_name = ').encode('utf-8') + current_account.encode('utf-8') + 'n')
    line[3] = (('account.1.display_name = ').encode('utf-8') + current_account.encode('utf-8') + 'n')
    line[6] = (('account.1.password = ').encode('utf-8') + current_account_password[0][0] + 'n')
    filewrite = open(place, 'w')
    for i in line:
      filewrite.write(i)
    filewrite.close()
    print place
    print config
    client.upload(config,place)


def get_phone_inform(ipaddr):
    fileconf = requests.get('http://admin:admin@'+ipaddr+'/servlet?phonecfg=get[&accounts=1]')
    conf = fileconf.text.split('|')
    current_account = conf[2]
    return current_account


def sniff_frame():
    pcapf = sniff(count=1, timeout=70, filter="dst host 192.168.0.3 and port 5060")
    if len(pcapf) == 0:
        exit()
    frame = pcapf[0]
    macaddr = frame.src
    print macaddr[:8]
    if macaddr[:8] != '80:5e:c0':
        exit()
    ipaddr = frame[0][IP].src
    return macaddr, ipaddr


def conn_mysql(query,fquery,macaddr,qwery2):
    connect = mysql.connector.connect(host='192.168.0.3', database='voip', user='voip_wr', password='***')
    cursor = connect.cursor()
    cursor.execute(fquery)
    state = cursor.fetchall()
    state = bool(state[0][0])
    if state == True:
        cursor.execute(qwery2)
        connect.commit()
        connect.close()
    else:
        cursor.execute(query)
        connect.commit()
        connect.close()


def check_account(current_account):
    connect = mysql.connector.connect(host='192.168.0.3', database='asterisk', user='voip_wr', password='***')
    cursor = connect.cursor()
    qwery = 'select data from sip where id=' + current_account + ' and keyword="secret";'
    cursor.execute(qwery)
    password = cursor.fetchall()
    if password == ' ':
        exit()
    else:
        return password


if __name__ == '__main__':
    macaddr, ipaddr = sniff_frame()
    current_account = get_phone_inform(ipaddr)
    current_account_password = check_account(current_account)
    macaddr = macaddr.replace(':', '')
    ipaddr = ipaddr.decode('utf-8')
    adname = conn_ldap(getpass.getuser())
    query = 'INSERT INTO station (mac, ip, name, number) VALUES (' + '"' + macaddr + '",' + '"' + ipaddr + '",' + '"' + adname + '",' + '"' + get_phone_inform(ipaddr) + '"' + ')'
    qwery2 = 'UPDATE station SET ip=' + '"' + ipaddr + '"' + ', name=' + '"' + adname + '"' + ', number=' + '"' + get_phone_inform(ipaddr) + '"' + ' WHERE mac=' + '"' + macaddr + '"'
    fquery = 'SELECT EXISTS(SELECT mac FROM voip.station WHERE mac=' + '"' + macaddr + '")'
    query = query.encode('utf-8')
    fquery = fquery.encode('utf-8')
    config = macaddr + '.cfg'
    place = os.path.expanduser("~") + "" + "AppDataLocal" + config
    conn_mysql(query,fquery,macaddr,qwery2)
    tftp_file_change(config,place,adname,current_account,current_account_password)
    requests.get('http://admin:admin@'+ipaddr+'/cgi-bin/ConfigManApp.com?key=AutoP')
    requests.get('http://admin:admin@'+ipaddr+'/cgi-bin/ConfigManApp.com?key=Reboot')

Бағдарлама пайдаланушының компьютерінде жұмыс істейді және компьютер желіге телефон арқылы қосылған жағдайда жұмыс істейді, өйткені Yealink T19 шлюз ретінде жұмыс істей алмайды.

Біріншіден, біз түсінуіміз керек, ол байланысты ма? және біздің телефонда қандай Mac және IP бар.

def sniff_frame():
    pcapf = sniff(count=1, timeout=70, filter="dst host 192.168.0.3 and port 5060")
    if len(pcapf) == 0:
        exit()
    frame = pcapf[0]
    macaddr = frame.src
    print macaddr[:8]
    if macaddr[:8] != '80:5e:c0':
        exit()
    ipaddr = frame[0][IP].src
    return macaddr, ipaddr

Мұнда біз scapy құрылымынан sniff функциясын қолданамыз, оның көмегімен біз алдын ала анықталған udp пакетін аламыз, 70 секунд күтеміз және ештеңе ұстамасақ, шығамыз.

count=1, timeout=70, filter="dst host 192.168.0.3 and port 5060"

Әрі қарай, біз құрылғының шынымен Yealink екеніне көз жеткіземіз және қажетті мәндерді (ip және mac) қайтарамыз.

Арнайы сұрауды пайдалана отырып, біз телефондағы ағымдағы есептік жазбаны анықтаймыз. Ол үшін ағымдағы конфигурация телефоннан жүктеліп, талданады.

def get_phone_inform(ipaddr):
    fileconf = requests.get('http://admin:admin@'+ipaddr+'/servlet?phonecfg=get[&accounts=1]')
    conf = fileconf.text.split('|')
    current_account = conf[2]
    return current_account

Осы есептік жазбаның құпия сөзін табыңыз. Ол үшін жұлдызша.sip кестесіне және ондағы деректер өрісіне жүгінеміз.

def check_account(current_account):
    connect = mysql.connector.connect(host='192.168.0.3', database='asterisk', user='voip_wr', password='***')
    cursor = connect.cursor()
    qwery = 'select data from sip where id=' + current_account + ' and keyword="secret";'
    cursor.execute(qwery)
    password = cursor.fetchall()
    if password == ' ':
        exit()
    else:
        return password

Ал, соңғы кезеңде біз ldap AD-ге қосыламыз және функция арқылы алынған sAMAccountName пайдаланамыз getpass.getuser() ағымдағы пайдаланушының cn кодын алыңыз (ол әдетте пайдаланушының толық аты-жөнін қамтиды).

def conn_ldap(login):
    ad = ldap.initialize('ldap://***.local')
    ad.simple_bind_s('voip@***.local', 'password')
    basedn = 'OU=***,DC=***,DC=LOCAL'
    basedn_user = 'OU=***,OU=***,DC=***,DC=LOCAL'
    scope = ldap.SCOPE_SUBTREE
    filterexp = "(&(sAMAccountName=" + login + ")(ObjectClass=person))"
    filterexp2 = "(&(ObjectClass=organizationUnit))"
    attrlist = ['cn']
    attrlist2 = ['OU']
    search = ad.search_s(basedn, scope, filterexp, attrlist)
    adname = search[0][1]['cn'][0].decode('utf-8')
    if adname == ' ':
        search = ad.search_s(basedn_user, scope, filterexp2, attrlist2)
        for i in range(1, len(search)+1):
            group = search[i][1]['ou'][0]
            basedn_user2 = 'OU='+group+','+basedn_user
            search = ad.search_s(basedn_user2, scope, filterexp, attrlist)
            adname = search[0][1]['cn'][0].decode('utf-8')
            if adname != ' ':
                return adname
        adname = search[0][1]['cn'][0].decode('utf-8')
    ad.unbind_s()
    return adname

Біз дерекқордағы алдын ала жасалған кестеге қосыламыз (мен оны сол жерде жасадым) және біз білген барлық нәрсені енгіземіз, атап айтқанда: ip, mac, пайдаланушы аты.

def conn_mysql(query,fquery,macaddr,qwery2):
    connect = mysql.connector.connect(host='192.168.0.3', database='voip', user='voip_wr', password='***')
    cursor = connect.cursor()
    cursor.execute(fquery)
    state = cursor.fetchall()
    state = bool(state[0][0])
    if state == True:
        cursor.execute(qwery2)
        connect.commit()
        connect.close()
    else:
        cursor.execute(query)
        connect.commit()
        connect.close()

Біз осы жерде тоқтай аламыз, өйткені біз динамикалық мекенжай кітабын жасап қойдық, сіз сұрай аласыз, бірақ мен одан әрі бардым және құрылғыларды автоматты түрде қамтамасыз етуді осында қостым.

Ол үшін шаблон конфигурациясы алдын ала конфигурацияланған tftp серверінен жүктеледі, біз оған өзгерістер енгізіп, оны mac.cfg ретінде сақтаймыз. Яғни, Yealink үшін конфигурацияның екі түрі бар, біреуі жаһандық, екіншісі белгілі бір телефонға қолданылады және mac_phone.cfg пішімінде болуы керек.

Файлдағы барлық өзгерістерден кейін және оны tftp серверіне сақтағаннан кейін біз телефонға құрылғыны қамтамасыз ету және қайта жүктеу пәрменін береміз.

def tftp_file_change(config,place,adname,current_account,current_account_password):

    client = tftpy.TftpClient("192.168.0.3", 69)
    client.download('template.cfg', place)
    fileread = open(place, 'r')
    line = fileread.readlines()
    fileread.close()
    line[5] = (('account.1.label = ').encode('utf-8') + adname.encode('utf-8') + 'n')
    line[2] = (('account.1.auth_name = ').encode('utf-8') + current_account.encode('utf-8') + 'n')
    line[3] = (('account.1.display_name = ').encode('utf-8') + current_account.encode('utf-8') + 'n')
    line[6] = (('account.1.password = ').encode('utf-8') + current_account_password[0][0] + 'n')
    filewrite = open(place, 'w')
    for i in line:
      filewrite.write(i)
    filewrite.close()
    print place
    print config
    client.upload(config,place)

requests.get('http://admin:admin@'+ipaddr+'/cgi-bin/ConfigManApp.com?key=AutoP')
requests.get('http://admin:admin@'+ipaddr+'/cgi-bin/ConfigManApp.com?key=Reboot')

Құрылғыны қайта жүктегеннен кейін біз телефон экранында толық аты-жөнімізді аламыз + дерекқор түрінде әрқашан дұрыс толтырылған мекенжай кітабы, содан кейін мазмұнды динамикалық түрде көрсету үшін XML және кішкене PHP қосу ғана қалады. Мұндай мысалдар өте көп, тіпті YEALINK-тің өзінде де бар.

PS: Үлкенірек масштабтау үшін негізгі параметрлерді (айнымалыларды) бөлек файлға жылжытуға болады.

Ақпарат көзі: www.habr.com

пікір қалдыру