Browse Source

add support for ZTE-F601 registration

devel
Dmitry Novikov 7 years ago
parent
commit
614c7dbcb0
  1. 2
      devapp/base_intr.py
  2. 96
      devapp/dev_types.py
  3. 4
      devapp/forms.py
  4. 16
      devapp/locale/ru/LC_MESSAGES/django.po
  5. 1
      devapp/models.py
  6. 6
      devapp/onu_config/__init__.py
  7. 86
      devapp/onu_config/base.py
  8. 165
      devapp/onu_config/f601.py
  9. 141
      devapp/onu_config/f660.py
  10. 31
      devapp/views.py
  11. 4
      djing/lib/tln/__init__.py
  12. 274
      djing/lib/tln/tln.py
  13. 3
      docs/extra_func.md
  14. 5
      requirements.txt

2
devapp/base_intr.py

@ -70,7 +70,7 @@ class DevBase(object, metaclass=ABCMeta):
def validate_extra_snmp_info(v: str) -> None: def validate_extra_snmp_info(v: str) -> None:
""" """
Validate extra snmp field for each device. Validate extra snmp field for each device.
If validation failed then raise en exception from djing.lib.tln.ValidationError
If validation failed then raise en exception from devapp.onu_config.ExpectValidationError
with description of error. with description of error.
:param v: String value for validate :param v: String value for validate
""" """

96
devapp/dev_types.py

@ -2,11 +2,12 @@ import re
from typing import AnyStr, Iterable, Optional, Dict from typing import AnyStr, Iterable, Optional, Dict
from datetime import timedelta from datetime import timedelta
from easysnmp import EasySNMPTimeoutError from easysnmp import EasySNMPTimeoutError
from pexpect import TIMEOUT
from transliterate import translit from transliterate import translit
from django.utils.translation import gettext_lazy as _, gettext from django.utils.translation import gettext_lazy as _, gettext
from djing.lib import RuTimedelta, safe_int from djing.lib import RuTimedelta, safe_int
from djing.lib.tln.tln import ValidationError as TlnValidationError, register_onu_ZTE_F660
from devapp.onu_config import register_f601_onu, register_f660_onu, ExpectValidationError, OnuZteRegisterError
from .base_intr import ( from .base_intr import (
DevBase, SNMPBaseWorker, BasePort, DeviceImplementationError, DevBase, SNMPBaseWorker, BasePort, DeviceImplementationError,
ListOrError, DeviceConfigurationError ListOrError, DeviceConfigurationError
@ -135,7 +136,7 @@ class ONUdev(BasePort):
class OLTDevice(DevBase, SNMPBaseWorker): class OLTDevice(DevBase, SNMPBaseWorker):
has_attachable_to_subscriber = False has_attachable_to_subscriber = False
description = _('PON OLT')
description = 'PON OLT'
is_use_device_port = False is_use_device_port = False
def __init__(self, dev_instance): def __init__(self, dev_instance):
@ -194,7 +195,7 @@ class OLTDevice(DevBase, SNMPBaseWorker):
class OnuDevice(DevBase, SNMPBaseWorker): class OnuDevice(DevBase, SNMPBaseWorker):
has_attachable_to_subscriber = True has_attachable_to_subscriber = True
description = _('PON ONU')
description = 'PON ONU BDCOM'
tech_code = 'bdcom_onu' tech_code = 'bdcom_onu'
is_use_device_port = False is_use_device_port = False
@ -260,7 +261,7 @@ class OnuDevice(DevBase, SNMPBaseWorker):
try: try:
int(v) int(v)
except ValueError: except ValueError:
raise TlnValidationError(_('Onu snmp field must be en integer'))
raise ExpectValidationError(_('Onu snmp field must be en integer'))
def monitoring_template(self, *args, **kwargs) -> Optional[str]: def monitoring_template(self, *args, **kwargs) -> Optional[str]:
device = self.db_instance device = self.db_instance
@ -351,7 +352,7 @@ def conv_signal(lvl: int) -> float:
class Olt_ZTE_C320(OLTDevice): class Olt_ZTE_C320(OLTDevice):
description = _('OLT ZTE C320')
description = 'OLT ZTE C320'
def get_fibers(self): def get_fibers(self):
fibers = ({ fibers = ({
@ -413,8 +414,45 @@ class Olt_ZTE_C320(OLTDevice):
return 'olt_ztec320.html' return 'olt_ztec320.html'
def _reg_dev_zte(device, extra_data: Dict, reg_func):
if not extra_data:
raise DeviceConfigurationError(_('You have not info in extra_data '
'field, please fill it in JSON'))
ip = None
if device.ip_address:
ip = device.ip_address
elif device.parent_dev:
ip = device.parent_dev.ip_address
if ip:
mac = str(device.mac_addr) if device.mac_addr else None
# Format serial number from mac address
# because saved mac address was make from serial number
sn = "ZTEG%s" % ''.join('%.2X' % int(x, base=16) for x in mac.split(':')[-4:])
telnet = extra_data.get('telnet')
try:
onu_snmp = reg_func(
onu_mac=mac,
serial=sn,
zte_ip_addr=str(ip),
telnet_login=telnet.get('login'),
telnet_passw=telnet.get('password'),
telnet_prompt=telnet.get('prompt'),
onu_vlan=extra_data.get('default_vid')
)
if onu_snmp is not None:
device.snmp_extra = onu_snmp
device.save(update_fields=('snmp_extra',))
else:
raise DeviceConfigurationError('unregistered onu not found, sn=%s' % sn)
except TIMEOUT as e:
raise OnuZteRegisterError(e)
else:
raise DeviceConfigurationError('not have ip')
class ZteOnuDevice(OnuDevice): class ZteOnuDevice(OnuDevice):
description = _('ZTE PON ONU')
description = 'Zte ONU F660'
tech_code = 'zte_onu' tech_code = 'zte_onu'
def get_details(self) -> Optional[Dict]: def get_details(self) -> Optional[Dict]:
@ -462,7 +500,7 @@ class ZteOnuDevice(OnuDevice):
fiber_num, onu_port = v.split('.') fiber_num, onu_port = v.split('.')
int(fiber_num), int(onu_port) int(fiber_num), int(onu_port)
except ValueError: except ValueError:
raise TlnValidationError(_('Zte onu snmp field must be two dot separated integers'))
raise ExpectValidationError(_('Zte onu snmp field must be two dot separated integers'))
def monitoring_template(self, *args, **kwargs) -> Optional[str]: def monitoring_template(self, *args, **kwargs) -> Optional[str]:
device = self.db_instance device = self.db_instance
@ -489,42 +527,7 @@ class ZteOnuDevice(OnuDevice):
return '\n'.join(i for i in r if i) return '\n'.join(i for i in r if i)
def register_device(self, extra_data: Dict): def register_device(self, extra_data: Dict):
if not extra_data:
raise DeviceConfigurationError(_('You have not info in extra_data field, please fill it in JSON'))
device = self.db_instance
ip = None
if device.ip_address:
ip = device.ip_address
elif device.parent_dev:
ip = device.parent_dev.ip_address
if ip:
mac = str(device.mac_addr).encode()
# Format serial number from mac address
# because saved mac address was make from serial number
sn = (b'%.2X' % int(x, base=16) for x in mac.split(b':')[-4:])
sn = b"ZTEG%s" % b''.join(sn)
telnet = extra_data.get('telnet')
if telnet is None:
raise DeviceConfigurationError('For ZTE configuration needed "telnet" section in extra_data')
login = telnet.get('login')
password = telnet.get('password')
prompt = telnet.get('prompt')
default_vid = extra_data.get('default_vid')
if login is None or password is None or prompt is None:
raise DeviceConfigurationError('For ZTE configuration needed login, password and'
' prompt for telnet access in extra_data')
if default_vid is None:
raise DeviceConfigurationError('Please specify default vlan id "default_vid" for configuration onu')
stack_num, rack_num, fiber_num, new_onu_port_num = register_onu_ZTE_F660(
olt_ip=ip, onu_sn=sn, login_passwd=(login.encode(), password.encode()),
onu_mac=mac, prompt_title=prompt.encode(), vlan_id=int(default_vid)
)
bin_snmp_fiber_number = "10000{0:08b}{1:08b}00000000".format(rack_num, fiber_num)
snmp_fiber_num = int(bin_snmp_fiber_number, base=2)
device.snmp_extra = "%d.%d" % (snmp_fiber_num, new_onu_port_num)
device.save(update_fields=('snmp_extra',))
_reg_dev_zte(self.db_instance, extra_data, register_f660_onu)
def get_fiber_str(self): def get_fiber_str(self):
dev = self.db_instance dev = self.db_instance
@ -542,6 +545,13 @@ class ZteOnuDevice(OnuDevice):
) )
class ZteF601(ZteOnuDevice):
description = 'Zte ONU F601'
def register_device(self, extra_data: Dict):
_reg_dev_zte(self.db_instance, extra_data, register_f601_onu)
class HuaweiSwitch(EltexSwitch): class HuaweiSwitch(EltexSwitch):
description = _('Huawei switch') description = _('Huawei switch')
is_use_device_port = True is_use_device_port = True

4
devapp/forms.py

@ -4,7 +4,7 @@ from django.utils.translation import gettext_lazy as _
from django.db import IntegrityError from django.db import IntegrityError
from djing.lib import DuplicateEntry from djing.lib import DuplicateEntry
from djing.lib.tln.tln import ValidationError as TlnValidationError
from devapp.onu_config import ExpectValidationError
from . import models from . import models
from djing import MAC_ADDR_REGEX, IP_ADDR_REGEX from djing import MAC_ADDR_REGEX, IP_ADDR_REGEX
@ -43,7 +43,7 @@ class DeviceForm(forms.ModelForm):
manager_class = device.get_manager_klass() manager_class = device.get_manager_klass()
try: try:
manager_class.validate_extra_snmp_info(snmp_extra) manager_class.validate_extra_snmp_info(snmp_extra)
except TlnValidationError as e:
except ExpectValidationError as e:
raise ValidationError( raise ValidationError(
e, code='invalid' e, code='invalid'
) )

16
devapp/locale/ru/LC_MESSAGES/django.po

@ -30,18 +30,10 @@ msgstr "Свич D'Link"
msgid "does not fetch the mac" msgid "does not fetch the mac"
msgstr "не нашёл мак" msgstr "не нашёл мак"
#: dev_types.py:140
msgid "PON OLT"
msgstr ""
#: dev_types.py:169 views.py:345 views.py:503 #: dev_types.py:169 views.py:345 views.py:503
msgid "wait for a reply from the SNMP Timeout" msgid "wait for a reply from the SNMP Timeout"
msgstr "Время ожидания ответа от SNMP истекло" msgstr "Время ожидания ответа от SNMP истекло"
#: dev_types.py:199
msgid "PON ONU"
msgstr ""
#: dev_types.py:214 #: dev_types.py:214
msgid "Ip address or parent device with ip address required for ONU device" msgid "Ip address or parent device with ip address required for ONU device"
msgstr "" msgstr ""
@ -60,14 +52,6 @@ msgstr "Поле для snmp информации об ONU должно быть
msgid "Eltex switch" msgid "Eltex switch"
msgstr "Элтекс свич" msgstr "Элтекс свич"
#: dev_types.py:356
msgid "OLT ZTE C320"
msgstr ""
#: dev_types.py:419
msgid "ZTE PON ONU"
msgstr ""
#: dev_types.py:454 #: dev_types.py:454
msgid "Zte onu snmp field must be two dot separated integers" msgid "Zte onu snmp field must be two dot separated integers"
msgstr "" msgstr ""

1
devapp/models.py

@ -33,6 +33,7 @@ class Device(models.Model):
('Ex', dev_types.EltexSwitch), ('Ex', dev_types.EltexSwitch),
('Zt', dev_types.Olt_ZTE_C320), ('Zt', dev_types.Olt_ZTE_C320),
('Zo', dev_types.ZteOnuDevice), ('Zo', dev_types.ZteOnuDevice),
('Z6', dev_types.ZteF601),
('Hw', dev_types.HuaweiSwitch) ('Hw', dev_types.HuaweiSwitch)
) )
devtype = models.CharField(_('Device type'), max_length=2, default=DEVICE_TYPES[0][0], devtype = models.CharField(_('Device type'), max_length=2, default=DEVICE_TYPES[0][0],

6
devapp/onu_config/__init__.py

@ -0,0 +1,6 @@
from .f601 import register_onu as register_f601_onu
from .f660 import register_onu as register_f660_onu
from .base import (
ZteOltConsoleError, OnuZteRegisterError,
ZTEFiberIsFull, ZteOltLoginFailed, ExpectValidationError
)

86
devapp/onu_config/base.py

@ -0,0 +1,86 @@
import re
import sys
from pexpect import spawn
class ZteOltConsoleError(Exception):
pass
class OnuZteRegisterError(ZteOltConsoleError):
pass
class ZTEFiberIsFull(ZteOltConsoleError):
pass
class ZteOltLoginFailed(ZteOltConsoleError):
pass
class ExpectValidationError(ValueError):
pass
class MySpawn(spawn):
def __init__(self, *args, **kwargs):
super(MySpawn, self).__init__(encoding='utf-8', *args, **kwargs)
self.logfile = sys.stdout
def do_cmd(self, c, prompt):
self.sendline(c)
return self.expect_exact(prompt)
def get_lines(self):
return self.buffer.split('\r\n')
def get_lines_before(self):
return self.before.split('\r\n')
def parse_onu_name(onu_name: str, name_regexp=re.compile('[/:_]')):
gpon_onu, stack_num, rack_num, fiber_num, onu_num = name_regexp.split(onu_name)
return {
'stack_num': stack_num,
'rack_num': rack_num,
'fiber_num': fiber_num,
'onu_num': onu_num
}
def get_unregistered_onu(lines, serial):
for line in lines:
if line.startswith('gpon-onu_'):
spls = re.split(r'\s+', line)
if len(spls) > 2:
if serial == spls[1]:
onu_index, sn, state = spls[:3]
return parse_onu_name(onu_index)
def get_free_registered_onu_number(lines):
onu_type_regexp = re.compile(r'^\s{1,5}onu \d{1,3} type [-\w\d]{4,64} sn \w{4,64}$')
onu_olt_num = None
i = 0
for l in lines:
if onu_type_regexp.match(l):
# match line
i += 1
onu, num, onu_type, onu_type, sn, onu_sn = l.split()
onu_olt_num = int(num)
if onu_olt_num > i:
return i
return onu_olt_num + 1
def sn_to_mac(sn: str):
t = sn[4:].lower()
r = tuple(t[i:i + 2] for i in range(0, len(t), 2))
return '45:47:%s' % ':'.join(r)
def onu_conv(rack_num: int, fiber_num: int, port_num: int):
r = "10000{0:08b}{1:08b}00000000".format(rack_num, fiber_num)
snmp_fiber_num = int(r, base=2)
return "%d.%d" % (snmp_fiber_num, port_num)

165
devapp/onu_config/f601.py

@ -0,0 +1,165 @@
import re
from typing import Optional
from djing.lib import process_lock
from . import base
def get_onu_template(vlan_id: int, mac_addr: str):
template = (
'sn-bind enable sn',
'tcont 1 profile HSI_100',
'gemport 1 unicast tcont 1 dir both',
'switchport mode hybrid vport 1',
'switchport vlan %d tag vport 1' % vlan_id,
'port-location format flexible-syntax vport 1',
'port-location sub-option remote-id enable vport 1',
'port-location sub-option remote-id name %s vport 1' % mac_addr,
'dhcp-option82 enable vport 1',
'dhcp-option82 trust true replace vport 1',
'ip dhcp snooping enable vport 1'
)
return template
def get_pon_mng_template(vlan_id: int):
template = (
'service HSI type internet gemport 1 vlan %d' % vlan_id,
'loop-detect ethuni eth_0/1 enable',
'vlan port eth_0/1 mode tag vlan %d' % vlan_id,
'dhcp-ip ethuni eth_0/1 from-internet'
)
return template
def appy_config(onu_mac: str, sn: str, hostname: str, login: str, password: str, prompt: str, vlan: int):
onu_type = 'ZTE-F601'
# Входим
ch = base.MySpawn('telnet %s' % hostname)
ch.timeout = 15
ch.expect_exact('Username:')
ch.do_cmd(login, 'Password:')
choice = ch.do_cmd(password, ['bad password.', '%s#' % prompt])
if choice == 0:
raise base.ZteOltLoginFailed
ch.do_cmd('terminal length 0', '%s#' % prompt)
choice = ch.do_cmd('show gpon onu uncfg', ['No related information to show', '%s#' % prompt])
if choice == 0:
ch.close()
raise base.OnuZteRegisterError('unregistered onu not found, sn=%s' % sn)
elif choice == 1:
# Получим незареганные onu
unregistered_onu = base.get_unregistered_onu(
lines=ch.get_lines_before(),
serial=sn
)
if unregistered_onu is None:
ch.close()
raise base.OnuZteRegisterError('unregistered onu not found, sn=%s' % sn)
stack_num = int(unregistered_onu.get('stack_num'))
rack_num = int(unregistered_onu.get('rack_num'))
fiber_num = int(unregistered_onu.get('fiber_num'))
# Получим последнюю зарегистрированную onu
ch.do_cmd('show run int gpon-olt_%(stack)s/%(rack)s/%(fiber)s' % {
'stack': stack_num,
'rack': rack_num,
'fiber': fiber_num
}, '%s#' % prompt)
free_onu_number = base.get_free_registered_onu_number(
ch.get_lines_before()
)
if free_onu_number > 126:
ch.close()
raise base.ZTEFiberIsFull('olt fiber %d is full' % fiber_num)
# enter to config
ch.do_cmd('conf t', '%s(config)#' % prompt)
int_addr = '%d/%d/%d' % (
stack_num,
rack_num,
fiber_num
)
# go to olt interface
ch.do_cmd('interface gpon-olt_%s' % int_addr, '%s(config-if)#' % prompt)
# register onu on olt interface
ch.do_cmd('onu %d type %s sn %s' % (
free_onu_number,
onu_type,
sn
), '%s(config-if)#' % prompt)
# Exit from int olt
ch.do_cmd('exit', '%s(config)#' % prompt)
# Enter to int onu
ch.do_cmd('int gpon-onu_%(int_addr)s:%(onu_num)d' % {
'int_addr': int_addr,
'onu_num': free_onu_number
}, '%s(config-if)#' % prompt)
# Apply int onu config
template = get_onu_template(vlan, onu_mac)
for line in template:
ch.do_cmd(line, '%s(config-if)#' % prompt)
# Exit
ch.do_cmd('exit', '%s(config)#' % prompt)
# Enter to pon-onu-mng
ch.do_cmd('pon-onu-mng gpon-onu_%(int_addr)s:%(onu_num)d' % {
'int_addr': int_addr,
'onu_num': free_onu_number
}, '%s(gpon-onu-mng)#' % prompt)
# Apply config to pon-onu-mng
for line in get_pon_mng_template(vlan):
ch.do_cmd(line, '%s(gpon-onu-mng)#' % prompt)
# Exit
ch.do_cmd('exit', '%s(config)#' % prompt)
ch.close()
return base.onu_conv(
rack_num=rack_num,
fiber_num=fiber_num,
port_num=free_onu_number
)
else:
ch.close()
raise base.ZteOltConsoleError("I don't know what choice:", choice)
# Main Entry point
@process_lock
def register_onu(onu_mac: Optional[str], serial: str, zte_ip_addr: str, telnet_login: str,
telnet_passw: str, telnet_prompt: str, onu_vlan: int):
if not re.match(r'^ZTEG[0-9A-F]{8}$', serial):
raise base.ExpectValidationError('Serial not valid, match: ^ZTEG[0-9A-F]{8}$')
if not isinstance(onu_vlan, int):
onu_vlan = int(onu_vlan)
if onu_mac is None:
onu_mac = base.sn_to_mac(serial)
IP4_ADDR_REGEX = (
r'^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.'
r'(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.'
r'(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.'
r'(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$'
)
if not re.match(IP4_ADDR_REGEX, zte_ip_addr):
raise base.ExpectValidationError('ip address for zte not valid')
return appy_config(onu_mac, serial, zte_ip_addr, telnet_login,
telnet_passw, telnet_prompt, onu_vlan)

141
devapp/onu_config/f660.py

@ -0,0 +1,141 @@
import re
from typing import Optional
from djing.lib import process_lock
from . import base
def get_onu_template(vlan_id: int, mac_addr: str):
template = (
'switchport mode hybrid vport 1',
'switchport vlan %d tag vport 1' % vlan_id,
'port-location format flexible-syntax vport 1',
'port-location sub-option remote-id enable vport 1',
'port-location sub-option remote-id name %s vport 1' % mac_addr,
'dhcp-option82 enable vport 1',
'dhcp-option82 trust true replace vport 1',
'ip dhcp snooping enable vport 1'
)
return template
def appy_config(onu_mac: str, sn: str, hostname: str, login: str, password: str, prompt: str, vlan: int):
onu_type = 'ZTE-F660'
# Входим
ch = base.MySpawn('telnet %s' % hostname)
ch.timeout = 15
ch.expect_exact('Username:')
ch.do_cmd(login, 'Password:')
choice = ch.do_cmd(password, ['bad password.', '%s#' % prompt])
if choice == 0:
raise base.ZteOltLoginFailed
ch.do_cmd('terminal length 0', '%s#' % prompt)
choice = ch.do_cmd('show gpon onu uncfg', ['No related information to show', '%s#' % prompt])
if choice == 0:
ch.close()
raise base.OnuZteRegisterError('unregistered onu not found, sn=%s' % sn)
elif choice == 1:
# Получим незареганные onu
unregistered_onu = base.get_unregistered_onu(
lines=ch.get_lines_before(),
serial=sn
)
if unregistered_onu is None:
ch.close()
raise base.OnuZteRegisterError('unregistered onu not found, sn=%s' % sn)
stack_num = int(unregistered_onu.get('stack_num'))
rack_num = int(unregistered_onu.get('rack_num'))
fiber_num = int(unregistered_onu.get('fiber_num'))
# Получим последнюю зарегистрированную onu
ch.do_cmd('show run int gpon-olt_%(stack)s/%(rack)s/%(fiber)s' % {
'stack': stack_num,
'rack': rack_num,
'fiber': fiber_num
}, '%s#' % prompt)
free_onu_number = base.get_free_registered_onu_number(
ch.get_lines_before()
)
if free_onu_number > 126:
ch.close()
raise base.ZTEFiberIsFull('olt fiber %d is full' % fiber_num)
# enter to config
ch.do_cmd('conf t', '%s(config)#' % prompt)
int_addr = '%d/%d/%d' % (
stack_num,
rack_num,
fiber_num
)
# go to olt interface
ch.do_cmd('interface gpon-olt_%s' % int_addr, '%s(config-if)#' % prompt)
# register onu on olt interface
ch.do_cmd('onu %d type %s sn %s' % (
free_onu_number,
onu_type,
sn
), '%s(config-if)#' % prompt)
# register onu profile on olt interface
ch.do_cmd(
'onu %d profile line ZTE-F660-LINE remote ZTE-F660-ROUTER' % free_onu_number,
'%s(config-if)#' % prompt
)
# Exit from int olt
ch.do_cmd('exit', '%s(config)#' % prompt)
# Enter to int onu
ch.do_cmd('int gpon-onu_%(int_addr)s:%(onu_num)d' % {
'int_addr': int_addr,
'onu_num': free_onu_number
}, '%s(config-if)#' % prompt)
# Apply int onu config
template = get_onu_template(vlan, onu_mac)
for line in template:
ch.do_cmd(line, '%s(config-if)#' % prompt)
# Exit
ch.do_cmd('exit', '%s(config)#' % prompt)
ch.do_cmd('exit', '%s#' % prompt)
ch.close()
return base.onu_conv(
rack_num=rack_num,
fiber_num=fiber_num,
port_num=free_onu_number
)
else:
ch.close()
raise base.ZteOltConsoleError("I don't know what choice:", choice)
# Main Entry point
@process_lock
def register_onu(onu_mac: Optional[str], serial: str, zte_ip_addr: str, telnet_login: str,
telnet_passw: str, telnet_prompt: str, onu_vlan: int):
if not re.match(r'^ZTEG[0-9A-F]{8}$', serial):
raise base.ExpectValidationError('Serial not valid, match: ^ZTEG[0-9A-F]{8}$')
if not isinstance(onu_vlan, int):
onu_vlan = int(onu_vlan)
if onu_mac is None:
onu_mac = base.sn_to_mac(serial)
IP4_ADDR_REGEX = (
r'^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.'
r'(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.'
r'(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.'
r'(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$'
)
if not re.match(IP4_ADDR_REGEX, zte_ip_addr):
raise base.ExpectValidationError('ip address for zte not valid')
return appy_config(onu_mac, serial, zte_ip_addr, telnet_login,
telnet_passw, telnet_prompt, onu_vlan)

31
devapp/views.py

@ -21,17 +21,16 @@ from djing.lib import safe_int, ProcessLocked, DuplicateEntry
from djing.lib.decorators import json_view from djing.lib.decorators import json_view
from djing.lib.decorators import only_admins, hash_auth_view from djing.lib.decorators import only_admins, hash_auth_view
from djing.lib.mixins import LoginAdminPermissionMixin, LoginAdminMixin from djing.lib.mixins import LoginAdminPermissionMixin, LoginAdminMixin
from djing.lib.tln import ZteOltConsoleError, OnuZteRegisterError, \
ZteOltLoginFailed
from djing.tasks import multicast_email_notify from djing.tasks import multicast_email_notify
from easysnmp import EasySNMPTimeoutError, EasySNMPError from easysnmp import EasySNMPTimeoutError, EasySNMPError
from group_app.models import Group from group_app.models import Group
from messenger.tasks import multicast_viber_notify from messenger.tasks import multicast_viber_notify
from guardian.decorators import permission_required_or_403 as permission_required from guardian.decorators import permission_required_or_403 as permission_required
from guardian.shortcuts import get_objects_for_user from guardian.shortcuts import get_objects_for_user
from .forms import DeviceForm, PortForm, DeviceExtraDataForm
from .models import Device, Port, DeviceDBException, DeviceMonitoringException
from .tasks import onu_register
from devapp.forms import DeviceForm, PortForm, DeviceExtraDataForm
from devapp.models import Device, Port, DeviceDBException, DeviceMonitoringException
from devapp.tasks import onu_register
from devapp import onu_config
class DevicesListView(LoginAdminPermissionMixin, class DevicesListView(LoginAdminPermissionMixin,
@ -784,17 +783,25 @@ def register_device(request, group_id: int, device_id: int):
try: try:
device.register_device() device.register_device()
status = 0 status = 0
except OnuZteRegisterError:
except onu_config.OnuZteRegisterError:
text = format_msg(gettext('Unregistered onu not found'), 'eye-close') text = format_msg(gettext('Unregistered onu not found'), 'eye-close')
except ZteOltLoginFailed:
text = format_msg(gettext('Wrong login or password for telnet access'),
'lock')
except (ConnectionRefusedError, ZteOltConsoleError) as e:
except onu_config.ZteOltLoginFailed:
text = format_msg(
gettext('Wrong login or password for telnet access'),
'lock'
)
except (
ConnectionRefusedError, onu_config.ZteOltConsoleError,
onu_config.ExpectValidationError, onu_config.ZTEFiberIsFull
) as e:
text = format_msg(e, 'exclamation-sign') text = format_msg(e, 'exclamation-sign')
except DeviceImplementationError as e: except DeviceImplementationError as e:
text = format_msg(e, 'wrench')
text = format_msg(str(e), 'wrench')
except ProcessLocked: except ProcessLocked:
text = format_msg(gettext('Process locked by another process'), 'time')
text = format_msg(
gettext('Process locked by another process'),
'time'
)
else: else:
text = format_msg(msg='ok', icon='ok') text = format_msg(msg='ok', icon='ok')
return { return {

4
djing/lib/tln/__init__.py

@ -1,4 +0,0 @@
from .tln import *
__all__ = ('TelnetApi', 'ValidationError', 'ZTEFiberIsFull', 'ZteOltLoginFailed',
'OnuZteRegisterError', 'ZteOltConsoleError', 'register_onu_ZTE_F660')

274
djing/lib/tln/tln.py

@ -1,274 +0,0 @@
#!/usr/bin/env python3
import re
import struct
from telnetlib import Telnet
from time import sleep
from typing import Generator, Dict, Optional, Tuple
from djing.lib import process_lock
class ZteOltConsoleError(Exception):
pass
class OnuZteRegisterError(ZteOltConsoleError):
pass
class ZTEFiberIsFull(ZteOltConsoleError):
pass
class ZteOltLoginFailed(ZteOltConsoleError):
pass
class ValidationError(ValueError):
pass
MAC_ADDR_REGEX = b'^([0-9A-Fa-f]{1,2}[:-]){5}([0-9A-Fa-f]{1,2})$'
IP_ADDR_REGEX = (
'^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.'
'(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.'
'(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.'
'(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$'
)
ONU_SN_REGEX = b'^ZTEG[A-F\d]{8}$'
class TelnetApi(Telnet):
def __init__(self, prompt_string: bytes, *args, **kwargs):
timeout = kwargs.get('timeout')
if timeout:
self._timeout = timeout
self._prompt_string = prompt_string or b'ZTE#'
self.config_level = []
super().__init__(*args, **kwargs)
def write(self, buffer: bytes) -> None:
buffer = buffer + b'\n'
print('>>', buffer)
super().write(buffer)
def resize_screen(self, width: int, height: int):
naws_cmd = struct.pack('>BBBHHBB',
255, 250, 31, # IAC SB NAWS
width, height,
255, 240 # IAC SE
)
sock = self.get_socket()
sock.send(naws_cmd)
def read_lines(self) -> Generator:
while True:
line = self.read_until(b'\r\n', timeout=self._timeout)
line = line.replace(b'\r\n', b'')
if self._prompt_string == line:
break
if line == b'':
continue
yield line
def command_to(self, cmd: bytes) -> Generator:
self.write(cmd)
return self.read_lines()
def set_prompt_string(self, prompt_string: bytes) -> None:
self.config_level.append(prompt_string)
self._prompt_string = prompt_string
def level_exit(self) -> Optional[Tuple]:
if len(self.config_level) < 2:
print('We are in root')
return
self.config_level.pop()
self.set_prompt_string(self.config_level[-1])
return tuple(self.command_to(b'exit'))
def __del__(self):
if self.sock:
self.write(b'exit')
super().__del__()
def parse_onu_name(onu_name: bytes, name_regexp=re.compile(b'[/:_]')) -> Dict[str, bytes]:
gpon_onu, stack_num, rack_num, fiber_num, onu_num = name_regexp.split(onu_name)
return {
'stack_num': stack_num,
'rack_num': rack_num,
'fiber_num': fiber_num,
'onu_num': onu_num
}
class OltZTERegister(TelnetApi):
def __init__(self, screen_size: Tuple[int, int], prompt_title: bytes, *args, **kwargs):
super().__init__(prompt_string=prompt_title, *args, **kwargs)
self.prompt_title = prompt_title
self.set_prompt_string(b'%s#' % prompt_title)
self.resize_screen(*screen_size)
def enter(self, username: bytes, passw: bytes) -> None:
self.read_until(b'Username:')
self.write(username)
self.read_until(b'Password:')
self.write(passw)
for l in self.read_lines():
if b'bad password' in l:
raise ZteOltLoginFailed
def get_unregistered_onu(self, sn: bytes) -> Optional[Dict]:
lines = tuple(self.command_to(b'show gpon onu uncfg'))
if len(lines) > 3:
# devices available
# find onu by sn
line = tuple(ln for ln in lines if sn.lower() in ln.lower())
if len(line) > 0:
line = line[0]
onu_name, onu_sn, onu_state = line.split()
onu_numbers = parse_onu_name(onu_name)
onu_numbers.update({
'onu_name': onu_name,
'onu_sn': onu_sn,
'onu_state': onu_state
})
return onu_numbers
def get_last_registered_onu_number(self, stack_num: int, rack_num: int, fiber_num: int) -> int:
registered_lines = self.command_to(b'show run int gpon-olt_%d/%d/%d' % (
stack_num,
rack_num,
fiber_num
))
onu_type_regexp = re.compile(b'^\s{2}onu \d{1,3} type [-\w\d]{4,64} sn \w{4,64}$')
last_onu = 0
for rl in registered_lines:
if rl == b' --More--':
self.write(b' ')
if onu_type_regexp.match(rl):
_onu, num, _type, onu_type, _sn, onu_sn = rl.split()
last_onu = int(num)
return last_onu
def enter_to_config_mode(self) -> bool:
prompt = b'%s(config)#' % self.prompt_title
self.set_prompt_string(prompt)
res = tuple(self.command_to(b'config terminal'))
if res[1].startswith(b'Enter configuration commands'):
# ok, we in the config mode
return True
return False
def go_to_olt_interface(self, stack_num: int, rack_num: int, fiber_num: int) -> Tuple:
self.set_prompt_string(b'%s(config-if)#' % self.prompt_title)
return tuple(self.command_to(b'interface gpon-olt_%d/%d/%d' % (
stack_num,
rack_num,
fiber_num
)))
def go_to_onu_interface(self, stack_num: int, rack_num: int, fiber_num: int, onu_port_num: int) -> Tuple:
self.set_prompt_string(b'%s(config-if)#' % self.prompt_title)
return tuple(self.command_to(b'interface gpon-onu_%d/%d/%d:%d' % (
stack_num,
rack_num,
fiber_num,
onu_port_num
)))
def apply_conf_to_onu(self, mac_addr: bytes, vlan_id: int) -> None:
tmpl = (
b'switchport vlan %d tag vport 1' % vlan_id,
b'port-location format flexible-syntax vport 1',
b'port-location sub-option remote-id enable vport 1',
b'port-location sub-option remote-id name %s vport 1' % mac_addr,
b'dhcp-option82 enable vport 1',
b'dhcp-option82 trust true replace vport 1',
b'ip dhcp snooping enable vport 1'
)
for conf_line in tmpl:
self.write(conf_line)
def register_onu_on_olt_fiber(self, onu_type: bytes, new_onu_num: int, onu_sn: bytes, line_profile: bytes,
remote_profile: bytes) -> Tuple:
# ok, we in interface
tpl = b'onu %d type %s sn %s' % (new_onu_num, onu_type, onu_sn)
r = tuple(self.command_to(tpl))
return tuple(self.command_to(b'onu %d profile line %s remote %s' % (
new_onu_num,
line_profile,
remote_profile
))) + r
@process_lock
def register_onu_ZTE_F660(olt_ip: str, onu_sn: bytes, login_passwd: Tuple[bytes, bytes], onu_mac: bytes, prompt_title: bytes, vlan_id: int) -> Tuple:
onu_type = b'ZTE-F660'
line_profile = b'ZTE-F660-LINE'
remote_profile = b'ZTE-F660-ROUTER'
if not re.match(MAC_ADDR_REGEX, onu_mac):
raise ValidationError
if not re.match(IP_ADDR_REGEX, olt_ip):
raise ValidationError
if not re.match(ONU_SN_REGEX, onu_sn):
raise ValidationError
tn = OltZTERegister(host=olt_ip, timeout=2, screen_size=(120, 128), prompt_title=prompt_title)
tn.enter(*login_passwd)
unregistered_onu = tn.get_unregistered_onu(onu_sn)
if unregistered_onu is None:
raise OnuZteRegisterError('unregistered onu not found, sn=%s' % onu_sn.decode('utf-8'))
stack_num = int(unregistered_onu['stack_num'])
rack_num = int(unregistered_onu['rack_num'])
fiber_num = int(unregistered_onu['fiber_num'])
last_onu_number = tn.get_last_registered_onu_number(
stack_num, rack_num, fiber_num
)
if last_onu_number > 126:
raise ZTEFiberIsFull('olt fiber %d is full' % fiber_num)
# enter to config
if not tn.enter_to_config_mode():
raise ZteOltConsoleError('Failed to enter to config mode')
# go to olt interface
if not tn.go_to_olt_interface(stack_num, rack_num, fiber_num):
raise ZteOltConsoleError('Failed to enter in olt fiber port')
# new onu port number
new_onu_port_num = last_onu_number + 1
# register onu on olt interface
r = tn.register_onu_on_olt_fiber(onu_type, new_onu_port_num, onu_sn, line_profile, remote_profile)
print(r)
# exit from olt interface
tn.level_exit()
r = tn.go_to_onu_interface(stack_num, rack_num, fiber_num, new_onu_port_num)
print(r)
tn.apply_conf_to_onu(onu_mac, vlan_id)
sleep(1)
return stack_num, rack_num, fiber_num, new_onu_port_num
if __name__ == '__main__':
ip = '192.168.0.100'
try:
register_onu_ZTE_F660(
olt_ip=ip, onu_sn=b'ZTEG^#*$&@&', login_passwd=(b'login', b'password'),
onu_mac=b'MAC'
)
except ZteOltConsoleError as e:
print(e)
except ConnectionRefusedError:
print('ERROR: connection refused', ip)

3
docs/extra_func.md

@ -3,13 +3,10 @@
Его совсем не много, но без внимания оставить нельзя. Его совсем не много, но без внимания оставить нельзя.
Все вспомогательные модули можно найти в пакете **djing.lib**. Все вспомогательные модули можно найти в пакете **djing.lib**.
### tln
Это модуль работы по *telnet*
### messaging ### messaging
Этот модуль помогает работать с форматами СМС сообщений. Этот модуль помогает работать с форматами СМС сообщений.
### init ### init
Содержит всякие мелкие примочки, код прост и с комментариями, зайдите посмотрите. Содержит всякие мелкие примочки, код прост и с комментариями, зайдите посмотрите.

5
requirements.txt

@ -6,7 +6,7 @@ Pillow
netaddr netaddr
# for testing required xmltodict # for testing required xmltodict
xmltodict
#xmltodict
dicttoxml dicttoxml
# db client for Postgres # db client for Postgres
@ -39,6 +39,9 @@ asterisk
# viberbot # viberbot
-e git://github.com/Viber/viber-bot-python.git#egg=viberbot -e git://github.com/Viber/viber-bot-python.git#egg=viberbot
# pexpect
-e git://github.com/pexpect/pexpect.git#egg=pexpect
Celery Celery
redis==2.10.6 redis==2.10.6
celery[redis] celery[redis]
Loading…
Cancel
Save