@ -8,7 +8,7 @@
## Добавление поддерживаемого устройства (Свича)
## Добавление поддерживаемого устройства (Свича)
Для того чтоб добавить новый тип устройства с которым потом сможет работать биллинг нужно открыть файл *devapp/dev_types.py*
Для того чтобы добавить новый тип устройства с которым потом сможет работать биллинг, нужно открыть файл *devapp/dev_types.py*
и переопределить 2 интерфейса. Первый это *BasePort* для порта свича, а второй *DevBase* для самого свича соответственно.
и переопределить 2 интерфейса. Первый это *BasePort* для порта свича, а второй *DevBase* для самого свича соответственно.
Разберём этот процесс на примере готовой реализации для Eltex.
Разберём этот процесс на примере готовой реализации для Eltex.
@ -36,12 +36,13 @@ class EltexPort(BasePort):
1
1
)
)
```
```
Тут в инициилизации мы передаём все базовые параметры базовому конструктору, и дополнительный аргумент snmpWorker
для работы по SNMP. *snmpWorker* это объект реализованного интерфейса SNMPBaseWorker, далее я опишу где мы его реализуем.
Тут в иницииа лизации мы передаём все базовые параметры базовому конструктору, и дополнительный аргумент * snmpWorker*
для работы по SNMP. *snmpWorker* это объект реализованного интерфейса * SNMPBaseWorker* , далее я опишу где мы его реализуем.
Для порта надо переопределить 2 метода: *disable* и *enable* понятно для чего, чтоб включать и отключать порт.
Для порта надо переопределить 2 метода: *disable* и *enable* понятно для чего, чтоб включать и отключать порт.
Шаблон реализации можно даже не менять, просто укажите вместо строки .1.3.6.1.2.1.2.2.1.7 нужный SNMP OID для включения порта.
Шаблон реализации можно даже не менять, просто укажите вместо строки .1.3.6.1.2.1.2.2.1.7 нужный SNMP OID для включения порта.
К этой строке будет добавляться номер порта который нужно включить.
К этой строке будет добавляться номер порта который нужно включить.
Или, все же, переопределите этот метод если вы хотите, например, реализовать включение/выключение по *telnet* или *ssh* .
Для отключения так-же по аналогии.
Для отключения так-же по аналогии.
Теперь реализация для свича:
Теперь реализация для свича:
@ -85,31 +86,32 @@ class EltexSwitch(DLinkDevice):
"""
"""
```
```
Свойство **@description** Просто отображает человекопонятное название вашего устройства в биллинге.
Свойство **@description** Просто отображает человекопонятное название вашего устройства в биллинге.
Заметьте что строка на английском и заключена в процедуру **_** (это ugettext_lazy, см. в импорте вверху файла),
Заметьте, что строка на английском и заключена в процедуру **_** (это ugettext_lazy, см. в импорте вверху файла),
это локализация для текущего языка. Про локализацию можно почитать в соответствующем разделе документации *Django*
это локализация для текущего языка. Про локализацию можно почитать в соответствующем разделе документации *Django*
[django translation ](https://docs.djangoproject.com/en/1.1 1/topics/i18n/translation/ ).
[django translation ](https://docs.djangoproject.com/en/2. 1/topics/i18n/translation/ ).
Метод **@get_ports** чаще всего редко изменяется по алгоритму, так что вам, в большенстве случаев, достаточно добавить
Метод **@get_ports** чаще всего редко изменяется по алгоритму, так что вам, в большенстве случаев, достаточно добавить
нужные SNMP OID в соответствующие места процедуры. Но вы вольны реализовать ваш метод получения портов
нужные SNMP OID в соответствующие места процедуры. Но вы вольны реализовать ваш метод получения портов
как вам угодно, главное чтоб возвращался список объектов определённого выше класса порта для этого свича.
как вам угодно, главное чтоб возвращался список объектов определённого выше класса порта для этого свича.
В данном случае возвращается список объектов *EltexPort* . На самом деле не обязательно список , можете вернуть кортеж
или генератор, что-то итери уемое.
В данном случае возвращается генератор объектов *EltexPort* . На самом деле не обязательно генератор , можете вернуть кортеж
или список, что-то итерир уемое.
Метод **@get_device_name** получает по SNMP имя устройства, просто укажите в вашей реализации нужный OID.
Метод **@get_device_name** получает по SNMP имя устройства, просто укажите в вашей реализации нужный OID.
Метод **@uptime** , понятно что возвращает, укажите нужный OID. Вернётся тип *RuTimedelta* , это переопределённый тип **timedelta** , я его реализовал
для локализации временного промежутка на русский.
Метод **@uptime** , понятно что возвращает, укажите нужный OID. Вернётся тип *RuTimedelta* , это переопределённый тип
**timedelta**, я его реализовал для локализации временного промежутка на русский.
Свойство **@has_attachable_to_subscriber** возвращает правду если это устройство можно привязать к абоненту.
Свойство **@has_attachable_to_subscriber** возвращает правду если это устройство можно привязать к абоненту.
Например у Dlink стоит True потому что Dlink стоит во многих местах на доступе, и его порты принадлежат
Например у Dlink стоит True потому что Dlink стоит во многих местах на доступе, и его порты принадлежат
абонентам при авторизации.
абонентам при авторизации.
Свойство **@is_use_device_port** используется в DHCP чтоб понять что мы используем для привязки к абоненту всё устройство или
только порт устройства. Например, если у устройства только 1 порт абонента (PON ONU), и мы привязываем этого абонента ко всему устройству
а не к порту, то нужно указать False, На обычных свичах где мы авторизуем абонента на порту возвращаем True.
Свойство **@is_use_device_port** используется в **DHCP** чтоб понять что мы используем для привязки к абоненту всё
устройство или только порт устройства. Например, если у устройства только 1 порт абонента (PON ONU), и мы привязываем
этого абонента ко всему устройству а не к порту, то нужно указать False, На обычных свичах где мы авторизуем абонента
на порту возвращаем True.
Реализация SNMPBaseWorker по сути не нужна, класс абстрактных методов не имеет.
Потому когда наследуемся от *DevBase* то в базовые классы добавим и SNMPBaseWorker, как это сделано в *DLinkDevice* :
Реализация ** SNMPBaseWorker** по сути не нужна, класс абстрактных методов не имеет.
Потому, когда наследуемся от *DevBase* то в базовые классы добавим и SNMPBaseWorker, как это сделано в *DLinkDevice* :
```python
```python
class DLinkDevice(DevBase, SNMPBaseWorker):
class DLinkDevice(DevBase, SNMPBaseWorker):
@ -122,11 +124,8 @@ class DLinkDevice(DevBase, SNMPBaseWorker):
Вы, наверное, обратили внимание, что *EltexSwitch* наследован от *DLinkDevice* , это потому что некоторые методы идентичны,
Вы, наверное, обратили внимание, что *EltexSwitch* наследован от *DLinkDevice* , это потому что некоторые методы идентичны,
и реализация для обоих свичей похожа.
и реализация для обоих свичей похожа.
>П.С. Не изучайте как пример реализацию для PON, она, как по мне, костыльна. Это связано с тем что PON сильно отличается от
>принципа работы обычного свича, и чтоб подружить свичи и PON был реализован такой костыль.
## Реализация своего NAS
## Реализация своего NAS
Сейчас биллинг работает с Mikrotik в роли устройства для доступа абонентов в интернет.
Сейчас биллинг работает с несколькими Mikrotik в роли устройства для доступа абонентов в интернет.
Как можно реализовать такой-же для вашего роутера, например на GNU/Linux.
Как можно реализовать такой-же для вашего роутера, например на GNU/Linux.
Создадим файл *gw_app/nas_managers/mod_linux.py* и реализуем потомка для интерфейса *BaseTransmitter* .
Создадим файл *gw_app/nas_managers/mod_linux.py* и реализуем потомка для интерфейса *BaseTransmitter* .
@ -209,23 +208,22 @@ class LinuxTransmitter(BaseTransmitter):
```
```
Для того чтоб биллинг знал о вашем классе надо указать его в *gw_app/nas_managers/\_\_init\_\_.py*.
Для того чтоб биллинг знал о вашем классе надо указать его в *gw_app/nas_managers/\_\_init\_\_.py*.
Замените
>from .mod_mikrotik import MikrotikTransmitter
На это
>from .mod_mikrotik import LinuxTransmitter
И укажите ваш класс
> Transmitter = MikrotikTransmitter
Добавьте в кортеж *NAS_TYPES* ещё один кортеж из двух элементов, в котором первый будет код реализации в БД,
максимум 4 символа. А второй будет классом вашей реализации.
Получится примерно такое содержимое:
Получится примерно такое содержимое:
```python
```python
from .mod_mikrotik import LinuxTransmitter
from .core import NasFailedResult, NasNetworkError
from .structs import TariffStruct, AbonStruct
Transmitter = LinuxTransmitter
from gw_app.nas_managers.mod_mikrotik import MikrotikTransmitter
from gw_app.nas_managers.mod_linux import LinuxTransmitter
from gw_app.nas_managers.core import NasNetworkError, NasFailedResult
from gw_app.nas_managers.structs import SubnetQueue
# Указываем какие реализации шлюзов у нас есть, это будет использоваться в
# web интерфейсе
NAS_TYPES = (
('mktk', MikrotikTransmitter),
('linx', NasNetworkError),
)
```
```
Для примера, как вы наверное уже догадались, можно посмотреть реализацию для Mikrotik в файле *gw_app/nas_managers/mod_mikrotik.py*
Для примера, как вы наверное уже догадались, можно посмотреть реализацию для Mikrotik в файле *gw_app/nas_managers/mod_mikrotik.py*
@ -237,11 +235,7 @@ NasNetworkError, как понятно из названия, вызываетс
Кстати, не все методы обязательно реализовывать, некоторые из них зарезервированы на будущие цели, в комментариях к их
Кстати, не все методы обязательно реализовывать, некоторые из них зарезервированы на будущие цели, в комментариях к их
прототипам в интерфейсе *BaseTransmitter* это сказано. Поэтому просто переопределите эти зарезервированные методы как
прототипам в интерфейсе *BaseTransmitter* это сказано. Поэтому просто переопределите эти зарезервированные методы как
пустые, например метод *add_tariff_range* нигде в биллинге пока не вызывается. так что можно определить его пустым.
```python
def add_tariff_range(self, tariff_list):
pass
```
пустые, я имею ввиду *pass* в реализации.
## Отправляем оповещения
## Отправляем оповещения
@ -264,57 +258,14 @@ send_notify(msg_text='Text message',account=employee_profile, tag='apptag')
с помощью тега *msgapp* , и вы не спутаете ваши сообщения с сообщениями из модуля, например, задач который использует тэг *taskap* .
с помощью тега *msgapp* , и вы не спутаете ваши сообщения с сообщениями из модуля, например, задач который использует тэг *taskap* .
#### Получение оповещения
```python
from chatbot.models import MessageQueue
msg = MessageQueue.objects.pop(user=employee_profile, tag='apptag')
```
Метод **pop** плучает первое сообщение и удаляет его.
#### Отображаем оповещение в браузере
Чтоб отобразить **WebNotification** добавте в файле скриптов *static/js/my.js* строку вида:
```javascript
$(document).notifys({news_url: '/url/to/your_view', check_interval: 60});
```
Тут *news_url* это путь к вашему представления которое возвращает новые оповещения, а *check_interval* - это интервал обращения к вашему представлению.
Представление для Web оповещений выглядит примерно так:
```python
@login_required
@only_admins
def check_news(request):
msg = MessageQueue.objects.pop(user=request.user, tag='apptag')
if msg is not None:
r = {
'exist': True,
'content': msg,
'title': 'Message title'
}
else:
r = {'exist': False}
return HttpResponse(dumps(r))
```
Убедитесь что вашему представлению не будет доступа от абонентов, об этом позаботится декоратор *only_admins* из *djing.lib* .
После получения сообщения надо вернуть словарь с параметрами:
*exist* - Логическое значение, обозначает есть или нет информации в ответе. Если *exist* == True тогда возвращае ещё *content* и *title* .
*content* - Соответственно содержимое оповещения.
*title* - Заголовок оповещения.
### Свой сервис для API
### Свой сервис для API
Сервисы общаются с биллингом через http запросы и могут быть самыми разными, но все они должны уметь одинаково
Сервисы общаются с биллингом через http запросы и могут быть самыми разными, но все они должны уметь одинаково
расчитывать хеш сумму для проведения транзакци, иначе web сервер биллинга просто вернёт 403.
расчитывать хеш сумму для проведения транзакци, иначе web сервер биллинга просто вернёт 403. Код расчёта хеш
суммы находится в *djing/lib/__init__.py* в функции *check_sign* и *calc_hash*, они рядом, там увидите. И
используются они декоратором *hash_auth_view* в *djing/lib/decorators.py* и примесью(Mixin) *HashAuthView* в
файле *djing/global_base_views.py*.
Смысл в том, чтобы
### Дополнительная инфа в устройствах
### Дополнительная инфа в устройствах