diff --git a/devapp/base_intr.py b/devapp/base_intr.py index 085383d..84a5077 100644 --- a/devapp/base_intr.py +++ b/devapp/base_intr.py @@ -33,7 +33,7 @@ class DevBase(object, metaclass=ABCMeta): return cls.description @abstractmethod - def reboot(self): + def reboot(self, save_before_reboot=False): pass @abstractmethod @@ -70,7 +70,7 @@ class DevBase(object, metaclass=ABCMeta): def validate_extra_snmp_info(v: str) -> None: """ Validate extra snmp field for each device. - If validation failed then raise en exception from devapp.onu_config.ExpectValidationError + If validation failed then raise en exception from devapp.expect_scripts.ExpectValidationError with description of error. :param v: String value for validate """ diff --git a/devapp/dev_types.py b/devapp/dev_types.py index 29f9df7..901634d 100644 --- a/devapp/dev_types.py +++ b/devapp/dev_types.py @@ -1,4 +1,4 @@ -import re +import os import re from typing import AnyStr, Iterable, Optional, Dict from datetime import timedelta @@ -6,9 +6,10 @@ from easysnmp import EasySNMPTimeoutError from pexpect import TIMEOUT from transliterate import translit from django.utils.translation import gettext_lazy as _, gettext +from django.conf import settings from djing.lib import RuTimedelta, safe_int -from devapp.onu_config import register_f601_onu, register_f660_onu, ExpectValidationError, OnuZteRegisterError +from devapp.expect_scripts import register_f601_onu, register_f660_onu, ExpectValidationError, OnuZteRegisterError from .base_intr import ( DevBase, SNMPBaseWorker, BasePort, DeviceImplementationError, ListOrError, DeviceConfigurationError @@ -43,6 +44,18 @@ def plain_ip_device_mon_template(device) -> Optional[AnyStr]: return '\n'.join(i for i in r if i) +def ex_expect(filename, params=()): + base_dir = getattr(settings, 'BASE_DIR') + if base_dir is not None: + exec_file = os.path.join(base_dir, 'devapp', 'expect_scripts', filename) + if os.path.isfile(exec_file) and os.access(path=exec_file, mode=os.X_OK): + params = ' '.join(str(p) for p in params) + if params: + return os.system('%s %s' % (exec_file, params)) + else: + return os.system(exec_file) + + class DLinkPort(BasePort): def __init__(self, snmp_worker, *args, **kwargs): BasePort.__init__(self, writable=True, *args, **kwargs) @@ -71,8 +84,18 @@ class DLinkDevice(DevBase, SNMPBaseWorker): DevBase.__init__(self, dev_instance) SNMPBaseWorker.__init__(self, dev_instance.ip_address, dev_instance.man_passw, 2) - def reboot(self): - return self.get_item('.1.3.6.1.4.1.2021.8.1.101.1') + def reboot(self, save_before_reboot=False): + dat = self.db_instance.extra_data + if dat is None: + return + login = dat.get('login') + passw = dat.get('password') + if login and passw: + return ex_expect('dlink_DGS1100_reboot.exp', ( + self.db_instance.ip_address, + login, passw, + 1 if save_before_reboot else 0 + )) def get_ports(self) -> ListOrError: interfaces_count = safe_int(self.get_item('.1.3.6.1.2.1.2.1.0')) @@ -144,7 +167,7 @@ class OLTDevice(DevBase, SNMPBaseWorker): DevBase.__init__(self, dev_instance) SNMPBaseWorker.__init__(self, dev_instance.ip_address, dev_instance.man_passw, 2) - def reboot(self): + def reboot(self, save_before_reboot=False): pass def get_ports(self) -> ListOrError: @@ -215,7 +238,7 @@ class OnuDevice(DevBase, SNMPBaseWorker): )) SNMPBaseWorker.__init__(self, dev_ip_addr, dev_instance.man_passw, 2) - def reboot(self): + def reboot(self, save_before_reboot=False): pass def get_ports(self) -> ListOrError: @@ -341,6 +364,9 @@ class EltexSwitch(DLinkDevice): device = self.db_instance return plain_ip_device_mon_template(device) + def reboot(self, save_before_reboot=False): + pass + def conv_signal(lvl: int) -> float: if lvl == 65535: return 0.0 @@ -581,3 +607,6 @@ class HuaweiSwitch(EltexSwitch): ) ep.writable = True yield ep + + def reboot(self, save_before_reboot=False): + pass diff --git a/devapp/onu_config/__init__.py b/devapp/expect_scripts/__init__.py similarity index 100% rename from devapp/onu_config/__init__.py rename to devapp/expect_scripts/__init__.py diff --git a/devapp/onu_config/base.py b/devapp/expect_scripts/base.py similarity index 100% rename from devapp/onu_config/base.py rename to devapp/expect_scripts/base.py diff --git a/devapp/expect_scripts/dlink_DGS1100_reboot.exp b/devapp/expect_scripts/dlink_DGS1100_reboot.exp new file mode 100755 index 0000000..08fe21d --- /dev/null +++ b/devapp/expect_scripts/dlink_DGS1100_reboot.exp @@ -0,0 +1,57 @@ +#!/usr/bin/expect -f + +if { $argc <4 } { + puts "Usage: dlink_reboot.exp \n" + exit 2 +} + +set timeout -1 +set ip [lindex $argv 0] +set login [lindex $argv 1] +set passw [lindex $argv 2] +set is_save [lindex $argv 3] + +spawn telnet -4 $ip + +expect { + "UserName:" { + send -- "$login\r" + set prmpt "DGS-1100-06/ME:5" + } + "DGS-1100-10/ME login: " { + send -- "$login\r" + set prmpt "DGS-1100-10/ME:5" + } +} + +expect -exact "Password: " +send -- "$passw\r" + +expect { + "Incorrect Login/Password" { + puts "Wrong password" + exit 1 + } + "$prmpt# " { + puts "Login ok" + } +} + +if { $is_save == 1 } { + puts "Save" + send -- "save\r" +} elseif { $is_save == 0 } { + puts "Without save" +} else { + puts "Unexpected choice" + exit 2 +} + + +send -- "reboot\r" +expect "Are you sure you want to proceed with the system reboot" + +send -- "y\r" +expect -exact "rebooting" + +expect eof diff --git a/devapp/onu_config/f601.py b/devapp/expect_scripts/f601.py similarity index 100% rename from devapp/onu_config/f601.py rename to devapp/expect_scripts/f601.py diff --git a/devapp/onu_config/f660.py b/devapp/expect_scripts/f660.py similarity index 100% rename from devapp/onu_config/f660.py rename to devapp/expect_scripts/f660.py diff --git a/devapp/forms.py b/devapp/forms.py index 8f37db2..a2f2da8 100644 --- a/devapp/forms.py +++ b/devapp/forms.py @@ -4,7 +4,7 @@ from django.utils.translation import gettext_lazy as _ from django.db import IntegrityError from djing.lib import DuplicateEntry -from devapp.onu_config import ExpectValidationError +from devapp.expect_scripts import ExpectValidationError from . import models from djing import MAC_ADDR_REGEX, IP_ADDR_REGEX @@ -87,3 +87,10 @@ class PortForm(forms.ModelForm): raise DuplicateEntry(_('Port number on device must be unique')) else: raise models.DeviceDBException(e) + + +class DeviceRebootForm(forms.Form): + def __init__(self, instance=None, *args, **kwargs): + super().__init__(*args, **kwargs) + + is_save = forms.BooleanField(label=_('Is save before reboot'), required=False) diff --git a/devapp/locale/ru/LC_MESSAGES/django.po b/devapp/locale/ru/LC_MESSAGES/django.po index 69b0e22..8287887 100644 --- a/devapp/locale/ru/LC_MESSAGES/django.po +++ b/devapp/locale/ru/LC_MESSAGES/django.po @@ -648,3 +648,18 @@ msgstr "Посмотреть" msgid "Enter valid JSON" msgstr "Введите данные в формате JSON" + +msgid "Reboot" +msgstr "Перезагрузить" + +msgid "Are you sure you want to reboot that device?" +msgstr "Вы уверены что хотите перезагрузить это устройство?" + +msgid "Signal for reboot has been sent" +msgstr "Сигнал для перезагрузки отправлен" + +msgid "Command returned %s" +msgstr "Комманда вернула %s" + +msgid "Command return nothing" +msgstr "Комманда вернула пустое значение" diff --git a/devapp/templates/devapp/dev.html b/devapp/templates/devapp/dev.html index d7a6588..f3c5bc9 100644 --- a/devapp/templates/devapp/dev.html +++ b/devapp/templates/devapp/dev.html @@ -73,6 +73,9 @@ {% trans 'Register device' %} + + {% trans 'Reboot' %} + diff --git a/devapp/templates/devapp/modal_device_reboot.html b/devapp/templates/devapp/modal_device_reboot.html new file mode 100644 index 0000000..af37dc0 --- /dev/null +++ b/devapp/templates/devapp/modal_device_reboot.html @@ -0,0 +1,17 @@ +{% extends 'base_delete_modal.html' %} +{% load i18n bootstrap3 %} + +{% block modal_form_url %} + {% url 'devapp:reboot' object.pk %} +{% endblock %} + +{% block modal_form_title %} + {% trans 'Reboot' %} +{% endblock %} + +{% block modal_form_text %} +

{% trans 'Are you sure you want to reboot that device?' %}

+ {% bootstrap_form form %} +{% endblock %} + +{% block modal_btn_delete_text %}{% trans 'Reboot' %}{% endblock %} diff --git a/devapp/urls.py b/devapp/urls.py index c3d9daf..8cad054 100644 --- a/devapp/urls.py +++ b/devapp/urls.py @@ -7,6 +7,7 @@ urlpatterns = [ path('', views.GroupsListView.as_view(), name='group_list'), path('devices_without_groups/', views.DevicesWithoutGroupsListView.as_view(), name='devices_null_group'), path('fix_onu/', views.fix_onu, name='fix_onu'), + path('/reboot/', views.RebootDevice.as_view(), name='reboot'), path('/', views.DevicesListView.as_view(), name='devs'), path('/add/', views.DeviceCreateView.as_view(), name='add'), path('//', views.devview, name='view'), diff --git a/devapp/views.py b/devapp/views.py index c78532d..d332fd3 100644 --- a/devapp/views.py +++ b/devapp/views.py @@ -27,10 +27,10 @@ from group_app.models import Group from messenger.tasks import multicast_viber_notify from guardian.decorators import permission_required_or_403 as permission_required from guardian.shortcuts import get_objects_for_user -from devapp.forms import DeviceForm, PortForm, DeviceExtraDataForm +from devapp.forms import DeviceForm, PortForm, DeviceExtraDataForm, DeviceRebootForm from devapp.models import Device, Port, DeviceDBException, DeviceMonitoringException from devapp.tasks import onu_register -from devapp import onu_config +from devapp import expect_scripts class DevicesListView(LoginAdminPermissionMixin, @@ -533,6 +533,31 @@ def zte_port_view_uncfg(request, group_id: str, device_id: str, fiber_id: str): }) +class RebootDevice(LoginAdminPermissionMixin, UpdateView): + permission_required = 'devapp.change_device' + template_name = 'devapp/modal_device_reboot.html' + model = Device + pk_url_kwarg = 'device_id' + form_class = DeviceRebootForm + + def form_valid(self, form): + device = self.object + manager = device.get_manager_object() + is_save = form.cleaned_data.get('is_save') + ret = manager.reboot(save_before_reboot=is_save) + if ret == 0: + messages.success(self.request, _('Signal for reboot has been sent')) + elif ret is None: + messages.warning(self.request, _('Command return nothing')) + else: + messages.error(self.request, _('Command returned %s') % str(ret)) + return redirect( + 'devapp:view', + device.group.pk if device.group is not None else 0, + device.pk + ) + + @login_required @only_admins @permission_required('devapp.can_toggle_ports') @@ -805,16 +830,16 @@ def register_device(request, group_id: int, device_id: int): try: device.register_device() status = 0 - except onu_config.OnuZteRegisterError: + except expect_scripts.OnuZteRegisterError: text = format_msg(gettext('Unregistered onu not found'), 'eye-close') - except onu_config.ZteOltLoginFailed: + except expect_scripts.ZteOltLoginFailed: text = format_msg( gettext('Wrong login or password for telnet access'), 'lock' ) except ( - ConnectionRefusedError, onu_config.ZteOltConsoleError, - onu_config.ExpectValidationError, onu_config.ZTEFiberIsFull + ConnectionRefusedError, expect_scripts.ZteOltConsoleError, + expect_scripts.ExpectValidationError, expect_scripts.ZTEFiberIsFull ) as e: text = format_msg(e, 'exclamation-sign') except DeviceImplementationError as e: