Dmitry Novikov 8 years ago
parent
commit
5f9757b69c
  1. 4
      abonapp/models.py
  2. 26
      abonapp/views.py
  3. 6
      agent/core.py
  4. 69
      agent/mod_mikrotik.py
  5. 32
      agent/structs.py
  6. 15
      djing/lib/__init__.py
  7. 3
      ip_pool/models.py

4
abonapp/models.py

@ -215,8 +215,8 @@ class Abon(BaseAccount):
# make subscriber from agent structure # make subscriber from agent structure
def build_agent_struct(self): def build_agent_struct(self):
abon_addresses = tuple(ip_address(i.ip) for i in self.ip_addresses.filter(is_active=True)) abon_addresses = tuple(ip_address(i.ip) for i in self.ip_addresses.filter(is_active=True))
if not abon_addresses:
return
# if not abon_addresses:
# return
abon_tariff = self.active_tariff() abon_tariff = self.active_tariff()
if abon_tariff is None: if abon_tariff is None:
agent_trf = None agent_trf = None

26
abonapp/views.py

@ -1,3 +1,4 @@
from ipaddress import ip_address
from typing import Dict, Optional from typing import Dict, Optional
from datetime import datetime, date from datetime import datetime, date
from django.contrib.gis.shortcuts import render_to_text from django.contrib.gis.shortcuts import render_to_text
@ -16,7 +17,6 @@ from django.conf import settings
from jsonview.decorators import json_view from jsonview.decorators import json_view
from agent.commands.dhcp import dhcp_commit, dhcp_expiry, dhcp_release from agent.commands.dhcp import dhcp_commit, dhcp_expiry, dhcp_release
from agent.structs import IpStruct
from statistics.models import StatCache from statistics.models import StatCache
from tariff_app.models import Tariff from tariff_app.models import Tariff
from agent import NasFailedResult, Transmitter, NasNetworkError from agent import NasFailedResult, Transmitter, NasNetworkError
@ -1117,16 +1117,20 @@ def user_session_toggle(request, gid, uname, lease_id, action=None):
lease = abon.ip_addresses.get(pk=lease_id) lease = abon.ip_addresses.get(pk=lease_id)
tm = Transmitter() tm = Transmitter()
abon_nas_obj = abon.build_agent_struct() abon_nas_obj = abon.build_agent_struct()
res_text = '#Parameter needed#'
if action == 'free':
lease.free()
tm.lease_free(abon_nas_obj, IpStruct(lease.ip))
res_text = _('Ip lease has been freed')
elif action == 'start':
lease.start()
tm.lease_start(abon_nas_obj, IpStruct(lease.ip))
res_text = _('Ip lease has been started')
messages.success(request, res_text)
try:
if action == 'free':
if abon.ip_addresses.filter(is_active=True).count() > 1:
tm.lease_free(abon_nas_obj, ip_address(lease.ip))
lease.free()
messages.success(request, _('Ip lease has been freed'))
else:
messages.error(request, _('You cannot disable last session'))
elif action == 'start':
tm.lease_start(abon_nas_obj, ip_address(lease.ip))
lease.start()
messages.success(request, _('Ip lease has been started'))
except NasFailedResult as e:
messages.error(request, e)
return redirect('abonapp:abon_home', gid, uname) return redirect('abonapp:abon_home', gid, uname)

6
agent/core.py

@ -1,7 +1,7 @@
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from typing import Iterator, Any, Tuple, Optional from typing import Iterator, Any, Tuple, Optional
from .structs import AbonStruct, TariffStruct, VectorAbon, VectorTariff, IpStruct
from .structs import AbonStruct, TariffStruct, VectorAbon, VectorTariff
# Raised if NAS has returned failed result # Raised if NAS has returned failed result
@ -78,14 +78,14 @@ class BaseTransmitter(ABC):
pass pass
@abstractmethod @abstractmethod
def lease_free(self, user: AbonStruct, lease: IpStruct):
def lease_free(self, user: AbonStruct, lease):
""" """
Remove ip lease from allowed to network Remove ip lease from allowed to network
:return: :return:
""" """
@abstractmethod @abstractmethod
def lease_start(self, user: AbonStruct, lease: IpStruct):
def lease_start(self, user: AbonStruct, lease):
""" """
Starts ip lease to allowed to network Starts ip lease to allowed to network
:return: :return:

69
agent/mod_mikrotik.py

@ -3,13 +3,14 @@ import socket
import binascii import binascii
from abc import ABCMeta from abc import ABCMeta
from hashlib import md5 from hashlib import md5
from ipaddress import ip_network, _BaseAddress
from ipaddress import _BaseAddress, ip_address
from typing import Iterable, Optional, Tuple, Generator, Dict from typing import Iterable, Optional, Tuple, Generator, Dict
from django.conf import settings from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from djing.lib.decorators import LazyInitMetaclass from djing.lib.decorators import LazyInitMetaclass
from .structs import TariffStruct, AbonStruct, IpStruct, VectorAbon, VectorTariff
from .structs import TariffStruct, AbonStruct, VectorAbon, VectorTariff
from . import settings as local_settings from . import settings as local_settings
from djing import ping from djing import ping
from agent.core import BaseTransmitter, NasNetworkError, NasFailedResult from agent.core import BaseTransmitter, NasNetworkError, NasFailedResult
@ -161,18 +162,11 @@ class ApiRos(object):
return ret return ret
def __del__(self): def __del__(self):
if hasattr(self, 'sk'):
sk = getattr(self, 'sk')
if sk is not None:
self.sk.close() self.sk.close()
class IpAddressListObj(IpStruct):
__slots__ = ('__ip', 'mk_id')
def __init__(self, ip, mk_id):
super(IpAddressListObj, self).__init__(ip)
self.mk_id = str(mk_id).replace('*', '')
class MikrotikTransmitter(BaseTransmitter, ApiRos, metaclass=type('_ABC_Lazy_mcs', (ABCMeta, LazyInitMetaclass), {})): class MikrotikTransmitter(BaseTransmitter, ApiRos, metaclass=type('_ABC_Lazy_mcs', (ABCMeta, LazyInitMetaclass), {})):
def __init__(self, login=None, password=None, ip=None, port=None): def __init__(self, login=None, password=None, ip=None, port=None):
@ -246,17 +240,18 @@ class MikrotikTransmitter(BaseTransmitter, ApiRos, metaclass=type('_ABC_Lazy_mcs
if disabled is not None: if disabled is not None:
disabled = True if disabled == 'true' else False disabled = True if disabled == 'true' else False
if target is not None and name is not None: if target is not None and name is not None:
target_ip, target_net = target.split('/')
# target may be '192.168.0.3/32,192.168.0.2/32'
ips = (ip.split('/')[0] for ip in target.split(','))
a = AbonStruct( a = AbonStruct(
uid=int(name[3:]), uid=int(name[3:]),
ip=target_ip,
ips=ips,
tariff=t, tariff=t,
is_active=disabled or False
is_access=disabled or False
) )
a.queue_id = info.get('=.id') a.queue_id = info.get('=.id')
return a return a
except ValueError:
pass
except ValueError as e:
print('ValueError:', e)
################################################# #################################################
# QUEUES # QUEUES
@ -266,18 +261,19 @@ class MikrotikTransmitter(BaseTransmitter, ApiRos, metaclass=type('_ABC_Lazy_mcs
def find_queue(self, name: str) -> Optional[AbonStruct]: def find_queue(self, name: str) -> Optional[AbonStruct]:
r = self._exec_cmd(('/queue/simple/print', '?name=%s' % name)) r = self._exec_cmd(('/queue/simple/print', '?name=%s' % name))
if r: if r:
return self._build_shape_obj(r)
return self._build_shape_obj(r.get('!re'))
def add_queue(self, user: AbonStruct) -> None: def add_queue(self, user: AbonStruct) -> None:
if not isinstance(user, AbonStruct): if not isinstance(user, AbonStruct):
raise TypeError raise TypeError
if user.tariff is None or not isinstance(user.tariff, TariffStruct): if user.tariff is None or not isinstance(user.tariff, TariffStruct):
return return
ips = ','.join(str(i) for i in user.ips)
self._exec_cmd(( self._exec_cmd((
'/queue/simple/add', '/queue/simple/add',
'=name=uid%d' % user.uid, '=name=uid%d' % user.uid,
# FIXME: тут в разных микротиках или =target-addresses или =target # FIXME: тут в разных микротиках или =target-addresses или =target
'=target=%s' % ','.join(str(i) for i in user.ips),
'=target=%s' % ips,
'=max-limit=%.3fM/%.3fM' % (user.tariff.speedOut, user.tariff.speedIn), '=max-limit=%.3fM/%.3fM' % (user.tariff.speedOut, user.tariff.speedIn),
'=queue=MikroBILL_SFQ/MikroBILL_SFQ', '=queue=MikroBILL_SFQ/MikroBILL_SFQ',
'=burst-time=1/1' '=burst-time=1/1'
@ -314,7 +310,7 @@ class MikrotikTransmitter(BaseTransmitter, ApiRos, metaclass=type('_ABC_Lazy_mcs
'/queue/simple/set', '/queue/simple/set',
'=name=uid%d' % user.uid, '=name=uid%d' % user.uid,
'=max-limit=%.3fM/%.3fM' % (user.tariff.speedOut, user.tariff.speedIn), '=max-limit=%.3fM/%.3fM' % (user.tariff.speedOut, user.tariff.speedIn),
# FIXME: тут в разных микротиках или =target-addresses или =target
# FIXME: тут в разных версиях прошивки микротика или =target-addresses или =target
'=target=%s' % ','.join(str(i) for i in user.ips), '=target=%s' % ','.join(str(i) for i in user.ips),
'=queue=MikroBILL_SFQ/MikroBILL_SFQ', '=queue=MikroBILL_SFQ/MikroBILL_SFQ',
'=burst-time=1/1' '=burst-time=1/1'
@ -334,8 +330,8 @@ class MikrotikTransmitter(BaseTransmitter, ApiRos, metaclass=type('_ABC_Lazy_mcs
# Ip->firewall->address list # Ip->firewall->address list
################################################# #################################################
def add_ip(self, list_name: str, ip: IpStruct):
if not isinstance(ip, IpStruct):
def add_ip(self, list_name: str, ip):
if not issubclass(ip.__class__, _BaseAddress):
raise TypeError raise TypeError
commands = ( commands = (
'/ip/firewall/address-list/add', '/ip/firewall/address-list/add',
@ -373,7 +369,7 @@ class MikrotikTransmitter(BaseTransmitter, ApiRos, metaclass=type('_ABC_Lazy_mcs
'?dynamic=no' '?dynamic=no'
)) ))
for dat in ips: for dat in ips:
yield IpAddressListObj(dat.get('=address'), dat.get('=.id'))
yield ip_address(dat.get('=address')), dat.get('=.id')
################################################# #################################################
# BaseTransmitter implementation # BaseTransmitter implementation
@ -402,7 +398,7 @@ class MikrotikTransmitter(BaseTransmitter, ApiRos, metaclass=type('_ABC_Lazy_mcs
raise TypeError raise TypeError
self.add_queue(user) self.add_queue(user)
for ip in user.ips: for ip in user.ips:
if not isinstance(ip, IpStruct):
if not issubclass(ip.__class__, _BaseAddress):
raise TypeError raise TypeError
self.add_ip(LIST_USERS_ALLOWED, ip) self.add_ip(LIST_USERS_ALLOWED, ip)
@ -444,7 +440,7 @@ class MikrotikTransmitter(BaseTransmitter, ApiRos, metaclass=type('_ABC_Lazy_mcs
self.add_queue(user) self.add_queue(user)
return return
if queue != user: if queue != user:
self.update_queue(user)
self.update_queue(user, queue)
def ping(self, host, count=10) -> Optional[Tuple[int, int]]: def ping(self, host, count=10) -> Optional[Tuple[int, int]]:
r = self._exec_cmd(( r = self._exec_cmd((
@ -503,20 +499,25 @@ class MikrotikTransmitter(BaseTransmitter, ApiRos, metaclass=type('_ABC_Lazy_mcs
# self.remove_ip_range(diff) # self.remove_ip_range(diff)
return queues return queues
def lease_free(self, user: AbonStruct, lease: IpStruct):
ip = self.find_ip(lease, LIST_USERS_ALLOWED)
if ip is not None:
self.remove_ip(ip.get('=.id'))
def lease_free(self, user: AbonStruct, lease):
queue = self.find_queue('uid%d' % user.uid) queue = self.find_queue('uid%d' % user.uid)
if queue is not None:
user.ips = tuple(i for i in user.ips if i != lease)
self.update_queue(user, queue)
if len(queue.ips) > 1:
if queue is not None:
user.ips = tuple(i for i in user.ips if i != lease)
self.update_queue(user, queue)
ip = self.find_ip(lease, LIST_USERS_ALLOWED)
if ip is not None:
self.remove_ip(ip.get('=.id'))
else:
raise NasFailedResult(_('You cannot disable last session'))
def lease_start(self, user: AbonStruct, lease: IpStruct):
def lease_start(self, user: AbonStruct, lease):
ip = self.find_ip(lease, LIST_USERS_ALLOWED) ip = self.find_ip(lease, LIST_USERS_ALLOWED)
if ip is None: if ip is None:
self.add_ip(LIST_USERS_ALLOWED, lease) self.add_ip(LIST_USERS_ALLOWED, lease)
queue = self.find_queue('uid%d' % user.uid) queue = self.find_queue('uid%d' % user.uid)
if queue is not None:
user.ips += lease,
user.ips += lease,
if queue is None:
self.add_queue(user)
else:
self.update_queue(user, queue) self.update_queue(user, queue)

32
agent/structs.py

@ -1,40 +1,12 @@
# -*- coding: utf-8 -*-
from abc import ABCMeta from abc import ABCMeta
from ipaddress import ip_address
from typing import Iterable from typing import Iterable
from djing.lib import int2ip, ip2int
class BaseStruct(object, metaclass=ABCMeta): class BaseStruct(object, metaclass=ABCMeta):
__slots__ = () __slots__ = ()
class IpStruct(BaseStruct):
__slots__ = ('__ip',)
def __init__(self, ip):
if type(ip) is int:
self.__ip = ip
else:
self.__ip = ip2int(str(ip))
def get_int(self):
return self.__ip
def __eq__(self, other):
if not isinstance(other, IpStruct):
raise TypeError('Instance must be IpStruct')
return self.__ip == other.__ip
def __int__(self):
return self.__ip
def __str__(self):
return int2ip(self.__ip)
def __hash__(self):
return hash(self.__ip)
# Как обслуживается абонент # Как обслуживается абонент
class TariffStruct(BaseStruct): class TariffStruct(BaseStruct):
__slots__ = ('tid', 'speedIn', 'speedOut') __slots__ = ('tid', 'speedIn', 'speedOut')
@ -71,7 +43,7 @@ class AbonStruct(BaseStruct):
if ips is None: if ips is None:
self.ips = () self.ips = ()
else: else:
self.ips = tuple(IpStruct(ip) for ip in ips)
self.ips = tuple(ip_address(ip) for ip in ips)
self.tariff = tariff self.tariff = tariff
self.is_access = is_access self.is_access = is_access
self.queue_id = 0 self.queue_id = 0

15
djing/lib/__init__.py

@ -1,25 +1,10 @@
import socket import socket
import struct
from functools import wraps from functools import wraps
from hashlib import sha256 from hashlib import sha256
from datetime import timedelta from datetime import timedelta
from collections import Iterator from collections import Iterator
def ip2int(addr):
try:
return struct.unpack("!I", socket.inet_aton(addr))[0]
except:
return 0
def int2ip(addr):
try:
return socket.inet_ntoa(struct.pack("!I", addr))
except:
return ''
def safe_float(fl): def safe_float(fl):
try: try:
return 0.0 if fl is None or fl == '' else float(fl) return 0.0 if fl is None or fl == '' else float(fl)

3
ip_pool/models.py

@ -10,6 +10,7 @@ from django.db import models
from django.utils.timezone import now from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from djing.fields import MACAddressField
from djing.lib import DuplicateEntry from djing.lib import DuplicateEntry
from ip_pool.fields import GenericIpAddressWithPrefix from ip_pool.fields import GenericIpAddressWithPrefix
from group_app.models import Group from group_app.models import Group
@ -162,9 +163,11 @@ class IpLeaseManager(models.Manager):
class IpLeaseModel(models.Model): class IpLeaseModel(models.Model):
ip = models.GenericIPAddressField(verbose_name=_('Ip address'), unique=True) ip = models.GenericIPAddressField(verbose_name=_('Ip address'), unique=True)
network = models.ForeignKey(NetworkModel, on_delete=models.CASCADE, verbose_name=_('Parent network')) network = models.ForeignKey(NetworkModel, on_delete=models.CASCADE, verbose_name=_('Parent network'))
mac_addr = MACAddressField(verbose_name=_('Mac address'), null=True, blank=True, unique=True)
lease_time = models.DateTimeField(_('Lease time'), auto_now_add=True) lease_time = models.DateTimeField(_('Lease time'), auto_now_add=True)
is_dynamic = models.BooleanField(_('Is dynamic'), default=False) is_dynamic = models.BooleanField(_('Is dynamic'), default=False)
is_active = models.BooleanField(_('Is active'), default=True) is_active = models.BooleanField(_('Is active'), default=True)
device_info = models.CharField(null=True, blank=True, default=None, max_length=128)
objects = IpLeaseManager() objects = IpLeaseManager()

Loading…
Cancel
Save