diff --git a/agent/monitoring_agent.py b/agent/monitoring_agent.py index 254121e..bc99216 100755 --- a/agent/monitoring_agent.py +++ b/agent/monitoring_agent.py @@ -21,13 +21,13 @@ def calc_hash(data): return sha256(result_data).hexdigest() -def check_sign(get_list: Iterable, sign: str): +def check_sign(get_list: Iterable, sign_str: str): hashed = '_'.join(get_list) my_sign = calc_hash(hashed) - return sign == my_sign + return sign_str == my_sign -def validate(regexp: Union[bytes, str, re.__Regex], string: AnyStr): +def validate(regexp: Union[bytes, str], string: AnyStr): if not re.match(regexp, string): raise ValueError return string diff --git a/devapp/base_intr.py b/devapp/base_intr.py index a1dee37..2eaad92 100644 --- a/devapp/base_intr.py +++ b/devapp/base_intr.py @@ -5,6 +5,8 @@ from easysnmp import Session from django.utils.translation import gettext +from djing.lib.decorators import abstract_static_method + ListOrError = Union[ Iterable, Union[Exception, Iterable] @@ -15,11 +17,11 @@ class DeviceImplementationError(Exception): pass -class DevBase(object): +class DevBase(object, metaclass=ABCMeta): def __init__(self, dev_instance=None): self.db_instance = dev_instance - @staticmethod + @abstract_static_method def description() -> AnyStr: pass @@ -43,13 +45,11 @@ class DevBase(object): def get_template_name(self) -> AnyStr: """Return path to html template for device""" - @staticmethod - @abstractmethod + @abstract_static_method def has_attachable_to_subscriber() -> bool: """Can connect device to subscriber""" - @staticmethod - @abstractmethod + @abstract_static_method def is_use_device_port() -> bool: """True if used device port while opt82 authorization""" @@ -62,6 +62,13 @@ class DevBase(object): :param v: String value for validate """ + @abstract_static_method + def monitoring_template(device_instance, *args, **kwargs) -> Optional[str]: + """ + Template for monitoring system config + :return: string for config file + """ + class BasePort(object, metaclass=ABCMeta): def __init__(self, num, name, status, mac, speed): @@ -89,19 +96,29 @@ class SNMPBaseWorker(object, metaclass=ABCMeta): def __init__(self, ip: Optional[str], community='public', ver=2): if ip is None or ip == '': raise DeviceImplementationError(gettext('Ip address is required')) - self.ses = Session(hostname=ip, community=community, version=ver) + self._ip = ip + self._community = community + self._ver = ver + + def start_ses(self): + if self.ses is None: + self.ses = Session(hostname=self._ip, community=self._community, version=self._ver) def set_int_value(self, oid: str, value): + self.start_ses() return self.ses.set(oid, value, 'i') def get_list(self, oid) -> Generator: + self.start_ses() for v in self.ses.walk(oid): yield v.value def get_list_keyval(self, oid) -> Generator: + self.start_ses() for v in self.ses.walk(oid): snmpnum = v.oid.split('.')[-1:] yield v.value, snmpnum[0] if len(snmpnum) > 0 else None def get_item(self, oid): + self.start_ses() return self.ses.get(oid).value diff --git a/devapp/dev_types.py b/devapp/dev_types.py index a785b79..b2db57d 100644 --- a/devapp/dev_types.py +++ b/devapp/dev_types.py @@ -1,6 +1,8 @@ +import re from typing import AnyStr, Iterable, Optional, Dict from datetime import timedelta from easysnmp import EasySNMPTimeoutError +from transliterate import translit from django.utils.translation import gettext_lazy as _, gettext from djing.lib import RuTimedelta, safe_int @@ -8,6 +10,34 @@ from djing.lib.tln.tln import ValidationError as TlnValidationError from .base_intr import DevBase, SNMPBaseWorker, BasePort, DeviceImplementationError, ListOrError +def _norm_name(name: str, replreg=None): + if replreg is None: + return re.sub(pattern='\W{1,255}', repl='', string=name, flags=re.IGNORECASE) + return replreg.sub('', name) + + +def plain_ip_device_mon_template(device) -> Optional[AnyStr]: + if not device: + raise ValueError + + parent_host_name = _norm_name("%d%s" % ( + device.parent_dev.pk, translit(device.parent_dev.comment, language_code='ru', reversed=True) + )) if device.parent_dev else None + + host_name = _norm_name("%d%s" % (device.pk, translit(device.comment, language_code='ru', reversed=True))) + mac_addr = device.mac_addr + r = ( + "define host{", + "\tuse generic-switch", + "\thost_name %s" % host_name, + "\taddress %s" % device.ip_address, + "\tparents %s" % parent_host_name if parent_host_name is not None else '', + "\t_mac_addr %s" % mac_addr if mac_addr is not None else '', + "}\n" + ) + return '\n'.join(i for i in r if i) + + class DLinkPort(BasePort): def __init__(self, num, name, status, mac, speed, snmp_worker): BasePort.__init__(self, num, name, status, mac, speed) @@ -82,6 +112,10 @@ class DLinkDevice(DevBase, SNMPBaseWorker): # Dlink has no require snmp info pass + @staticmethod + def monitoring_template(device, *args, **kwargs) -> Optional[str]: + return plain_ip_device_mon_template(device, *args, **kwargs) + class ONUdev(BasePort): def __init__(self, num, name, status, mac, speed, signal, snmp_worker): @@ -159,6 +193,10 @@ class OLTDevice(DevBase, SNMPBaseWorker): # Olt has no require snmp info pass + @staticmethod + def monitoring_template(device, *args, **kwargs) -> Optional[str]: + return plain_ip_device_mon_template(device) + class OnuDevice(DevBase, SNMPBaseWorker): def __init__(self, dev_instance): @@ -232,6 +270,24 @@ class OnuDevice(DevBase, SNMPBaseWorker): except ValueError: raise TlnValidationError(_('Onu snmp field must be en integer')) + @staticmethod + def monitoring_template(device, *args, **kwargs) -> Optional[str]: + if not device: + return + host_name = _norm_name("%d%s" % (device.pk, translit(device.comment, language_code='ru', reversed=True))) + snmp_item = device.snmp_extra + mac = device.mac_addr + r = ( + "define host{", + "\tuse device-onu", + "\thost_name %s" % host_name, + # "\taddress %s" % device.ip_address, + "\t_snmp_item %s" % snmp_item if snmp_item is not None else '', + "\t_mac_addr %s" % mac if mac is not None else '', + "}\n" + ) + return '\n'.join(i for i in r if i) + class EltexPort(BasePort): def __init__(self, snmp_worker, *args, **kwargs): @@ -287,6 +343,10 @@ class EltexSwitch(DLinkDevice): def is_use_device_port(): return False + @staticmethod + def monitoring_template(device, *args, **kwargs) -> Optional[str]: + return plain_ip_device_mon_template(device) + def conv_signal(lvl: int) -> float: if lvl == 65535: return 0.0 @@ -384,3 +444,21 @@ class ZteOnuDevice(OnuDevice): int(fiber_num), int(onu_port) except ValueError: raise TlnValidationError(_('Zte onu snmp field must be two dot separated integers')) + + @staticmethod + def monitoring_template(device, *args, **kwargs) -> Optional[str]: + if not device: + return + host_name = _norm_name("%d%s" % (device.pk, translit(device.comment, language_code='ru', reversed=True))) + snmp_item = device.snmp_extra + mac = device.mac_addr + r = ( + "define host{", + "\tuse dev-onu-zte-f660", + "\thost_name %s" % host_name, + # "\taddress %s" % device.ip_address, + "\t_snmp_item %s" % snmp_item if snmp_item is not None else '', + "\t_mac_addr %s" % mac if mac is not None else '', + "}\n" + ) + return '\n'.join(i for i in r if i) diff --git a/devapp/locale/ru/LC_MESSAGES/django.po b/devapp/locale/ru/LC_MESSAGES/django.po index 9b685a4..4fb0495 100644 --- a/devapp/locale/ru/LC_MESSAGES/django.po +++ b/devapp/locale/ru/LC_MESSAGES/django.po @@ -603,3 +603,6 @@ msgstr "%(device_name)s недостижим" msgid "Device %(device_name)s getting undefined status code" msgstr "Устройство %(device_name)s получило не определённый код состояния" + +msgid "View" +msgstr "Посмотреть" diff --git a/devapp/models.py b/devapp/models.py index 4dddfa5..e3c0eaf 100644 --- a/devapp/models.py +++ b/devapp/models.py @@ -1,14 +1,15 @@ import os +from typing import Optional, AnyStr +from subprocess import run from django.db import models +from django.conf import settings +from django.utils.translation import gettext_lazy as _ from djing.fields import MACAddressField -from .base_intr import DevBase from djing.lib import MyGenericIPAddressField, MyChoicesAdapter -from . import dev_types -from subprocess import run -from django.conf import settings -from django.utils.translation import gettext_lazy as _ from group_app.models import Group +from . import dev_types +from .base_intr import DevBase class DeviceDBException(Exception): @@ -104,6 +105,10 @@ class Device(models.Model): param = 'update' run((filepath, param, newmac, code)) + def generate_config_template(self) -> Optional[AnyStr]: + mng_class = self.get_manager_klass() + return mng_class.monitoring_template(self) + class Port(models.Model): device = models.ForeignKey(Device, models.CASCADE, verbose_name=_('Device')) diff --git a/devapp/templates/devapp/devices.html b/devapp/templates/devapp/devices.html index 8e75e2a..eeaee62 100644 --- a/devapp/templates/devapp/devices.html +++ b/devapp/templates/devapp/devices.html @@ -43,21 +43,23 @@ {% with can_del_dev=perms.devapp.delete_device can_ch_dev=perms.devapp.change_device grpid=group.id %} {% for dev in devices %} - + {% url 'devapp:view' grpid dev.pk as viewurl %} - {% if dev.status == 'und' %}– - {% else %} - {% if dev.status == 'unr' or dev.status == 'dwn' %} - - {% elif dev.status == 'up' %} - + + {% if dev.status == 'und' %}– {% else %} - + {% if dev.status == 'unr' or dev.status == 'dwn' %} + + {% elif dev.status == 'up' %} + + {% else %} + + {% endif %} {% endif %} - {% endif %} + - {{ dev.ip_address }} + {{ dev.ip_address }} {{ dev.comment }} {{ dev.mac_addr|default:_('Not assigned') }} {{ dev.get_devtype_display }} diff --git a/devapp/templates/devapp/ext.htm b/devapp/templates/devapp/ext.htm index 1563558..ac23656 100644 --- a/devapp/templates/devapp/ext.htm +++ b/devapp/templates/devapp/ext.htm @@ -26,7 +26,7 @@ {% url 'devapp:view' dev.group.pk|default:0 dev.pk as devapp_view %} - {% trans 'Ports' %} {{ dev.ip_address }} + {% trans 'View' %} {{ dev.ip_address }} diff --git a/devapp/urls.py b/devapp/urls.py index fdbf696..8b4c51a 100644 --- a/devapp/urls.py +++ b/devapp/urls.py @@ -8,11 +8,11 @@ urlpatterns = [ url(r'^devices_without_groups$', views.DevicesWithoutGroupsListView.as_view(), name='devices_null_group'), url(r'^fix_onu/$', views.fix_onu, name='fix_onu'), url(r'^(?P\d+)$', views.DevicesListView.as_view(), name='devs'), - url(r'^(?P\d+)/add$', views.dev, name='add'), + url(r'^(?P\d+)/add$', views.DeviceCreateView.as_view(), name='add'), url(r'^(\d+)/(?P\d+)$', views.devview, name='view'), url(r'^(\d+)/(?P\d+)/del$', views.DeviceVeleteView.as_view(), name='del'), url(r'^(?P\d+)/(?P\d+)/add$', views.add_single_port, name='add_port'), - url(r'^(?P\d+)/(?P\d+)/edit$', views.dev, name='edit'), + url(r'^(?P\d+)/(?P\d+)/edit$', views.DeviceUpdate.as_view(), name='edit'), url(r'^(\d+)/(?P\d+)/ports$', views.manage_ports, name='manage_ports'), url(r'^(?P\d+)/(?P\d+)/ports/(?P\d+)/fix_port_conflict$', views.fix_port_conflict, name='fix_port_conflict'), @@ -32,6 +32,6 @@ urlpatterns = [ url(r'^on_device_event/$', views.OnDeviceMonitoringEvent.as_view()), # Nagios mon generate - url(r'^nagios/hosts/$', views.NagiosObjectsConfView.as_view(), name='nagios_objects_conf'), + url(r'^nagios/hosts/$', views.nagios_objects_conf, name='nagios_objects_conf'), url(r'^api/getall/$', views.DevicesGetListView.as_view()) ] diff --git a/devapp/views.py b/devapp/views.py index 13caf28..708ebc4 100644 --- a/devapp/views.py +++ b/devapp/views.py @@ -1,5 +1,4 @@ import re -from typing import Optional from django.contrib.auth.decorators import login_required from django.contrib.gis.shortcuts import render_to_text from django.core.exceptions import PermissionDenied @@ -11,10 +10,10 @@ 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 DetailView, DeleteView +from django.views.generic import DetailView, DeleteView, UpdateView, CreateView from devapp.base_intr import DeviceImplementationError -from djing.lib.decorators import only_admins +from djing.lib.decorators import only_admins, hash_auth_view from djing.lib import safe_int from abonapp.models import Abon from group_app.models import Group @@ -88,81 +87,138 @@ class DeviceVeleteView(DeleteView): return res -@login_required -@permission_required('devapp.can_view_device') -def dev(request, group_id, device_id=0): - device_group = get_object_or_404(Group, pk=group_id) - if not request.user.has_perm('group_app.can_view_group', device_group): - raise PermissionDenied - devinst = get_object_or_404(Device, id=device_id) if device_id != 0 else None +@method_decorator(login_required, name='dispatch') +@method_decorator(permission_required('devapp.can_view_device'), name='dispatch') +class DeviceUpdate(UpdateView): + template_name = 'devapp/dev.html' + context_object_name = 'dev' + model = Device + form_class = DeviceForm + pk_url_kwarg = 'device_id' + device_group = None already_dev = None - if request.method == 'POST': - if device_id == 0: - if not request.user.has_perm('devapp.add_device'): - raise PermissionDenied - else: - if not request.user.has_perm('devapp.change_device'): - raise PermissionDenied + def post(self, request, *args, **kwargs): + if not request.user.has_perm('devapp.change_device'): + raise PermissionDenied try: - frm = DeviceForm(request.POST, instance=devinst) - if frm.is_valid(): - - # check if that device is exist - try: - already_dev = Device.objects.exclude(pk=device_id).get(mac_addr=request.POST.get('mac_addr')) - if already_dev.group: - messages.warning(request, _('You have redirected to existing device')) - return redirect('devapp:view', already_dev.group.pk, already_dev.pk) - else: - messages.warning(request, _('Please attach group for device')) - return redirect('devapp:fix_device_group', already_dev.pk) - except Device.DoesNotExist: - pass - - # else update device info - ndev = frm.save() - # change device info in dhcpd.conf - ndev.update_dhcp() - messages.success(request, _('Device info has been saved')) - return redirect('devapp:edit', ndev.group.pk, ndev.pk) - else: - messages.error(request, _('Form is invalid, check fields and try again')) + return super().post(request, *args, **kwargs) except IntegrityError as e: if 'unique constraint' in str(e): messages.error(request, _('Duplicate user and port: %s') % e) else: messages.error(request, e) - else: - if devinst is None: - frm = DeviceForm(initial={ - 'group': device_group, - 'devtype': request.GET.get('t'), - 'mac_addr': request.GET.get('mac'), - 'comment': request.GET.get('c'), - 'ip_address': request.GET.get('ip'), - 'man_passw': getattr(settings, 'DEFAULT_SNMP_PASSWORD', ''), - 'snmp_extra': request.GET.get('n') or '' - }) - else: - frm = DeviceForm(instance=devinst) - - if devinst is None: - parent_device_id = request.GET.get('pdev') - return render(request, 'devapp/add_dev.html', { - 'form': frm, - 'group': device_group, - 'already_dev': already_dev, - 'selected_parent_dev': get_object_or_None(Device, pk=parent_device_id) - }) - else: - return render(request, 'devapp/dev.html', { - 'form': frm, - 'dev': devinst, - 'selected_parent_dev': devinst.parent_dev, - 'group': device_group, - 'already_dev': already_dev - }) + return self.form_invalid(self.get_form()) + + def form_valid(self, form): + # check if that device is exist + device_id = self.kwargs.get(self.pk_url_kwarg) + try: + already_dev = self.model.objects.exclude(pk=device_id).get(mac_addr=self.request.POST.get('mac_addr')) + self.already_dev = already_dev + if already_dev.group: + messages.warning(self.request, _('You have redirected to existing device')) + return redirect('devapp:view', already_dev.group.pk, already_dev.pk) + else: + messages.warning(self.request, _('Please attach group for device')) + return redirect('devapp:fix_device_group', already_dev.pk) + except Device.DoesNotExist: + pass + r = super().form_valid(form) + # change device info in dhcpd.conf + print(self.object) + self.object.update_dhcp() + messages.success(self.request, _('Device info has been saved')) + return r + + def get_success_url(self): + return resolve_url('devapp:edit', self.device_group.pk, self.object.pk) + + def dispatch(self, request, *args, **kwargs): + group_id = self.kwargs.get('group_id') + device_group = get_object_or_404(Group, pk=group_id) + if not request.user.has_perm('group_app.can_view_group', device_group): + raise PermissionDenied + self.device_group = device_group + return super().dispatch(request, *args, **kwargs) + + def form_invalid(self, form): + messages.error(self.request, _('Form is invalid, check fields and try again')) + return super().form_invalid(form) + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context['selected_parent_dev'] = self.object.parent_dev + context['group'] = self.device_group + context['already_dev'] = self.already_dev + return context + + +@method_decorator(login_required, name='dispatch') +@method_decorator(permission_required('devapp.can_view_device'), name='dispatch') +class DeviceCreateView(CreateView): + template_name = 'devapp/add_dev.html' + context_object_name = 'dev' + model = Device + form_class = DeviceForm + device_group = None + already_dev = None + + def get(self, request, *args, **kwargs): + if not request.user.has_perm('devapp.add_device'): + raise PermissionDenied + return super().get(request, *args, **kwargs) + + def form_valid(self, form): + # check if that device is exist + device_id = self.kwargs.get(self.pk_url_kwarg) + try: + already_dev = self.model.objects.exclude(pk=device_id).get(mac_addr=self.request.POST.get('mac_addr')) + self.already_dev = already_dev + if already_dev.group: + messages.warning(self.request, _('You have redirected to existing device')) + return redirect('devapp:view', already_dev.group.pk, already_dev.pk) + else: + messages.warning(self.request, _('Please attach group for device')) + return redirect('devapp:fix_device_group', already_dev.pk) + except Device.DoesNotExist: + pass + r = super().form_valid(form) + # change device info in dhcpd.conf + print(self.object) + self.object.update_dhcp() + messages.success(self.request, _('Device info has been saved')) + return r + + def get_success_url(self): + return resolve_url('devapp:edit', self.device_group.pk, self.object.pk) + + def dispatch(self, request, *args, **kwargs): + group_id = self.kwargs.get('group_id') + device_group = get_object_or_404(Group, pk=group_id) + if not request.user.has_perm('group_app.can_view_group', device_group): + raise PermissionDenied + self.device_group = device_group + return super().dispatch(request, *args, **kwargs) + + def get_initial(self): + return { + 'group': self.device_group, + 'devtype': self.request.GET.get('t'), + 'mac_addr': self.request.GET.get('mac'), + 'comment': self.request.GET.get('c'), + 'ip_address': self.request.GET.get('ip'), + 'man_passw': getattr(settings, 'DEFAULT_SNMP_PASSWORD', ''), + 'snmp_extra': self.request.GET.get('n') or '' + } + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context['group'] = self.device_group + context['already_dev'] = self.already_dev + parent_device_id = self.request.GET.get('pdev') + context['selected_parent_dev'] = get_object_or_None(Device, pk=parent_device_id) + return context @login_required @@ -471,7 +527,7 @@ def fix_device_group(request, device_id): device = get_object_or_404(Device, pk=device_id) try: if request.method == 'POST': - frm = DeviceForm(request.POST, instance=dev) + frm = DeviceForm(request.POST, instance=device) if frm.is_valid(): ch_dev = frm.save() if ch_dev.group: @@ -482,7 +538,7 @@ def fix_device_group(request, device_id): else: messages.error(request, _('Form is invalid, check fields and try again')) else: - frm = DeviceForm(instance=dev) + frm = DeviceForm(instance=device) except ValueError: return HttpResponse('ValueError') return render(request, 'devapp/fix_dev_group.html', { @@ -605,70 +661,21 @@ class OnDeviceMonitoringEvent(global_base_views.SecureApiView): } -class NagiosObjectsConfView(global_base_views.AuthenticatedOrHashAuthView): - http_method_names = ('get',) - - def get(self, request, *args, **kwargs): - from transliterate import translit - confs = list() - - def norm_name(name: str, replreg=re.compile(r'\W{1,255}', re.IGNORECASE)): - return replreg.sub('', name) - - for device in Device.objects.exclude(Q(mac_addr=None) | Q(ip_address='127.0.0.1')) \ - .select_related('parent_dev') \ - .only('ip_address', 'comment', 'parent_dev'): - host_name = norm_name("%d%s" % (device.pk, translit(device.comment, language_code='ru', reversed=True))) - conf = None - mac_addr = device.mac_addr - if device.devtype == 'On': - if device.parent_dev: - host_addr = device.parent_dev.ip_address - conf = self.templ_onu(host_name, host_addr, mac=mac_addr, snmp_item=device.snmp_extra or None) - else: - if device.ip_address: - host_addr = device.ip_address - conf = self.templ_onu(host_name, host_addr, mac=mac_addr, snmp_item=device.snmp_extra or None) - else: - parent_host_name = norm_name("%d%s" % ( - device.parent_dev.pk, translit(device.parent_dev.comment, language_code='ru', reversed=True) - )) if device.parent_dev else None - conf = self.templ(host_name, host_addr=device.ip_address, mac=mac_addr, parent_host_name=parent_host_name) - if conf is not None: - confs.append(conf) - response = HttpResponse(''.join(confs), content_type='text/plain') - response['Content-Disposition'] = 'attachment; filename="objects.cfg"' - return response - - @staticmethod - def templ(host_name: str, host_addr: str, mac: Optional[str], parent_host_name: Optional[str]): - if not host_addr: - return - r = ( - "define host{", - "\tuse generic-switch", - "\thost_name %s" % host_name, - "\taddress %s" % host_addr, - "\tparents %s" % parent_host_name if parent_host_name is not None else '', - "\t_mac_addr %s" % mac if mac is not None else '', - "}\n" - ) - return '\n'.join(i for i in r if i) - - @staticmethod - def templ_onu(host_name: str, host_addr: str, mac: Optional[str], snmp_item: str): - if not host_addr: - return - r = ( - "define host{", - "\tuse device-onu", - "\thost_name %s" % host_name, - "\taddress %s" % host_addr, - "\t_snmp_item %s" % snmp_item if snmp_item is not None else '', - "\t_mac_addr %s" % mac if mac is not None else '', - "}\n" - ) - return '\n'.join(i for i in r if i) +@hash_auth_view +def nagios_objects_conf(request): + def getconf(device_instance: Device): + config = device_instance.generate_config_template() + if config is not None: + return config + + devices_queryset = Device.objects.exclude(Q(mac_addr=None) | Q(ip_address='127.0.0.1')) \ + .select_related('parent_dev') \ + .only('ip_address', 'comment', 'parent_dev') + confs = map(getconf, devices_queryset) + confs = (c for c in confs if c is not None) + response = HttpResponse(''.join(confs), content_type='text/plain') + response['Content-Disposition'] = 'attachment; filename="objects.cfg"' + return response class DevicesGetListView(global_base_views.SecureApiView): diff --git a/djing/global_base_views.py b/djing/global_base_views.py index d639cdb..92c081e 100644 --- a/djing/global_base_views.py +++ b/djing/global_base_views.py @@ -1,5 +1,6 @@ -from hashlib import sha256 from json import dumps + +from django.utils.decorators import method_decorator from django.views.generic.base import View from django.http.response import HttpResponseForbidden, Http404, HttpResponseRedirect, HttpResponse from django.utils.translation import gettext_lazy as _ @@ -7,6 +8,7 @@ from django.conf import settings from django.views.generic import ListView from netaddr import IPNetwork, IPAddress from django.core.paginator import InvalidPage, EmptyPage +from djing.lib.decorators import hash_auth_view API_AUTH_SECRET = getattr(settings, 'API_AUTH_SECRET') API_AUTH_SUBNET = getattr(settings, 'API_AUTH_SUBNET') @@ -22,20 +24,8 @@ class RedirectWhenError(Exception): return self.message or '' +@method_decorator(hash_auth_view, name='dispatch') class HashAuthView(View): - @staticmethod - def calc_hash(data): - if type(data) is str: - result_data = data.encode('utf-8') - else: - result_data = bytes(data) - return sha256(result_data).hexdigest() - - @staticmethod - def check_sign(get_list, sign): - hashed = '_'.join(get_list) - my_sign = HashAuthView.calc_hash(hashed) - return sign == my_sign def __init__(self, *args, **kwargs): if API_AUTH_SECRET is None or API_AUTH_SECRET == 'your api secret': @@ -43,22 +33,6 @@ class HashAuthView(View): else: super(HashAuthView, self).__init__(*args, **kwargs) - def dispatch(self, request, *args, **kwargs): - sign = request.GET.get('sign') - if sign is None or sign == '': - return HttpResponseForbidden('Access Denied') - - # Transmittent get list without sign - get_values = request.GET.copy() - del get_values['sign'] - values_list = [l for l in get_values.values() if l] - values_list.sort() - values_list.append(API_AUTH_SECRET) - if self.check_sign(values_list, sign): - return super(HashAuthView, self).dispatch(request, *args, **kwargs) - else: - return HttpResponseForbidden('Access Denied') - class AuthenticatedOrHashAuthView(HashAuthView): diff --git a/djing/lib/__init__.py b/djing/lib/__init__.py index d358041..9373b6a 100644 --- a/djing/lib/__init__.py +++ b/djing/lib/__init__.py @@ -1,6 +1,6 @@ import socket import struct -from abc import ABCMeta +from hashlib import sha256 from datetime import timedelta from collections import Iterator from django.db import models @@ -133,3 +133,21 @@ class Singleton(type): if cls not in cls._instances: cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) return cls._instances[cls] + + +# +# Function for hash auth +# + +def calc_hash(data): + if type(data) is str: + result_data = data.encode('utf-8') + else: + result_data = bytes(data) + return sha256(result_data).hexdigest() + + +def check_sign(get_list, sign): + hashed = '_'.join(get_list) + my_sign = calc_hash(hashed) + return sign == my_sign diff --git a/djing/lib/decorators.py b/djing/lib/decorators.py index 9be54a5..fdd5a18 100644 --- a/djing/lib/decorators.py +++ b/djing/lib/decorators.py @@ -1,10 +1,12 @@ from functools import wraps from django.conf import settings -from django.http import HttpResponseRedirect +from django.http import HttpResponseRedirect, HttpResponseForbidden from django.shortcuts import redirect +from djing.lib import check_sign DEBUG = getattr(settings, 'DEBUG', False) +API_AUTH_SECRET = getattr(settings, 'API_AUTH_SECRET') def require_ssl(view): @@ -33,3 +35,34 @@ def only_admins(fn): else: return redirect('client_side:home') return wrapped + + +# hash auth for functional views +def hash_auth_view(fn): + @wraps(fn) + def wrapped(request, *args, **kwargs): + sign = request.GET.get('sign') + if sign is None or sign == '': + return HttpResponseForbidden('Access Denied') + + # Transmittent get list without sign + get_values = request.GET.copy() + del get_values['sign'] + values_list = [l for l in get_values.values() if l] + values_list.sort() + values_list.append(API_AUTH_SECRET) + if check_sign(values_list, sign): + return fn(request, *args, **kwargs) + else: + return HttpResponseForbidden('Access Denied') + return wrapped + + +class abstract_static_method(staticmethod): + __slots__ = () + + def __init__(self, func): + super(abstract_static_method, self).__init__(func) + func.__isabstractmethod__ = True + + __isabstractmethod__ = True diff --git a/djing/lib/tln/tln.py b/djing/lib/tln/tln.py index fa52217..48ec8b7 100755 --- a/djing/lib/tln/tln.py +++ b/djing/lib/tln/tln.py @@ -18,10 +18,6 @@ class ZTEFiberIsFull(ZteOltConsoleError): pass -class ZTEFiberNumberNotFound(ZteOltConsoleError): - pass - - class ValidationError(ValueError): pass @@ -222,9 +218,7 @@ def register_onu_ZTE_F660(olt_ip: str, onu_sn: bytes, login_passwd: Tuple[bytes, stack_num, rack_num, fiber_num ) - if last_onu_number < 1: - raise ZTEFiberNumberNotFound - elif last_onu_number > 126: + if last_onu_number > 126: raise ZTEFiberIsFull('olt fiber %d is full' % fiber_num) # enter to config @@ -257,8 +251,8 @@ 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' + olt_ip=ip, onu_sn=b'ZTEG^#*$&@&', login_passwd=(b'admin', b'password'), + onu_mac=b'MAC' ) except ZteOltConsoleError as e: print(e)