diff --git a/agent/mod_mikrotik.py b/agent/mod_mikrotik.py index 3f246ce..cdb3634 100644 --- a/agent/mod_mikrotik.py +++ b/agent/mod_mikrotik.py @@ -6,7 +6,7 @@ from abc import ABCMeta from hashlib import md5 from typing import Iterable, Optional, Tuple from .core import BaseTransmitter, NasFailedResult, NasNetworkError -from djing.lib import singleton +from djing.lib import Singleton from .structs import TariffStruct, AbonStruct, IpStruct, VectorAbon, VectorTariff from . import settings as local_settings from django.conf import settings @@ -18,8 +18,7 @@ LIST_USERS_ALLOWED = 'DjingUsersAllowed' LIST_USERS_BLOCKED = 'DjingUsersBlocked' -@singleton -class ApiRos: +class ApiRos(metaclass=Singleton): """Routeros api""" sk = None is_login = False @@ -223,8 +222,8 @@ class TransmitterManager(BaseTransmitter, metaclass=ABCMeta): speeds = info['=max-limit'].split('/') t = TariffStruct( - speedIn=parse_speed(speeds[1]), - speedOut=parse_speed(speeds[0]) + speed_in=parse_speed(speeds[1]), + speed_out=parse_speed(speeds[0]) ) try: a = AbonStruct( @@ -480,7 +479,7 @@ class MikrotikTransmitter(QueueManager, IpAddressListManager): interface = r[0]['=interface'] r = self._exec_cmd(( '/ping', '=address=%s' % host, '=arp-ping=yes', '=interval=100ms', '=count=%d' % count, - '=interface=%s' % interface + '=interface=%s' % interface )) received, sent = int(r[-2:][0]['=received']), int(r[-2:][0]['=sent']) return received, sent diff --git a/devapp/base_intr.py b/devapp/base_intr.py index 5725e66..a1dee37 100644 --- a/devapp/base_intr.py +++ b/devapp/base_intr.py @@ -1,10 +1,10 @@ from abc import ABCMeta, abstractmethod from datetime import timedelta -from django.utils.translation import gettext from typing import Union, Iterable, AnyStr, Generator, Optional - from easysnmp import Session +from django.utils.translation import gettext + ListOrError = Union[ Iterable, Union[Exception, Iterable] @@ -15,7 +15,7 @@ class DeviceImplementationError(Exception): pass -class DevBase(object, metaclass=ABCMeta): +class DevBase(object): def __init__(self, dev_instance=None): self.db_instance = dev_instance @@ -53,6 +53,15 @@ class DevBase(object, metaclass=ABCMeta): def is_use_device_port() -> bool: """True if used device port while opt82 authorization""" + @abstractmethod + def validate_extra_snmp_info(self, v: str) -> None: + """ + Validate extra snmp field for each device. + If validation failed then raise en exception from djing.lib.tln.ValidationError + with description of error. + :param v: String value for validate + """ + class BasePort(object, metaclass=ABCMeta): def __init__(self, num, name, status, mac, speed): diff --git a/devapp/dev_types.py b/devapp/dev_types.py index c6c46c7..a785b79 100644 --- a/devapp/dev_types.py +++ b/devapp/dev_types.py @@ -4,6 +4,7 @@ from easysnmp import EasySNMPTimeoutError from django.utils.translation import gettext_lazy as _, gettext from djing.lib import RuTimedelta, safe_int +from djing.lib.tln.tln import ValidationError as TlnValidationError from .base_intr import DevBase, SNMPBaseWorker, BasePort, DeviceImplementationError, ListOrError @@ -77,6 +78,10 @@ class DLinkDevice(DevBase, SNMPBaseWorker): def is_use_device_port(): return True + def validate_extra_snmp_info(self, v: str) -> None: + # Dlink has no require snmp info + pass + class ONUdev(BasePort): def __init__(self, num, name, status, mac, speed, signal, snmp_worker): @@ -150,6 +155,10 @@ class OLTDevice(DevBase, SNMPBaseWorker): def is_use_device_port(): return False + def validate_extra_snmp_info(self, v: str) -> None: + # Olt has no require snmp info + pass + class OnuDevice(DevBase, SNMPBaseWorker): def __init__(self, dev_instance): @@ -216,6 +225,13 @@ class OnuDevice(DevBase, SNMPBaseWorker): except EasySNMPTimeoutError as e: return {'err': "%s: %s" % (_('ONU not connected'), e)} + def validate_extra_snmp_info(self, v: str) -> None: + # DBCOM Onu have en integer snmp port + try: + int(v) + except ValueError: + raise TlnValidationError(_('Onu snmp field must be en integer')) + class EltexPort(BasePort): def __init__(self, snmp_worker, *args, **kwargs): @@ -360,3 +376,11 @@ class ZteOnuDevice(OnuDevice): def get_template_name(self): return 'onu_for_zte.html' + + def validate_extra_snmp_info(self, v: str) -> None: + # for example 268501760.5 + try: + fiber_num, onu_port = v.split('.') + int(fiber_num), int(onu_port) + except ValueError: + raise TlnValidationError(_('Zte onu snmp field must be two dot separated integers')) diff --git a/devapp/forms.py b/devapp/forms.py index c3145c4..dd67e5b 100644 --- a/devapp/forms.py +++ b/devapp/forms.py @@ -1,7 +1,9 @@ from django import forms +from django.core.exceptions import ValidationError from django.utils.translation import gettext_lazy as _ from django.db import IntegrityError +from djing.lib.tln.tln import ValidationError as TlnValidationError from . import models from djing import MAC_ADDR_REGEX, IP_ADDR_REGEX @@ -15,9 +17,23 @@ class DeviceForm(forms.ModelForm): 'unique': _('Device with that mac is already exist') }) + def clean_snmp_extra(self): + snmp_extra = self.cleaned_data.get('snmp_extra') + if snmp_extra is None: + return + device = self.instance + manager = device.get_manager_object() + try: + manager.validate_extra_snmp_info(snmp_extra) + except TlnValidationError as e: + raise ValidationError( + e, code='invalid' + ) + return snmp_extra + class Meta: model = models.Device - exclude = ['map_dot', 'status'] + exclude = ('map_dot', 'status') widgets = { 'ip_address': forms.TextInput(attrs={ 'pattern': IP_ADDR_REGEX, diff --git a/devapp/locale/ru/LC_MESSAGES/django.po b/devapp/locale/ru/LC_MESSAGES/django.po index c9c3976..9b685a4 100644 --- a/devapp/locale/ru/LC_MESSAGES/django.po +++ b/devapp/locale/ru/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2018-04-24 23:27+0300\n" +"POT-Creation-Date: 2018-06-02 16:39+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Dmitry Novikov nerosketch@gmail.com\n" "Language: ru\n" @@ -18,377 +18,456 @@ msgstr "" "%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n" "%100>=11 && n%100<=14)? 2 : 3);\n" -#: devapp/base_intr.py:82 +#: base_intr.py:92 msgid "Ip address is required" msgstr "Ip адрес необходим" -#: devapp/dev_types.py:35 +#: dev_types.py:37 msgid "DLink switch" msgstr "Свич D'Link" -#: devapp/dev_types.py:54 +#: dev_types.py:56 msgid "does not fetch the mac" msgstr "не нашёл мак" -#: devapp/dev_types.py:106 +#: dev_types.py:112 msgid "PON OLT" msgstr "" -#: devapp/dev_types.py:131 devapp/views.py:268 devapp/views.py:413 +#: dev_types.py:136 views.py:275 views.py:433 msgid "wait for a reply from the SNMP Timeout" msgstr "Время ожидания ответа от SNMP истекло" -#: devapp/dev_types.py:171 +#: dev_types.py:176 +msgid "Ip address or parent device with ip address required for ONU device" +msgstr "" +"Необходим ip адрес. Или родительское устройство с назначенным ip адресом " +"необходимо." + +#: dev_types.py:182 msgid "PON ONU" msgstr "" -#: devapp/dev_types.py:216 +#: dev_types.py:227 msgid "ONU not connected" msgstr "ONU не в сети" -#: devapp/dev_types.py:242 +#: dev_types.py:234 +msgid "Onu snmp field must be en integer" +msgstr "Поле для snmp информации об ONU должно быть числом" + +#: dev_types.py:260 msgid "Eltex switch" msgstr "Элтекс свич" -#: devapp/forms.py:14 +#: dev_types.py:305 +msgid "OLT ZTE C320" +msgstr "" + +#: dev_types.py:354 +msgid "ZTE PON ONU" +msgstr "" + +#: dev_types.py:387 +msgid "Zte onu snmp field must be two dot separated integers" +msgstr "" +"Поле snmp информации для ZTE ONU должно быть двумя целыми числами, " +"разделенными точкой" + +#: forms.py:16 msgid "Mac address is required for fill" msgstr "MAC-адрес необходим для заполнения" -#: devapp/forms.py:15 +#: forms.py:17 msgid "Device with that mac is already exist" msgstr "Устройство с этим мак-адресом уже есть" -#: devapp/forms.py:48 +#: forms.py:63 msgid "Port number on device must be unique" msgstr "Номер порта на устройстве должен быть уникальным" -#: devapp/models.py:21 devapp/templates/devapp/custom_dev_page/onu.html:18 -#: devapp/templates/devapp/devices.html:22 -#: devapp/templates/devapp/devices_null_group.html:20 +#: models.py:25 templates/devapp/custom_dev_page/onu.html:18 +#: templates/devapp/custom_dev_page/onu_for_zte.html:18 +#: templates/devapp/devices.html:22 templates/devapp/devices_null_group.html:20 msgid "Ip address" msgstr "Ip адрес" -#: devapp/models.py:22 devapp/templates/devapp/devices.html:32 +#: models.py:26 templates/devapp/devices.html:32 msgid "Mac address" msgstr "Мак адрес" -#: devapp/models.py:23 devapp/templates/devapp/devices.html:28 -#: devapp/templates/devapp/devices_null_group.html:26 +#: models.py:27 templates/devapp/devices.html:28 +#: templates/devapp/devices_null_group.html:26 msgid "Comment" msgstr "Комментарий" -#: devapp/models.py:30 devapp/templates/devapp/devices.html:36 -#: devapp/templates/devapp/devices_null_group.html:32 +#: models.py:36 templates/devapp/devices.html:36 +#: templates/devapp/devices_null_group.html:32 msgid "Device type" msgstr "Тип устройства" -#: devapp/models.py:32 +#: models.py:38 msgid "SNMP password" msgstr "Пароль SNMP" -#: devapp/models.py:33 +#: models.py:39 msgid "Device group" msgstr "Группа устройства" -#: devapp/models.py:34 devapp/templates/devapp/add_dev.html:46 -#: devapp/templates/devapp/custom_dev_page/onu.html:34 -#: devapp/templates/devapp/custom_dev_page/ports.html:59 -#: devapp/templates/devapp/dev.html:33 -#: devapp/templates/devapp/fix_dev_group.html:42 +#: models.py:40 templates/devapp/add_dev.html:46 +#: templates/devapp/custom_dev_page/onu.html:34 +#: templates/devapp/custom_dev_page/onu_for_zte.html:34 +#: templates/devapp/custom_dev_page/ports.html:59 templates/devapp/dev.html:33 +#: templates/devapp/fix_dev_group.html:42 msgid "Parent device" msgstr "Родительское устройство" -#: devapp/models.py:37 -msgid "SNMP Number" -msgstr "SNMP Ном." +#: models.py:43 +msgid "SNMP extra info" +msgstr "Доп. инфо для snmp" -#: devapp/models.py:40 +#: models.py:46 msgid "Undefined" msgstr "Не определено" -#: devapp/models.py:41 +#: models.py:47 msgid "Up" msgstr "В сети" -#: devapp/models.py:42 +#: models.py:48 msgid "Unreachable" msgstr "Не доступно" -#: devapp/models.py:43 +#: models.py:49 msgid "Down" msgstr "Не в сети" -#: devapp/models.py:45 +#: models.py:51 msgid "Status" msgstr "Состояние" -#: devapp/models.py:47 +#: models.py:53 msgid "Send notify when monitoring state changed" msgstr "Отправлять уведомления при событиях мониторинга" -#: devapp/models.py:52 +#: models.py:58 msgid "Can view device" msgstr "Может видеть устройство" -#: devapp/models.py:54 devapp/models.py:126 +#: models.py:60 models.py:108 msgid "Device" msgstr "Устройство" -#: devapp/models.py:55 devapp/templates/devapp/devices.html:14 -#: devapp/templates/devapp/devices_null_group.html:8 +#: models.py:61 templates/devapp/devices.html:14 +#: templates/devapp/devices_null_group.html:8 msgid "Devices" msgstr "Устройства" -#: devapp/models.py:127 devapp/templates/devapp/manage_ports/list.html:11 +#: models.py:96 views.py:175 views.py:237 +msgid "Device does not have a group, please fix that" +msgstr "У устройства нет группы, пожалуйста, исправьте это" + +#: models.py:109 templates/devapp/manage_ports/list.html:11 msgid "Number" msgstr "Номер" -#: devapp/models.py:128 devapp/templates/devapp/custom_dev_page/onu.html:20 -#: devapp/templates/devapp/custom_dev_page/ports.html:88 -#: devapp/templates/devapp/manage_ports/add_ports.html:33 -#: devapp/templates/devapp/manage_ports/list.html:12 +#: models.py:110 templates/devapp/custom_dev_page/onu.html:20 +#: templates/devapp/custom_dev_page/onu_for_zte.html:20 +#: templates/devapp/custom_dev_page/ports.html:88 +#: templates/devapp/manage_ports/add_ports.html:33 +#: templates/devapp/manage_ports/list.html:12 msgid "Description" msgstr "Описание" -#: devapp/models.py:137 +#: models.py:119 msgid "Can toggle ports" msgstr "Может переключать порты" -#: devapp/models.py:139 devapp/templates/devapp/custom_dev_page/ports.html:110 -#: devapp/templates/devapp/manage_ports/fix_abon_device.html:24 +#: models.py:121 templates/devapp/custom_dev_page/ports.html:110 +#: templates/devapp/manage_ports/fix_abon_device.html:24 msgid "Port" msgstr "Порт" -#: devapp/models.py:140 +#: models.py:122 msgid "Ports" msgstr "Порты" -#: devapp/templates/devapp/add_dev.html:8 -#: devapp/templates/devapp/devices.html:8 -#: devapp/templates/devapp/devices_null_group.html:7 -#: devapp/templates/devapp/fix_dev_group.html:9 -#: devapp/templates/devapp/group_list.html:7 -#: devapp/templates/devapp/manage_ports/add_ports.html:7 -#: devapp/templates/devapp/manage_ports/fix_abon_device.html:7 +#: templates/devapp/add_dev.html:8 +#: templates/devapp/custom_dev_page/olt_ztec320_ports.html:7 +#: templates/devapp/devices.html:8 templates/devapp/devices_null_group.html:7 +#: templates/devapp/fix_dev_group.html:9 templates/devapp/group_list.html:7 +#: templates/devapp/manage_ports/add_ports.html:7 +#: templates/devapp/manage_ports/fix_abon_device.html:7 msgid "Groups" msgstr "Группы" -#: devapp/templates/devapp/add_dev.html:10 +#: templates/devapp/add_dev.html:10 msgid "Add new device" msgstr "Добавить устройство" -#: devapp/templates/devapp/add_dev.html:16 -#: devapp/templates/devapp/custom_dev_page/ports.html:68 -#: devapp/templates/devapp/devices.html:63 -#: devapp/templates/devapp/manage_ports/add_ports.html:16 +#: templates/devapp/add_dev.html:16 +#: templates/devapp/custom_dev_page/olt_ztec320_ports.html:12 +#: templates/devapp/custom_dev_page/ports.html:68 +#: templates/devapp/devices.html:63 +#: templates/devapp/manage_ports/add_ports.html:16 msgid "Not assigned" msgstr "<Не назначено>" -#: devapp/templates/devapp/add_dev.html:21 devapp/templates/devapp/dev.html:8 +#: templates/devapp/add_dev.html:21 templates/devapp/dev.html:8 msgid "Device info" msgstr "Инфа о железке" -#: devapp/templates/devapp/add_dev.html:60 devapp/templates/devapp/dev.html:43 -#: devapp/templates/devapp/dev.html:47 -#: devapp/templates/devapp/fix_dev_group.html:51 -#: devapp/templates/devapp/fix_dev_group.html:55 +#: templates/devapp/add_dev.html:60 templates/devapp/dev.html:43 +#: templates/devapp/dev.html:47 templates/devapp/fix_dev_group.html:51 +#: templates/devapp/fix_dev_group.html:55 msgid "Find the device" msgstr "Найти устройство" -#: devapp/templates/devapp/add_dev.html:72 devapp/templates/devapp/dev.html:61 -#: devapp/templates/devapp/fix_dev_group.html:64 -#: devapp/templates/devapp/manage_ports/add_ports.html:75 -#: devapp/templates/devapp/manage_ports/add_ports.html:79 -#: devapp/templates/devapp/manage_ports/modal_add_edit_port.html:21 +#: templates/devapp/add_dev.html:72 templates/devapp/dev.html:61 +#: templates/devapp/fix_dev_group.html:64 +#: templates/devapp/manage_ports/add_ports.html:75 +#: templates/devapp/manage_ports/add_ports.html:79 +#: templates/devapp/manage_ports/modal_add_edit_port.html:21 msgid "Save" msgstr "Сохранить" -#: devapp/templates/devapp/add_dev.html:75 devapp/templates/devapp/dev.html:73 -#: devapp/templates/devapp/fix_dev_group.html:67 +#: templates/devapp/add_dev.html:75 templates/devapp/dev.html:73 +#: templates/devapp/fix_dev_group.html:67 msgid "Reset" msgstr "Сбросить" -#: devapp/templates/devapp/custom_dev_page/olt.html:10 -#: devapp/templates/devapp/custom_dev_page/onu.html:12 -#: devapp/templates/devapp/custom_dev_page/ports.html:12 +#: templates/devapp/custom_dev_page/olt.html:10 +#: templates/devapp/custom_dev_page/olt_ztec320.html:11 +#: templates/devapp/custom_dev_page/onu.html:12 +#: templates/devapp/custom_dev_page/onu_for_zte.html:12 +#: templates/devapp/custom_dev_page/ports.html:12 msgid "Uptime" msgstr "Без перезагрузки" -#: devapp/templates/devapp/custom_dev_page/olt.html:17 +#: templates/devapp/custom_dev_page/olt.html:17 msgid "SNMP Num" msgstr "SNMP Ном." -#: devapp/templates/devapp/custom_dev_page/olt.html:18 +#: templates/devapp/custom_dev_page/olt.html:18 msgid "Name" msgstr "Имя" -#: devapp/templates/devapp/custom_dev_page/olt.html:19 -#: devapp/templates/devapp/custom_dev_page/onu.html:19 +#: templates/devapp/custom_dev_page/olt.html:19 +#: templates/devapp/custom_dev_page/onu.html:19 +#: templates/devapp/custom_dev_page/onu_for_zte.html:19 msgid "Mac" msgstr "Мак" -#: devapp/templates/devapp/custom_dev_page/olt.html:20 -#: devapp/templates/devapp/custom_dev_page/onu.html:73 +#: templates/devapp/custom_dev_page/olt.html:20 +#: templates/devapp/custom_dev_page/onu.html:74 +#: templates/devapp/custom_dev_page/onu_for_zte.html:77 msgid "Signal" msgstr "Ур. сигнала" -#: devapp/templates/devapp/custom_dev_page/olt.html:38 +#: templates/devapp/custom_dev_page/olt.html:38 +#: templates/devapp/custom_dev_page/olt_ztec320_ports.html:45 msgid "Create device" msgstr "Создать устройство" -#: devapp/templates/devapp/custom_dev_page/olt.html:45 -#: devapp/templates/devapp/manage_ports/list.html:48 +#: templates/devapp/custom_dev_page/olt.html:45 +#: templates/devapp/manage_ports/list.html:48 msgid "Ports not found" msgstr "Порты не найдены" -#: devapp/templates/devapp/custom_dev_page/onu.html:10 -#: devapp/templates/devapp/custom_dev_page/ports.html:10 +#: templates/devapp/custom_dev_page/olt_ztec320.html:31 +#: templates/devapp/custom_dev_page/ports.html:54 +msgid "We have not received info, please check options :(" +msgstr "Инфа не получена, проверьте настройки :(" + +#: templates/devapp/custom_dev_page/olt_ztec320.html:36 +msgid "Long description" +msgstr "Длинное описание" + +#: templates/devapp/custom_dev_page/olt_ztec320.html:37 +msgid "Hostname" +msgstr "Имя хоста" + +#: templates/devapp/custom_dev_page/olt_ztec320_ports.html:15 +msgid "OLT Scan" +msgstr "Скан OLT" + +#: templates/devapp/custom_dev_page/olt_ztec320_ports.html:27 +msgid "Onu type" +msgstr "Тип ONU" + +#: templates/devapp/custom_dev_page/olt_ztec320_ports.html:28 +msgid "Onu port" +msgstr "Порт ONU" + +#: templates/devapp/custom_dev_page/olt_ztec320_ports.html:29 +msgid "Onu signal" +msgstr "Сигнала ONU" + +#: templates/devapp/custom_dev_page/olt_ztec320_ports.html:30 +msgid "Serial" +msgstr "Серийный номер" + +#: templates/devapp/custom_dev_page/olt_ztec320_ports.html:52 +msgid "ONU not found" +msgstr "ONU не найдена" + +#: templates/devapp/custom_dev_page/onu.html:10 +#: templates/devapp/custom_dev_page/onu_for_zte.html:10 +#: templates/devapp/custom_dev_page/ports.html:10 msgid "Title of the type of switch" msgstr "Название типа свича" -#: devapp/templates/devapp/custom_dev_page/onu.html:22 +#: templates/devapp/custom_dev_page/onu.html:22 +#: templates/devapp/custom_dev_page/onu_for_zte.html:22 msgid "Attached user" msgstr "Прикрепленный абонент" -#: devapp/templates/devapp/custom_dev_page/onu.html:47 +#: templates/devapp/custom_dev_page/onu.html:48 +#: templates/devapp/custom_dev_page/onu_for_zte.html:50 msgid "ONU Status" msgstr "Состояние ONU" -#: devapp/templates/devapp/custom_dev_page/onu.html:57 +#: templates/devapp/custom_dev_page/onu.html:58 +#: templates/devapp/custom_dev_page/onu_for_zte.html:60 msgid "ONU error" msgstr "ONU ошибка" -#: devapp/templates/devapp/custom_dev_page/onu.html:71 +#: templates/devapp/custom_dev_page/onu.html:72 +#: templates/devapp/custom_dev_page/onu_for_zte.html:75 msgid "Name on OLT" msgstr "Имя на OLT" -#: devapp/templates/devapp/custom_dev_page/onu.html:72 +#: templates/devapp/custom_dev_page/onu.html:73 +#: templates/devapp/custom_dev_page/onu_for_zte.html:76 msgid "Distance(m)" msgstr "Расстояние (м)" -#: devapp/templates/devapp/custom_dev_page/onu.html:77 -#: devapp/templates/devapp/custom_dev_page/onu.html:85 +#: templates/devapp/custom_dev_page/onu.html:78 +#: templates/devapp/custom_dev_page/onu.html:86 msgid "Mac on OLT" msgstr "MAC адрес на OLT" -#: devapp/templates/devapp/custom_dev_page/onu.html:78 +#: templates/devapp/custom_dev_page/onu.html:79 msgid "Mac-addresses does not match" msgstr "MAC адреса не совпадают" -#: devapp/templates/devapp/custom_dev_page/onu.html:79 -#: devapp/templates/devapp/custom_dev_page/onu.html:81 +#: templates/devapp/custom_dev_page/onu.html:80 +#: templates/devapp/custom_dev_page/onu.html:82 msgid "Fix it" msgstr "Исправить" -#: devapp/templates/devapp/custom_dev_page/onu.html:92 +#: templates/devapp/custom_dev_page/onu.html:93 +#: templates/devapp/custom_dev_page/onu_for_zte.html:83 msgid "Info does not fetch" msgstr "Информация не получена" -#: devapp/templates/devapp/custom_dev_page/ports.html:44 +#: templates/devapp/custom_dev_page/ports.html:44 msgid "Disable port" msgstr "Выключить порт" -#: devapp/templates/devapp/custom_dev_page/ports.html:48 +#: templates/devapp/custom_dev_page/ports.html:48 msgid "Enable port" msgstr "Включить порт" -#: devapp/templates/devapp/custom_dev_page/ports.html:54 -msgid "We have not received info, please check options :(" -msgstr "Инфа не получена, проверьте настройки :(" - -#: devapp/templates/devapp/custom_dev_page/ports.html:80 +#: templates/devapp/custom_dev_page/ports.html:80 msgid "Device log" msgstr "Лог устройства" -#: devapp/templates/devapp/custom_dev_page/ports.html:87 +#: templates/devapp/custom_dev_page/ports.html:87 msgid "Level" msgstr "Уровень" -#: devapp/templates/devapp/custom_dev_page/ports.html:89 +#: templates/devapp/custom_dev_page/ports.html:89 msgid "Date" msgstr "Дата" -#: devapp/templates/devapp/custom_dev_page/ports.html:104 +#: templates/devapp/custom_dev_page/ports.html:104 msgid "Ports comment" msgstr "Комментарии портов" -#: devapp/templates/devapp/custom_dev_page/ports.html:111 +#: templates/devapp/custom_dev_page/ports.html:111 msgid "Title" msgstr "Название" -#: devapp/templates/devapp/custom_dev_page/ports.html:122 +#: templates/devapp/custom_dev_page/ports.html:122 msgid "We have not received info for ports" msgstr "Инфа о портах не получена" -#: devapp/templates/devapp/dev.html:65 devapp/templates/devapp/dev.html:69 -#: devapp/templates/devapp/manage_ports/add_ports.html:53 -#: devapp/templates/devapp/manage_ports/add_ports.html:55 -#: devapp/templates/devapp/manage_ports/list.html:35 -#: devapp/templates/devapp/manage_ports/modal_del_port.html:14 +#: templates/devapp/dev.html:65 templates/devapp/dev.html:69 +#: templates/devapp/manage_ports/add_ports.html:53 +#: templates/devapp/manage_ports/add_ports.html:55 +#: templates/devapp/manage_ports/list.html:35 +#: templates/devapp/manage_ports/modal_del_port.html:14 msgid "Delete" msgstr "Удалить" -#: devapp/templates/devapp/dev.html:68 +#: templates/devapp/dev.html:68 msgid "Permission denied" msgstr "Доступ запрещён" -#: devapp/templates/devapp/devices.html:81 -#: devapp/templates/devapp/devices_null_group.html:62 +#: templates/devapp/device_confirm_delete.html:9 +msgid "Remove device" +msgstr "Удалить устройство" + +#: templates/devapp/device_confirm_delete.html:13 +msgid "Are you sure you want to delete device?" +msgstr "Вы уверены что хотите удалить устройство?" + +#: templates/devapp/devices.html:81 templates/devapp/devices_null_group.html:62 msgid "Devices does not found" msgstr "Нет созданных устройств" -#: devapp/templates/devapp/devices.html:81 -#: devapp/templates/devapp/devices.html:91 -#: devapp/templates/devapp/devices_null_group.html:62 -#: devapp/templates/devapp/devices_null_group.html:72 +#: templates/devapp/devices.html:81 templates/devapp/devices.html:91 +#: templates/devapp/devices_null_group.html:62 +#: templates/devapp/devices_null_group.html:72 msgid "Create" msgstr "Cоздать" -#: devapp/templates/devapp/devices_null_group.html:13 -#: devapp/templates/devapp/group_list.html:32 +#: templates/devapp/devices_null_group.html:13 +#: templates/devapp/group_list.html:32 msgid "Devices without group" msgstr "Устройства без группы" -#: devapp/templates/devapp/fix_dev_group.html:17 +#: templates/devapp/fix_dev_group.html:17 msgid "Fix device group" msgstr "Поправить группу устройства" -#: devapp/templates/devapp/group_list.html:14 +#: templates/devapp/group_list.html:14 msgid "Group title" msgstr "Название" -#: devapp/templates/devapp/group_list.html:24 +#: templates/devapp/group_list.html:24 msgid "Groups was not found" msgstr "Эта группа не найдена" -#: devapp/templates/devapp/group_list.html:35 +#: templates/devapp/group_list.html:35 msgid "Export to nagios objects" msgstr "Экспортировать конфиг для nagios" -#: devapp/templates/devapp/manage_ports/add_ports.html:10 -#: devapp/templates/devapp/manage_ports/list.html:58 +#: templates/devapp/manage_ports/add_ports.html:10 +#: templates/devapp/manage_ports/list.html:58 msgid "Add ports" msgstr "Добавить порты" -#: devapp/templates/devapp/manage_ports/add_ports.html:32 +#: templates/devapp/manage_ports/add_ports.html:32 msgid "Mode" msgstr "Режим" -#: devapp/templates/devapp/manage_ports/add_ports.html:60 -#: devapp/templates/devapp/manage_ports/list.html:57 +#: templates/devapp/manage_ports/add_ports.html:60 +#: templates/devapp/manage_ports/list.html:57 msgid "Add" msgstr "Добавить" -#: devapp/templates/devapp/manage_ports/fix_abon_device.html:10 -#: devapp/templates/devapp/manage_ports/fix_abon_device.html:16 +#: templates/devapp/manage_ports/fix_abon_device.html:10 +#: templates/devapp/manage_ports/fix_abon_device.html:16 msgid "Fix subscriber ports conflict" msgstr "Исправить конфликт абонентов на порту" -#: devapp/templates/devapp/manage_ports/fix_abon_device.html:21 +#: templates/devapp/manage_ports/fix_abon_device.html:21 msgid "Subscribers list on port" msgstr "Список абонентов на порту" -#: devapp/templates/devapp/manage_ports/fix_abon_device.html:25 +#: templates/devapp/manage_ports/fix_abon_device.html:25 msgid "" "You may choose the subscriber who correctly attached to device port. When " "you have found right subscriber, remove the port from the other person" @@ -397,15 +476,15 @@ msgstr "" "найдёте нужного абонента удалите этот порт у другого абонента чтоб исправить " "конфликт" -#: devapp/templates/devapp/manage_ports/fix_abon_device.html:31 +#: templates/devapp/manage_ports/fix_abon_device.html:31 msgid "Abons not found" msgstr "Абоненты не найдены" -#: devapp/templates/devapp/manage_ports/list.html:13 +#: templates/devapp/manage_ports/list.html:13 msgid "Count of subscribers" msgstr "Сколько абонов" -#: devapp/templates/devapp/manage_ports/list.html:26 +#: templates/devapp/manage_ports/list.html:26 #, python-format msgid "" "Port should not have more than one subscriber, fix thatисправить" -#: devapp/templates/devapp/manage_ports/list.html:31 +#: templates/devapp/manage_ports/list.html:31 msgid "Show subscriber on port" msgstr "Показать абонента на порту" -#: devapp/templates/devapp/manage_ports/list.html:40 -#: devapp/templates/devapp/manage_ports/modal_add_edit_port.html:14 +#: templates/devapp/manage_ports/list.html:40 +#: templates/devapp/manage_ports/modal_add_edit_port.html:14 msgid "Edit" msgstr "Редактировать" -#: devapp/templates/devapp/manage_ports/modal_del_port.html:7 +#: templates/devapp/manage_ports/modal_del_port.html:7 msgid "Are you sure?" msgstr "Вы уверены?" -#: devapp/templates/devapp/manage_ports/modal_del_port.html:11 +#: templates/devapp/manage_ports/modal_del_port.html:11 msgid "Are you sure that you want to delete switch port from db?" msgstr "Вы уверены что хотите удалить порт свича из бд?" -#: devapp/templates/devapp/manage_ports/modal_show_subscriber_on_port.html:5 +#: templates/devapp/manage_ports/modal_show_subscriber_on_port.html:5 msgid "Subscriber on port" msgstr "Абонент на порту" -#: devapp/views.py:80 -msgid "Delete failed" -msgstr "Неизвестная ошибка при удалении :(" +#: views.py:87 +msgid "Device successfully deleted" +msgstr "Устройство успешно удалено" -#: devapp/views.py:109 +#: views.py:115 msgid "You have redirected to existing device" msgstr "Вы были переадресованы на существующее устройство" -#: devapp/views.py:112 devapp/views.py:361 devapp/views.py:457 +#: views.py:118 views.py:368 views.py:481 msgid "Please attach group for device" msgstr "Пожалуйста назначте устройству группу в настройках" -#: devapp/views.py:121 +#: views.py:127 msgid "Device info has been saved" msgstr "Инфа о точке сохранена" -#: devapp/views.py:124 devapp/views.py:307 devapp/views.py:336 -#: devapp/views.py:459 +#: views.py:130 views.py:314 views.py:343 views.py:483 msgid "Form is invalid, check fields and try again" msgstr "Ошибка в данных, проверте их ещё раз" -#: devapp/views.py:127 +#: views.py:133 #, python-format msgid "Duplicate user and port: %s" msgstr "Пользователь с таким портом и устройством уже есть: %s" -#: devapp/views.py:168 devapp/views.py:230 -msgid "Device does not have a group, please fix that" -msgstr "У устройства нет группы, пожалуйста, исправьте это" - -#: devapp/views.py:173 devapp/views.py:263 devapp/views.py:348 +#: views.py:180 views.py:270 views.py:355 msgid "Device does not exist" msgstr "Устойство не найдено" -#: devapp/views.py:194 +#: views.py:201 msgid "Subscribers on port does not exist" msgstr "Абоненты на порту не найдены" -#: devapp/views.py:196 +#: views.py:203 msgid "More than one subscriber on device port" msgstr "Больше одного абонента на порту устройства" -#: devapp/views.py:282 +#: views.py:289 msgid "Port successfully removed" msgstr "Порт успешно удалён" -#: devapp/views.py:290 devapp/views.py:318 +#: views.py:297 views.py:325 msgid "Port does not exist" msgstr "Порт не найден" -#: devapp/views.py:305 devapp/views.py:333 +#: views.py:312 views.py:340 msgid "Port successfully saved" msgstr "Порт успешно сохранён" -#: devapp/views.py:368 devapp/views.py:411 +#: views.py:375 views.py:431 msgid "Dot was not pinged" msgstr "Эта точка не пингуется" -#: devapp/views.py:377 devapp/views.py:409 +#: views.py:384 views.py:429 msgid "Not Set snmp device password" msgstr "Не указан snmp пароль для устройства" -#: devapp/views.py:385 +#: views.py:392 msgid "SNMP error on device" msgstr "Ошибка SNMP на устройстве" -#: devapp/views.py:454 +#: views.py:478 msgid "Device fixed" msgstr "Устройство исправлено" -#: devapp/views.py:483 +#: views.py:508 #, python-format msgid "Device with mac address %(mac)s does not exist" msgstr "Устройство мак адресом %(mac)s не найдено" -#: devapp/views.py:491 +#: views.py:516 msgid "Fixed" msgstr "Исправлено, обновите страницу" -#: devapp/views.py:494 +#: views.py:519 msgid "Parent device not found" msgstr "Вышестоящее устройство не найдено" -#~ msgid "Plugin output" -#~ msgstr "Вывод мониторинга" - msgid "Device %(device_name)s is up" msgstr "%(device_name)s в сети" @@ -532,15 +603,3 @@ msgstr "%(device_name)s недостижим" msgid "Device %(device_name)s getting undefined status code" msgstr "Устройство %(device_name)s получило не определённый код состояния" - -msgid "Ip address or parent device with ip address required for ONU device" -msgstr "Необходим ip адрес. Или родительское устройство с назначенным ip адресом необходимо." - -msgid "Are you sure you want to delete device?" -msgstr "Вы уверены что хотите удалить устройство?" - -msgid "Remove device" -msgstr "Удалить устройство" - -msgid "Device successfully deleted" -msgstr "Устройство успешно удалено" diff --git a/devapp/models.py b/devapp/models.py index 24e5066..4dddfa5 100644 --- a/devapp/models.py +++ b/devapp/models.py @@ -20,6 +20,8 @@ class DeviceMonitoringException(Exception): class Device(models.Model): + _cached_manager = None + ip_address = MyGenericIPAddressField(verbose_name=_('Ip address'), null=True, blank=True) mac_addr = MACAddressField(verbose_name=_('Mac address'), null=True, blank=True, unique=True) comment = models.CharField(_('Comment'), max_length=256) @@ -71,11 +73,14 @@ class Device(models.Model): res = klasses[0][1] if issubclass(res, DevBase): return res - return + raise TypeError('one of types is not subclass of DevBase. ' + 'Or implementation of that device type is not found') def get_manager_object(self) -> DevBase: man_klass = self.get_manager_klass() - return man_klass(self) + if self._cached_manager is None: + self._cached_manager = man_klass(self) + return self._cached_manager # Can attach device to subscriber in subscriber page def has_attachable_to_subscriber(self): diff --git a/devapp/templates/devapp/devices.html b/devapp/templates/devapp/devices.html index 248ae43..8e75e2a 100644 --- a/devapp/templates/devapp/devices.html +++ b/devapp/templates/devapp/devices.html @@ -30,7 +30,6 @@ {% if order_by == 'comment' %}{% endif %} {% trans 'Mac address' %} - {# {% trans 'Plugin output' %} #} {% trans 'Device type' %} @@ -61,7 +60,6 @@ {{ dev.ip_address }} {{ dev.comment }} {{ dev.mac_addr|default:_('Not assigned') }} - {# {{ dev.mon.plugin_output|default:'–' }} #} {{ dev.get_devtype_display }} {% if can_del_dev %} diff --git a/djing/lib/__init__.py b/djing/lib/__init__.py index fc0e76d..d358041 100644 --- a/djing/lib/__init__.py +++ b/djing/lib/__init__.py @@ -1,5 +1,6 @@ import socket import struct +from abc import ABCMeta from datetime import timedelta from collections import Iterator from django.db import models @@ -125,12 +126,10 @@ class LogicError(Exception): pass -def singleton(class_): - instances = {} +class Singleton(type): + _instances = {} - def getinstance(*args, **kwargs): - if class_ not in instances: - instances[class_] = class_(*args, **kwargs) - return instances[class_] - - return getinstance + def __call__(cls, *args, **kwargs): + if cls not in cls._instances: + cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) + return cls._instances[cls] diff --git a/djing/lib/tln/__init__.py b/djing/lib/tln/__init__.py new file mode 100644 index 0000000..5f19e0c --- /dev/null +++ b/djing/lib/tln/__init__.py @@ -0,0 +1,4 @@ +from .tln import * + +__all__ = ('TelnetApi', 'ValidationError', 'ZTEFiberNumberNotFound', 'ZTEFiberIsFull', + 'OnuZteRegisterError', 'ZteOltConsoleError', 'register_onu_ZTE_F660') diff --git a/djing/lib/tln/tln.py b/djing/lib/tln/tln.py new file mode 100755 index 0000000..fa52217 --- /dev/null +++ b/djing/lib/tln/tln.py @@ -0,0 +1,266 @@ +#!/usr/bin/env python3 +import re +import struct +from telnetlib import Telnet +from time import sleep +from typing import Generator, Dict, Optional, Tuple + + +class ZteOltConsoleError(Exception): + pass + + +class OnuZteRegisterError(ZteOltConsoleError): + pass + + +class ZTEFiberIsFull(ZteOltConsoleError): + pass + + +class ZTEFiberNumberNotFound(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): + config_level = [] + + def __init__(self, *args, **kwargs): + timeout = kwargs.get('timeout') + if timeout: + self._timeout = timeout + self._prompt_string = b'ZTE-C320-PKP#' + 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 enter(self, username: bytes, passw: bytes) -> None: + self.read_until(b'Username:') + self.write(username) + self.read_until(b'Password:') + self.write(passw) + + 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], *args, **kwargs): + super().__init__(*args, **kwargs) + self.resize_screen(*screen_size) + + 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'ZTE-C320-PKP(config)#' + 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'ZTE-C320-PKP(config-if)#') + 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'ZTE-C320-PKP(config-if)#') + 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 + + +def register_onu_ZTE_F660(olt_ip: str, onu_sn: bytes, login_passwd: Tuple[bytes, bytes], onu_mac: bytes): + 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)) + 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 < 1: + raise ZTEFiberNumberNotFound + elif 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, 145) + sleep(1) + return + + +if __name__ == '__main__': + ip = '10.40.1.10' + try: + register_onu_ZTE_F660( + olt_ip=ip, onu_sn=b'ZTEGC0458DCE', login_passwd=(b'admin', b'2ekc3'), + onu_mac=b'cc:7b:35:8b:7:0' + ) + except ZteOltConsoleError as e: + print(e) + except ConnectionRefusedError: + print('ERROR: connection refused', ip)