Browse Source

Make registration button

devel
bashmak 8 years ago
parent
commit
d033dbf8d8
  1. 4
      abonapp/migrations/0001_initial.py
  2. 3
      abonapp/models.py
  3. 13
      devapp/base_intr.py
  4. 52
      devapp/dev_types.py
  5. 16
      devapp/forms.py
  6. 11
      devapp/locale/ru/LC_MESSAGES/django.po
  7. 2
      devapp/migrations/0001_initial.py
  8. 2
      devapp/migrations/0002_auto_20180409_1318.py
  9. 19
      devapp/models.py
  10. 3
      devapp/templates/devapp/dev.html
  11. 3
      devapp/urls.py
  12. 30
      devapp/views.py
  13. 28
      djing/fields.py
  14. 53
      djing/lib/__init__.py
  15. 2
      djing/lib/tln/__init__.py
  16. 3
      djing/lib/tln/tln.py
  17. 5
      docs/dev.md
  18. 2
      statistics/migrations/0001_initial.py
  19. 2
      statistics/models.py

4
abonapp/migrations/0001_initial.py

@ -7,7 +7,7 @@ from django.conf import settings
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
from djing import lib
from djing.fields import MyGenericIPAddressField
import re
@ -30,7 +30,7 @@ class Migration(migrations.Migration):
models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True,
primary_key=True, serialize=False, to='accounts_app.BaseAccount')),
('ballance', models.FloatField(default=0.0)),
('ip_address', lib.MyGenericIPAddressField(blank=True, max_length=8, null=True, protocol='ipv4')),
('ip_address', MyGenericIPAddressField(blank=True, max_length=8, null=True, protocol='ipv4')),
('description', models.TextField(blank=True, null=True, verbose_name='Comment')),
('house', models.CharField(blank=True, max_length=12, null=True, verbose_name='House')),
('is_dynamic_ip', models.BooleanField(default=False)),

3
abonapp/models.py

@ -14,7 +14,8 @@ from django.utils.translation import ugettext_lazy as _, gettext
from accounts_app.models import UserProfile, MyUserManager, BaseAccount
from agent import Transmitter, AbonStruct, TariffStruct, NasFailedResult, NasNetworkError
from group_app.models import Group
from djing.lib import ip2int, MyGenericIPAddressField, LogicError
from djing.lib import ip2int, LogicError
from djing.fields import MyGenericIPAddressField
from djing import IP_ADDR_REGEX
from tariff_app.models import Tariff, PeriodicPay
from bitfield import BitField

13
devapp/base_intr.py

@ -45,14 +45,15 @@ class DevBase(object, metaclass=ABCMeta):
def get_template_name(self) -> AnyStr:
"""Return path to html template for device"""
@abstract_static_method
def has_attachable_to_subscriber() -> bool:
@abstractmethod
def has_attachable_to_subscriber(self) -> bool:
"""Can connect device to subscriber"""
@abstract_static_method
def is_use_device_port() -> bool:
"""True if used device port while opt82 authorization"""
# fixme: only that is abstract static
@abstract_static_method
def validate_extra_snmp_info(v: str) -> None:
"""
@ -62,8 +63,12 @@ class DevBase(object, metaclass=ABCMeta):
:param v: String value for validate
"""
@abstract_static_method
def monitoring_template(device_instance, *args, **kwargs) -> Optional[str]:
@abstractmethod
def register_device(self):
pass
@abstractmethod
def monitoring_template(self, *args, **kwargs) -> Optional[str]:
"""
Template for monitoring system config
:return: string for config file

52
devapp/dev_types.py

@ -6,7 +6,7 @@ from transliterate import translit
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 djing.lib.tln.tln import ValidationError as TlnValidationError, register_onu_ZTE_F660
from .base_intr import DevBase, SNMPBaseWorker, BasePort, DeviceImplementationError, ListOrError
@ -100,8 +100,7 @@ class DLinkDevice(DevBase, SNMPBaseWorker):
def get_template_name(self):
return 'ports.html'
@staticmethod
def has_attachable_to_subscriber():
def has_attachable_to_subscriber(self) -> bool:
return True
@staticmethod
@ -113,8 +112,8 @@ class DLinkDevice(DevBase, SNMPBaseWorker):
# Dlink has no require snmp info
pass
@staticmethod
def monitoring_template(device, *args, **kwargs) -> Optional[str]:
def monitoring_template(self, *args, **kwargs) -> Optional[str]:
device = self.db_instance
return plain_ip_device_mon_template(device, *args, **kwargs)
@ -182,8 +181,7 @@ class OLTDevice(DevBase, SNMPBaseWorker):
def get_template_name(self):
return 'olt.html'
@staticmethod
def has_attachable_to_subscriber():
def has_attachable_to_subscriber(self) -> bool:
return False
@staticmethod
@ -195,8 +193,8 @@ class OLTDevice(DevBase, SNMPBaseWorker):
# Olt has no require snmp info
pass
@staticmethod
def monitoring_template(device, *args, **kwargs) -> Optional[str]:
def monitoring_template(self, *args, **kwargs) -> Optional[str]:
device = self.db_instance
return plain_ip_device_mon_template(device)
@ -235,8 +233,7 @@ class OnuDevice(DevBase, SNMPBaseWorker):
def get_template_name(self):
return "onu.html"
@staticmethod
def has_attachable_to_subscriber():
def has_attachable_to_subscriber(self) -> bool:
return True
@staticmethod
@ -273,8 +270,8 @@ class OnuDevice(DevBase, SNMPBaseWorker):
except ValueError:
raise TlnValidationError(_('Onu snmp field must be en integer'))
@staticmethod
def monitoring_template(device, *args, **kwargs) -> Optional[str]:
def monitoring_template(self, *args, **kwargs) -> Optional[str]:
device = self.db_instance
if not device:
return
host_name = _norm_name("%d%s" % (device.pk, translit(device.comment, language_code='ru', reversed=True)))
@ -344,16 +341,15 @@ class EltexSwitch(DLinkDevice):
tm = RuTimedelta(timedelta(seconds=uptimestamp / 100)) or RuTimedelta(timedelta())
return tm
@staticmethod
def has_attachable_to_subscriber():
def has_attachable_to_subscriber(self) -> bool:
return True
@staticmethod
def is_use_device_port():
return False
@staticmethod
def monitoring_template(device, *args, **kwargs) -> Optional[str]:
def monitoring_template(self, *args, **kwargs) -> Optional[str]:
device = self.db_instance
return plain_ip_device_mon_template(device)
@ -432,6 +428,9 @@ class Olt_ZTE_C320(OLTDevice):
def get_template_name(self):
return 'olt_ztec320.html'
def register_device(self):
pass
class ZteOnuDevice(OnuDevice):
@staticmethod
@ -472,8 +471,8 @@ class ZteOnuDevice(OnuDevice):
except ValueError:
raise TlnValidationError(_('Zte onu snmp field must be two dot separated integers'))
@staticmethod
def monitoring_template(device, *args, **kwargs) -> Optional[str]:
def monitoring_template(self, *args, **kwargs) -> Optional[str]:
device = self.db_instance
if not device:
return
host_name = _norm_name("%d%s" % (device.pk, translit(device.comment, language_code='ru', reversed=True)))
@ -495,3 +494,18 @@ class ZteOnuDevice(OnuDevice):
"}\n"
)
return '\n'.join(i for i in r if i)
def register_device(self):
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()
sn = b"ZTEG%s" % b''.join(mac.split(b':')[-4:]).upper()
register_onu_ZTE_F660(
olt_ip=ip, onu_sn=sn, login_passwd=(b'admin', b'2ekc3'),
onu_mac=mac
)

16
devapp/forms.py

@ -22,13 +22,15 @@ class DeviceForm(forms.ModelForm):
if snmp_extra is None:
return
device = self.instance
manager_class = device.get_manager_klass()
try:
manager_class.validate_extra_snmp_info(snmp_extra)
except TlnValidationError as e:
raise ValidationError(
e, code='invalid'
)
# fixme: if creating device than check disabled
if device.pk is not None:
manager_class = device.get_manager_klass()
try:
manager_class.validate_extra_snmp_info(snmp_extra)
except TlnValidationError as e:
raise ValidationError(
e, code='invalid'
)
return snmp_extra
class Meta:

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

@ -234,7 +234,7 @@ msgstr "Сохранить"
#: templates/devapp/add_dev.html:75 templates/devapp/dev.html:73
#: templates/devapp/fix_dev_group.html:67
msgid "Reset"
msgstr "Сбросить"
msgstr "Сбросить форму"
#: templates/devapp/custom_dev_page/olt.html:10
#: templates/devapp/custom_dev_page/olt_ztec320.html:11
@ -609,3 +609,12 @@ msgstr "Посмотреть"
msgid "Unregistered units"
msgstr "Незарегистрированные юниты"
msgid "Register device"
msgstr "Зарегистрировать устройство"
msgid "Unregistered onu not found"
msgstr "Незарегистрированные ONU не найдены"
msgid "Process locked by another process"
msgstr "Процесс занят другой задачей, подождите чуть и попробуйте ещё"

2
devapp/migrations/0001_initial.py

@ -5,7 +5,7 @@ from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
import djing.fields
from djing.lib import MyGenericIPAddressField
from djing.fields import MyGenericIPAddressField
class Migration(migrations.Migration):

2
devapp/migrations/0002_auto_20180409_1318.py

@ -3,7 +3,7 @@
from __future__ import unicode_literals
from django.db import migrations
from djing.lib import MyGenericIPAddressField
from djing.fields import MyGenericIPAddressField
class Migration(migrations.Migration):

19
devapp/models.py

@ -5,8 +5,8 @@ from django.db import models
from django.conf import settings
from django.utils.translation import gettext_lazy as _
from djing.fields import MACAddressField
from djing.lib import MyGenericIPAddressField, MyChoicesAdapter
from djing.fields import MACAddressField, MyGenericIPAddressField
from djing.lib import MyChoicesAdapter
from group_app.models import Group
from . import dev_types
from .base_intr import DevBase
@ -84,9 +84,9 @@ class Device(models.Model):
return self._cached_manager
# Can attach device to subscriber in subscriber page
def has_attachable_to_subscriber(self):
mngr_class = self.get_manager_klass()
return mngr_class.has_attachable_to_subscriber()
def has_attachable_to_subscriber(self) -> bool:
mngr = self.get_manager_object()
return mngr.has_attachable_to_subscriber()
def __str__(self):
return "%s: (%s) %s %s" % (self.comment, self.get_devtype_display(), self.ip_address or '', self.mac_addr or '')
@ -106,8 +106,13 @@ class Device(models.Model):
run((filepath, param, newmac, code))
def generate_config_template(self) -> Optional[AnyStr]:
mng_class = self.get_manager_klass()
return mng_class.monitoring_template(self)
mng = self.get_manager_object()
return mng.monitoring_template()
def register_device(self):
mng = self.get_manager_object()
# if hasattr(mng, 'register_device') and callable(mng.register_device):
mng.register_device()
class Port(models.Model):

3
devapp/templates/devapp/dev.html

@ -69,6 +69,9 @@
<span class="glyphicon glyphicon-remove"></span> {% trans 'Delete' %}
</a>
{% endif %}
<a href="{% url 'devapp:dev_register' group.pk|default:0 dev.pk %}" class="btn btn-default btn-cmd">
<span class="glyphicon glyphicon-fire"></span> <span class="hidden-xs">{% trans 'Register device' %}</span>
</a>
<button type="reset" class="btn btn-default">
<span class="glyphicon glyphicon-remove-circle"></span> {% trans 'Reset' %}
</button>

3
devapp/urls.py

@ -18,7 +18,8 @@ urlpatterns = [
name='fix_port_conflict'),
url(r'^(?P<group_id>\d+)/(?P<device_id>\d+)/ports/(?P<port_id>\d+)/show_subscriber_on_port$',
views.ShowSubscriberOnPort.as_view(), name='show_subscriber_on_port'),
url(r'^(\d+)/(?P<device_id>\d+)/ports_add', views.add_ports, name='add_ports'),
url(r'^(\d+)/(?P<device_id>\d+)/ports_add/$', views.add_ports, name='add_ports'),
url(r'^(\d+)/(?P<device_id>\d+)/regster_device/$', views.regster_device, name='dev_register'),
url(r'^(\d+)/(?P<device_id>\d+)/(?P<portid>\d+)_(?P<status>[0-1]{1})$', views.toggle_port, name='port_toggle'),
url(r'^(?P<group_id>\d+)/(?P<device_id>\d+)/(?P<portid>\d+)/del$', views.delete_single_port, name='del_port'),
url(r'^(?P<group_id>\d+)/(?P<device_id>\d+)/(?P<port_id>\d+)/edit$', views.edit_single_port, name='edit_port'),

30
devapp/views.py

@ -14,8 +14,9 @@ from django.views.generic import DetailView, DeleteView, UpdateView, CreateView
from devapp.base_intr import DeviceImplementationError
from djing.lib.decorators import only_admins, hash_auth_view
from djing.lib import safe_int
from djing.lib import safe_int, ProcessLocked
from abonapp.models import Abon
from djing.lib.tln import ZteOltConsoleError, OnuZteRegisterError
from group_app.models import Group
from accounts_app.models import UserProfile
from django.conf import settings
@ -693,3 +694,30 @@ class DevicesGetListView(global_base_views.SecureApiView):
if isinstance(r['mac_addr'], EUI):
r['mac_addr'] = int(r['mac_addr'])
return tuple(res)
@login_required
@json_view
def regster_device(request, device_id: str):
def format_msg(msg: str, icon: str):
return ' '.join((
'<span class="glyphicon glyphicon-%s"></span>' % icon,
'<span class="hidden-xs">%s</span>' % msg
))
device = get_object_or_404(Device, pk=device_id)
status = 1
try:
device.register_device()
status = 0
except OnuZteRegisterError:
text = format_msg(gettext('Unregistered onu not found'), 'eye-close')
except (ConnectionRefusedError, ZteOltConsoleError) as e:
text = format_msg(e, 'exclamation-sign')
except ProcessLocked:
text = format_msg(gettext('Process locked by another process'), 'time')
else:
text = format_msg(msg='ok', icon='ok')
return {
'status': status,
'dat': text
}

28
djing/fields.py

@ -4,6 +4,8 @@
from django.core.exceptions import ValidationError
from django.db import models
from netaddr import EUI, AddrFormatError
from djing.lib import ip2int, int2ip
from .formfields import MACAddressField as MACAddressFormField
from . import default_dialect
import warnings
@ -107,3 +109,29 @@ try:
add_introspection_rules([], ["^macaddress\.fields\.MACAddressField"])
except ImportError:
pass
class MyGenericIPAddressField(models.GenericIPAddressField):
description = "Int32 notation ip address"
def __init__(self, protocol='ipv4', *args, **kwargs):
super(MyGenericIPAddressField, self).__init__(protocol=protocol, *args, **kwargs)
self.max_length = 8
def get_prep_value(self, value):
# strIp to Int
value = super(MyGenericIPAddressField, self).get_prep_value(value)
return ip2int(value)
def to_python(self, value):
return value
def get_internal_type(self):
return 'PositiveIntegerField'
@staticmethod
def from_db_value(value, expression, connection, context):
return int2ip(value) if value != 0 else None
def int_ip(self):
return ip2int(self)

53
djing/lib/__init__.py

@ -1,9 +1,9 @@
import socket
import struct
from functools import wraps
from hashlib import sha256
from datetime import timedelta
from collections import Iterator
from django.db import models
def ip2int(addr):
@ -34,32 +34,6 @@ def safe_int(i):
return 0
class MyGenericIPAddressField(models.GenericIPAddressField):
description = "Int32 notation ip address"
def __init__(self, protocol='ipv4', *args, **kwargs):
super(MyGenericIPAddressField, self).__init__(protocol=protocol, *args, **kwargs)
self.max_length = 8
def get_prep_value(self, value):
# strIp to Int
value = super(MyGenericIPAddressField, self).get_prep_value(value)
return ip2int(value)
def to_python(self, value):
return value
def get_internal_type(self):
return 'PositiveIntegerField'
@staticmethod
def from_db_value(value, expression, connection, context):
return int2ip(value) if value != 0 else None
def int_ip(self):
return ip2int(self)
# Предназначен для Django CHOICES чтоб можно было передавать классы вместо просто описания поля,
# классы передавать для того чтоб по значению кода из базы понять какой класс нужно взять для нужной функциональности.
# Например по коду в базе вам нужно определять как считать тариф абонента, что реализовано в возвращаемом классе.
@ -151,3 +125,28 @@ def check_sign(get_list, sign):
hashed = '_'.join(get_list)
my_sign = calc_hash(hashed)
return sign == my_sign
#
# only one process for function
#
class ProcessLocked(OSError):
pass
def process_lock(fn):
@wraps(fn)
def wrapped(*args, **kwargs):
s = None
try:
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
# Create an abstract socket, by prefixing it with null.
s.bind('\0postconnect_djing_lock_func_%s' % fn.__name__)
return fn(*args, **kwargs)
except socket.error:
raise ProcessLocked
finally:
if s is not None:
s.close()
return wrapped

2
djing/lib/tln/__init__.py

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

3
djing/lib/tln/tln.py

@ -5,6 +5,8 @@ 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
@ -192,6 +194,7 @@ class OltZTERegister(TelnetApi):
))) + r
@process_lock
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'

5
docs/dev.md

@ -77,8 +77,7 @@ class EltexSwitch(DLinkDevice):
tm = RuTimedelta(timedelta(seconds=uptimestamp/100)) or RuTimedelta(timedelta())
return tm
@staticmethod
def has_attachable_to_subscriber():
def has_attachable_to_subscriber(self) -> bool:
return False
@staticmethod
@ -99,7 +98,7 @@ class EltexSwitch(DLinkDevice):
Метод **@uptime**, понятно что возвращает, укажите нужный OID. Вернётся тип *RuTimedelta*, это переопределённый тип **timedelta**, я его реализовал
для локализации временного промежутка на русский.
Статический метод **@has_attachable_to_subscriber** возвращает правду если это устройство можно привязать к абоненту.
Метод **@has_attachable_to_subscriber** возвращает правду если это устройство можно привязать к абоненту.
Например у Dlink стоит True потому что Dlink стоит во многих местах на доступе, и его порты принадлежат
абонентам при авторизации.

2
statistics/migrations/0001_initial.py

@ -3,7 +3,7 @@
from __future__ import unicode_literals
from django.db import migrations, models
from djing.lib import MyGenericIPAddressField
from djing.fields import MyGenericIPAddressField
import statistics.fields

2
statistics/models.py

@ -3,7 +3,7 @@ from datetime import datetime, timedelta, date, time
from django.db import models, connection, ProgrammingError
from django.utils.timezone import now
from djing.lib import MyGenericIPAddressField
from djing.fields import MyGenericIPAddressField
from .fields import UnixDateTimeField

Loading…
Cancel
Save