From 7b7e07fa0dadf6fe4c07ca35663781369d2a376d Mon Sep 17 00:00:00 2001 From: bashmak Date: Mon, 2 Apr 2018 15:32:52 +0300 Subject: [PATCH] Fix when error while snmp walk --- agent/mod_mikrotik.py | 46 ++++++++++++------------------------------ devapp/base_intr.py | 29 +++++++++++++++++--------- devapp/dev_types.py | 47 ++++++++++++++++++++++++------------------- devapp/views.py | 15 ++++++++------ 4 files changed, 67 insertions(+), 70 deletions(-) diff --git a/agent/mod_mikrotik.py b/agent/mod_mikrotik.py index 628281b..a432c93 100644 --- a/agent/mod_mikrotik.py +++ b/agent/mod_mikrotik.py @@ -3,7 +3,7 @@ import socket import binascii from abc import ABCMeta from hashlib import md5 -from typing import List, Iterable +from typing import List, Iterable, Optional, Tuple from .core import BaseTransmitter, NasFailedResult, NasNetworkError from mydefs import ping, singleton from .structs import TariffStruct, AbonStruct, IpStruct, VectorAbon, VectorTariff @@ -336,7 +336,7 @@ class IpAddressListObj(IpStruct): class IpAddressListManager(TransmitterManager, metaclass=ABCMeta): - def add(self, list_name: str, ip: IpStruct, timeout=0): + def add(self, list_name: str, ip: IpStruct): if not isinstance(ip, IpStruct): raise TypeError commands = [ @@ -344,18 +344,8 @@ class IpAddressListManager(TransmitterManager, metaclass=ABCMeta): '=list=%s' % list_name, '=address=%s' % str(ip) ] - if timeout > 0: - commands.append('=timeout=%d' % timeout) return self._exec_cmd(commands) - def _edit(self, mk_id, timeout=None): - if timeout is not None: - commands = [ - '/ip/firewall/address-list/set', '=.id=' + str(mk_id), - '=timeout=%d' % timeout - ] - return self._exec_cmd(commands) - def remove(self, mk_id): return self._exec_cmd([ '/ip/firewall/address-list/remove', @@ -410,12 +400,10 @@ class IpAddressListManager(TransmitterManager, metaclass=ABCMeta): class MikrotikTransmitter(QueueManager, IpAddressListManager): def add_user_range(self, user_list: VectorAbon): - super(MikrotikTransmitter, self).add_user_range(user_list) for usr in user_list: self.add_user(usr) def remove_user_range(self, users: VectorAbon): - super(MikrotikTransmitter, self).remove_user_range(users) queue_ids = [usr.queue_id for usr in users if usr is not None] QueueManager.remove_range(self, queue_ids) ips = [user.ip for user in users if isinstance(user, AbonStruct)] @@ -424,34 +412,30 @@ class MikrotikTransmitter(QueueManager, IpAddressListManager): if ip_list_entity is not None and len(ip_list_entity) > 1: IpAddressListManager.remove(self, ip_list_entity[0]['=.id']) - def add_user(self, user: AbonStruct, ip_timeout=0): - super(MikrotikTransmitter, self).add_user(user, ip_timeout) + def add_user(self, user: AbonStruct, *args): if not isinstance(user.ip, IpStruct): raise TypeError if user.tariff is None or not isinstance(user.tariff, TariffStruct): return QueueManager.add(self, user) - IpAddressListManager.add(self, LIST_USERS_ALLOWED, user.ip, ip_timeout) - # удаляем из списка заблокированных абонентов + IpAddressListManager.add(self, LIST_USERS_ALLOWED, user.ip) + # remove user from denied user list firewall_ip_list_obj = IpAddressListManager.find(self, user.ip, LIST_USERS_BLOCKED) if len(firewall_ip_list_obj) > 1: IpAddressListManager.remove(self, firewall_ip_list_obj[0]['=.id']) def remove_user(self, user: AbonStruct): - super(MikrotikTransmitter, self).remove_user(user) QueueManager.remove(self, user) firewall_ip_list_obj = IpAddressListManager.find(self, user.ip, LIST_USERS_ALLOWED) if firewall_ip_list_obj is not None and len(firewall_ip_list_obj) > 1: IpAddressListManager.remove(self, firewall_ip_list_obj[0]['=.id']) - # обновляем основную инфу абонента - def update_user(self, user: AbonStruct, ip_timeout=0): - super(MikrotikTransmitter, self).update_user(user, ip_timeout) + def update_user(self, user: AbonStruct, *args): if not isinstance(user.ip, IpStruct): raise TypeError - # ищем ip абонента в списке ip find_res = IpAddressListManager.find(self, user.ip, LIST_USERS_ALLOWED) + queue = QueueManager.find(self, 'uid%d' % user.uid) if not user.is_active: # если не активен - то и обновлять не надо @@ -459,11 +443,12 @@ class MikrotikTransmitter(QueueManager, IpAddressListManager): if len(find_res) > 1: # и если найден был - то удалим ip из разрешённых IpAddressListManager.remove(self, find_res[0]['=.id']) + if queue is not None: + QueueManager.remove(self, user) return # если нет услуги то её не должно быть и в nas if user.tariff is None or not isinstance(user.tariff, TariffStruct): - queue = QueueManager.find(self, 'uid%d' % user.uid) if queue is not None: QueueManager.remove(self, user) return @@ -471,22 +456,17 @@ class MikrotikTransmitter(QueueManager, IpAddressListManager): # если не найден (mikrotik возвращает пустой словарь в списке если ничего нет) if len(find_res) < 2: # добавим запись об абоненте - IpAddressListManager.add(self, LIST_USERS_ALLOWED, user.ip, ip_timeout) - else: - mk_id = find_res[0]['=.id'] - # то обновляем запись в mikrotik - IpAddressListManager._edit(self, mk_id, ip_timeout) + IpAddressListManager.add(self, LIST_USERS_ALLOWED, user.ip) # Проверяем шейпер - queue = QueueManager.find(self, 'uid%d' % user.uid) + if queue is None: QueueManager.add(self, user) return if queue != user: QueueManager.update(self, user) - def ping(self, host, count=10): - super(MikrotikTransmitter, self).ping(host) + def ping(self, host, count=10) -> Optional[Tuple[int, int]]: r = self._exec_cmd([ '/ip/arp/print', '?address=%s' % host @@ -520,7 +500,7 @@ class MikrotikTransmitter(QueueManager, IpAddressListManager): def remove_tariff(self, tid: int): pass - def read_users(self): + def read_users(self) -> Iterable[AbonStruct]: # shapes is ShapeItem allowed_ips = set(IpAddressListManager.read_ips_iter(self, LIST_USERS_ALLOWED)) queues = set(q for q in QueueManager.read_queue_iter(self) if q.ip in allowed_ips) diff --git a/devapp/base_intr.py b/devapp/base_intr.py index db61a66..dd714e4 100644 --- a/devapp/base_intr.py +++ b/devapp/base_intr.py @@ -1,7 +1,16 @@ from abc import ABCMeta, abstractmethod +from datetime import timedelta +from typing import Union, Iterable, AnyStr, Generator + from easysnmp import Session +ListOrError = Union[ + Iterable, + Union[Exception, Iterable] +] + + class DeviceImplementationError(Exception): pass @@ -12,7 +21,7 @@ class DevBase(object, metaclass=ABCMeta): self.db_instance = dev_instance @staticmethod - def description(): + def description() -> AnyStr: pass @abstractmethod @@ -20,29 +29,29 @@ class DevBase(object, metaclass=ABCMeta): pass @abstractmethod - def get_ports(self): + def get_ports(self) -> ListOrError: pass @abstractmethod - def get_device_name(self): + def get_device_name(self) -> AnyStr: """Return device name by snmp""" @abstractmethod - def uptime(self): + def uptime(self) -> timedelta: pass @abstractmethod - def get_template_name(self): + def get_template_name(self) -> AnyStr: """Return path to html template for device""" @staticmethod @abstractmethod - def has_attachable_to_subscriber(): + def has_attachable_to_subscriber() -> bool: """Can connect device to subscriber""" @staticmethod @abstractmethod - def is_use_device_port(): + def is_use_device_port() -> bool: """True if used device port while opt82 authorization""" @@ -62,7 +71,7 @@ class BasePort(object, metaclass=ABCMeta): def enable(self): pass - def mac(self): + def mac(self) -> str: return ':'.join(['%x' % ord(i) for i in self._mac]) @@ -75,11 +84,11 @@ class SNMPBaseWorker(object, metaclass=ABCMeta): def set_int_value(self, oid, value): return self.ses.set(oid, value, 'i') - def get_list(self, oid): + def get_list(self, oid) -> Generator: for v in self.ses.walk(oid): yield v.value - def get_list_keyval(self, oid): + def get_list_keyval(self, oid) -> Generator: for v in self.ses.walk(oid): snmpnum = v.oid.split('.')[-1:] yield v.value, snmpnum[0] if len(snmpnum) > 0 else None diff --git a/devapp/dev_types.py b/devapp/dev_types.py index 4d96ee8..5e5b778 100644 --- a/devapp/dev_types.py +++ b/devapp/dev_types.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -from django.utils.translation import gettext_lazy as _ +from django.utils.translation import gettext_lazy as _, gettext from mydefs import RuTimedelta, safe_int from datetime import timedelta from easysnmp import EasySNMPTimeoutError -from .base_intr import DevBase, SNMPBaseWorker, BasePort, DeviceImplementationError +from .base_intr import DevBase, SNMPBaseWorker, BasePort, DeviceImplementationError, ListOrError class DLinkPort(BasePort): @@ -40,7 +40,7 @@ class DLinkDevice(DevBase, SNMPBaseWorker): def reboot(self): return self.get_item('.1.3.6.1.4.1.2021.8.1.101.1') - def get_ports(self): + def get_ports(self) -> ListOrError: interfaces_count = safe_int(self.get_item('.1.3.6.1.2.1.2.1.0')) nams = list(self.get_list('.1.3.6.1.4.1.171.10.134.2.1.1.100.2.1.3')) stats = list(self.get_list('.1.3.6.1.2.1.2.2.1.7')) @@ -59,7 +59,7 @@ class DLinkDevice(DevBase, SNMPBaseWorker): self)) return res except IndexError: - raise DeviceImplementationError('Dlink port index error') + return DeviceImplementationError('Dlink port index error'), res def get_device_name(self): return self.get_item('.1.3.6.1.2.1.1.1.0') @@ -114,23 +114,28 @@ class OLTDevice(DevBase, SNMPBaseWorker): def reboot(self): pass - def get_ports(self): + def get_ports(self) -> ListOrError: nms = self.get_list('.1.3.6.1.4.1.3320.101.10.1.1.79') res = [] - for nm in nms: - n = int(nm) - status = self.get_item('.1.3.6.1.4.1.3320.101.10.1.1.26.%d' % n) - signal = self.get_item('.1.3.6.1.4.1.3320.101.10.5.1.5.%d' % n) - onu = ONUdev( - num=n, - name=self.get_item('.1.3.6.1.2.1.2.2.1.2.%d' % n), - status=True if status == '3' else False, - mac=self.get_item('.1.3.6.1.4.1.3320.101.10.1.1.3.%d' % n), - speed=0, - signal=int(signal) / 10 if signal != 'NOSUCHINSTANCE' else 0, - snmpWorker=self) - res.append(onu) + try: + for nm in nms: + n = int(nm) + status = self.get_item('.1.3.6.1.4.1.3320.101.10.1.1.26.%d' % n) + signal = self.get_item('.1.3.6.1.4.1.3320.101.10.5.1.5.%d' % n) + onu = ONUdev( + num=n, + name=self.get_item('.1.3.6.1.2.1.2.2.1.2.%d' % n), + status=True if status == '3' else False, + mac=self.get_item('.1.3.6.1.4.1.3320.101.10.1.1.3.%d' % n), + speed=0, + signal=int(signal) / 10 if signal != 'NOSUCHINSTANCE' else 0, + snmpWorker=self) + res.append(onu) + except EasySNMPTimeoutError as e: + return EasySNMPTimeoutError( + "%s (%s)" % (gettext('wait for a reply from the SNMP Timeout'), e) + ), res return res def get_device_name(self): @@ -166,8 +171,8 @@ class OnuDevice(DevBase, SNMPBaseWorker): def reboot(self): pass - def get_ports(self): - pass + def get_ports(self) -> ListOrError: + return [] def get_device_name(self): pass @@ -239,7 +244,7 @@ class EltexSwitch(DLinkDevice): def description(): return _('Eltex switch') - def get_ports(self): + def get_ports(self) -> ListOrError: #nams = self.get_list('.1.3.6.1.4.1.171.10.134.2.1.1.100.2.1.3') stats = list(self.get_list('.1.3.6.1.2.1.2.2.1.7')) oper_stats = list(self.get_list('.1.3.6.1.2.1.2.2.1.8')) diff --git a/devapp/views.py b/devapp/views.py index f4a0e12..21595ac 100644 --- a/devapp/views.py +++ b/devapp/views.py @@ -11,7 +11,7 @@ from django.contrib import messages from django.utils.decorators import method_decorator from django.utils.translation import gettext_lazy as _, gettext from easysnmp import EasySNMPTimeoutError, EasySNMPError -from django.views.generic import ListView, DetailView +from django.views.generic import DetailView from devapp.base_intr import DeviceImplementationError from mydefs import res_success, res_error, only_admins, ping, ip_addr_regex @@ -30,13 +30,13 @@ from .forms import DeviceForm, PortForm from mydefs import safe_int -class BaseDeviceListView(ListView): +class BaseDeviceListView(global_base_views.BaseListWithFiltering): http_method_names = ['get'] paginate_by = getattr(settings, 'PAGINATION_ITEMS_PER_PAGE', 10) @method_decorator([login_required, only_admins], name='dispatch') -class DevicesListView(BaseDeviceListView, global_base_views.OrderingMixin): +class DevicesListView(global_base_views.OrderingMixin, BaseDeviceListView): context_object_name = 'devices' template_name = 'devapp/devices.html' @@ -63,7 +63,7 @@ class DevicesListView(BaseDeviceListView, global_base_views.OrderingMixin): @method_decorator([login_required, only_admins], name='dispatch') -class DevicesWithoutGroupsListView(BaseDeviceListView, global_base_views.OrderingMixin): +class DevicesWithoutGroupsListView(global_base_views.OrderingMixin, BaseDeviceListView): context_object_name = 'devices' template_name = 'devapp/devices_null_group.html' queryset = Device.objects.filter(group=None).only('comment', 'devtype', 'pk', 'ip_address') @@ -366,6 +366,9 @@ def devview(request, device_id): if dev.man_passw: manager = dev.get_manager_object() ports = manager.get_ports() + if isinstance(ports[0], Exception): + messages.error(request, ports[0]) + ports = ports[1] template_name = manager.get_template_name() else: messages.warning(request, _('Not Set snmp device password')) @@ -377,8 +380,8 @@ def devview(request, device_id): 'dev_accs': Abon.objects.filter(device=dev), 'dev_manager': manager }) - except EasySNMPError: - messages.error(request, _('SNMP error on device')) + except EasySNMPError as e: + messages.error(request, "%s: %s" % (gettext('SNMP error on device'), e)) except (DeviceDBException, DeviceImplementationError) as e: messages.error(request, e) return render(request, 'devapp/custom_dev_page/' + template_name, {