Browse Source

Merge branch 'devel' of https://github.com/bashmak/djing into devel

devel
Dmitry 9 years ago
parent
commit
c31ba48193
  1. 58
      abonapp/__init__.py
  2. 1
      abonapp/admin.py
  3. 108
      abonapp/fields.py
  4. 31
      abonapp/formfields.py
  5. 2
      abonapp/forms.py
  6. 42
      abonapp/migrations/0014_auto_20170330_1452.py
  7. 30
      abonapp/models.py
  8. 5
      abonapp/templates/abonapp/addAbon.html
  9. 2
      abonapp/templates/abonapp/editAbon.html
  10. 60
      abonapp/views.py
  11. 4
      agent/core.py
  12. 31
      agent/mod_mikrotik.py
  13. 92
      dhcp_lever.py
  14. 2
      mydefs.py
  15. 2
      requirements.txt
  16. 2
      taskapp/locale/ru/LC_MESSAGES/django.po

58
abonapp/__init__.py

@ -1 +1,59 @@
from django.conf import settings
from netaddr import mac_unix, mac_eui48
import importlib
import warnings
class mac_linux(mac_unix):
"""MAC format with zero-padded all upper-case hex and colon separated"""
word_fmt = '%.2X'
def default_dialect(eui_obj=None):
# Check to see if a default dialect class has been specified in settings,
# using 'module.dialect_cls' string and use importlib and getattr to retrieve dialect class. 'module' is the module and
# 'dialect_cls' is the class name of the custom dialect. The dialect must either be defined or imported by the module's
# __init__.py if the module is a package.
from .fields import MACAddressField # Remove import at v1.4
if hasattr(settings, 'MACADDRESS_DEFAULT_DIALECT') and not MACAddressField.dialect:
module, dialect_cls = settings.MACADDRESS_DEFAULT_DIALECT.split('.')
dialect = getattr(importlib.import_module(module), dialect_cls, mac_linux)
return dialect
else:
if MACAddressField.dialect: # Remove this "if" statement at v1.4
warnings.warn(
"The set_dialect class method on MACAddressField has been deprecated, in favor of the default_dialect "
"utility function and settings.MACADDRESS_DEFAULT_DIALECT. See macaddress.__init__.py source or the "
"project README for more information.",
DeprecationWarning,
)
return MACAddressField.dialect
if eui_obj:
return eui_obj.dialect
else:
return mac_linux
def format_mac(eui_obj, dialect):
# Format a EUI instance as a string using the supplied dialect class, allowing custom string classes by
# passing directly or as a string, a la 'module.dialect_cls', where 'module' is the module and 'dialect_cls'
# is the class name of the custom dialect. The dialect must either be defined or imported by the module's __init__.py if
# the module is a package.
if not isinstance(dialect, mac_eui48):
if isinstance(dialect, str):
module, dialect_cls = dialect.split('.')
dialect = getattr(importlib.import_module(module), dialect_cls)
eui_obj.dialect = dialect
return str(eui_obj)
from pkg_resources import get_distribution, DistributionNotFound
import os.path
try:
_dist = get_distribution('django-macaddress')
except DistributionNotFound:
__version__ = 'Please install this project with setup.py'
else:
__version__ = _dist.version
VERSION = __version__ # synonym
default_app_config = 'abonapp.apps.AbonappConfig'

1
abonapp/admin.py

@ -13,3 +13,4 @@ admin.site.register(models.AllTimePayLog)
admin.site.register(models.AbonRawPassword)
admin.site.register(models.ExtraFieldsModel)
admin.site.register(models.AllPayLog)
admin.site.register(models.Opt82)

108
abonapp/fields.py

@ -0,0 +1,108 @@
#
# I got it on https://github.com/django-macaddress/django-macaddress
#
from django.core.exceptions import ValidationError
from django.db import models
from netaddr import EUI, AddrFormatError
from .formfields import MACAddressField as MACAddressFormField
from . import default_dialect
import warnings
class MACAddressField(models.Field):
description = "A MAC address validated by netaddr.EUI"
empty_strings_allowed = False
dialect = None
def __init__(self, *args, **kwargs):
self.integer = kwargs.pop('integer', True)
if not self.integer: # If storing MAC address as string, set max_length to default (17) or use supplied kwarg value.
kwargs['max_length'] = kwargs.get('max_length', 17)
super(MACAddressField, self).__init__(*args, **kwargs)
def deconstruct(self):
''' Django 1.7 migrations require this method
https://docs.djangoproject.com/en/dev/howto/custom-model-fields/#field-deconstruction
'''
name, path, args, kwargs = super(MACAddressField, self).deconstruct()
kwargs['integer'] = self.integer
return name, path, args, kwargs
@classmethod
def set_dialect(cls, new_dialect_clazz):
''' Setting dialect for EUI (MAC addresses) globally to this Field
class.
Class new_dialect_clazz should (finally) extend
netaddr.strategy.eui48.mac_eui48.
'''
warnings.warn(
"The set_dialect method has been deprecated, in favor of the default_dialect utility function and "
" settings.MACADDRESS_DEFAULT_DIALECT. See macaddress.__init__.py source or the project README for "
"more information.",
DeprecationWarning,
)
cls.dialect = new_dialect_clazz
def get_prep_value(self, value):
if value is None:
return None
if not isinstance(value, EUI):
value = self.to_python(value)
if self.integer:
return int(value)
return str(value)
value.dialect = default_dialect(self)
if self.integer:
return int(value)
return str(value)
def get_internal_type(self):
if self.integer:
return 'BigIntegerField'
return 'CharField'
def from_db_value(self, value, expression, connection, context):
return self.to_python(value)
def to_python(self, value):
if value is None:
return value
if isinstance(value, EUI):
value.dialect = default_dialect(value)
return value
try:
return EUI(value, version=48, dialect=default_dialect())
except (TypeError, ValueError, AddrFormatError):
raise ValidationError(
"This value must be a valid MAC address.")
def formfield(self, **kwargs):
defaults = {'form_class': MACAddressFormField}
defaults.update(kwargs)
return super(MACAddressField, self).formfield(**defaults)
def get_prep_lookup(self, lookup_type, value):
# data is stored internally as integer so searching as string
# yeild 0 result. for example: useful for search in admin.
if lookup_type in ('exact', 'iexact', 'icontains', 'icontains'):
try:
return self.get_prep_value(value)
except AddrFormatError:
return None
elif lookup_type in ('in'):
try:
macs = []
for mac in value:
macs += [self.get_prep_value(mac)]
return macs
except AddrFormatError:
return None
else:
raise TypeError('Lookup type %r not supported.' % lookup_type)
try:
from south.modelsinspector import add_introspection_rules
add_introspection_rules([], ["^macaddress\.fields\.MACAddressField"])
except ImportError:
pass

31
abonapp/formfields.py

@ -0,0 +1,31 @@
from django.forms import Field
from django.forms.fields import EMPTY_VALUES
#"From Django 1.8: The django.forms.util module has been renamed. Use django.forms.utils instead."
try:
from django.forms.utils import ValidationError
except ImportError:
from django.forms.util import ValidationError
from netaddr import EUI, AddrFormatError
class MACAddressField(Field):
default_error_messages = {
'invalid': 'Enter a valid MAC Address.',
}
def clean(self, value):
"""
Validates that EUI() can be called on the input. Returns the result
of EUI(). Returns None for empty values.
"""
value = super(MACAddressField, self).clean(value)
if value in EMPTY_VALUES:
return None
try:
value = EUI(str(value), version=48)
except (ValueError, TypeError, AddrFormatError):
raise ValidationError(self.error_messages['invalid'])
return value

2
abonapp/forms.py

@ -32,7 +32,7 @@ class AbonForm(forms.ModelForm):
}))
password = forms.CharField(max_length=64, initial=generate_random_password,
widget=forms.TextInput(attrs={'class': 'form-control', 'type': 'password'}))
widget=forms.TextInput(attrs={'class': 'form-control', 'type': 'password', 'autocomplete': 'new-password'}))
class Meta:
model = models.Abon

42
abonapp/migrations/0014_auto_20170330_1452.py

@ -0,0 +1,42 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2017-03-30 11:52
from __future__ import unicode_literals
import abonapp.fields
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('abonapp', '0013_abongroup_tariffs'),
]
operations = [
migrations.CreateModel(
name='Opt82',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('mac', abonapp.fields.MACAddressField(integer=True)),
('port', models.PositiveSmallIntegerField(default=0)),
],
options={
'db_table': 'opt_82',
},
),
migrations.AlterField(
model_name='abon',
name='extra_fields',
field=models.ManyToManyField(blank=True, to='abonapp.ExtraFieldsModel'),
),
migrations.AlterUniqueTogether(
name='opt82',
unique_together=set([('mac', 'port')]),
),
migrations.AddField(
model_name='abon',
name='opt82',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='abonapp.Opt82'),
),
]

30
abonapp/models.py

@ -9,6 +9,7 @@ from agent import Transmitter, AbonStruct, TariffStruct, NasFailedResult
from ip_pool.models import IpPoolItem
from tariff_app.models import Tariff
from accounts_app.models import UserProfile
from .fields import MACAddressField
class LogicError(Exception):
@ -187,6 +188,18 @@ class ExtraFieldsModel(models.Model):
db_table = 'abon_extra_fields'
class Opt82(models.Model):
mac = MACAddressField()
port = models.PositiveSmallIntegerField(default=0)
def __str__(self):
return "%s-%d" % (self.mac, self.port)
class Meta:
db_table = 'opt_82'
unique_together = (('mac', 'port'),)
class Abon(UserProfile):
current_tariffs = models.ManyToManyField(Tariff, through=AbonTariff)
group = models.ForeignKey(AbonGroup, models.SET_NULL, blank=True, null=True)
@ -195,7 +208,8 @@ class Abon(UserProfile):
description = models.TextField(null=True, blank=True)
street = models.ForeignKey(AbonStreet, on_delete=models.SET_NULL, null=True, blank=True)
house = models.CharField(max_length=12, null=True, blank=True)
extra_fields = models.ManyToManyField(ExtraFieldsModel)
extra_fields = models.ManyToManyField(ExtraFieldsModel, blank=True)
opt82 = models.ForeignKey(Opt82, null=True, blank=True)
_act_tar_cache = None
@ -298,7 +312,8 @@ class Abon(UserProfile):
# есть-ли доступ у абонента к услуге, смотрим в tariff_app.custom_tariffs.<TariffBase>.manage_access()
def is_access(self):
ats = AbonTariff.objects.filter(abon=self).exclude(time_start=None)
if not ats or ats.count() < 1: return False
if not ats or ats.count() < 1:
return False
trf = ats[0].tariff
ct = trf.get_calc_type()(ats[0])
if ct.manage_access(self):
@ -385,26 +400,25 @@ class AbonRawPassword(models.Model):
def abon_post_save(sender, instance, **kwargs):
#try:
timeout = None
if hasattr(instance, 'opt82'):
timeout = 3600
tm = Transmitter()
agent_abon = instance.build_agent_struct()
if agent_abon is None:
return True
if kwargs['created']:
# создаём абонента
tm.add_user(agent_abon)
tm.add_user(agent_abon, ip_timeout=timeout)
else:
# обновляем абонента на NAS
tm.update_user(agent_abon)
tm.update_user(agent_abon, ip_timeout=timeout)
# если не активен то приостановим услугу
if instance.is_active:
tm.start_user(agent_abon)
else:
tm.pause_user(agent_abon)
#except NasFailedResult:
# return True
def abon_del_signal(sender, instance, **kwargs):
try:

5
abonapp/templates/abonapp/addAbon.html

@ -78,6 +78,11 @@
<div class="input-group">
<span class="input-group-addon"><span class="glyphicon glyphicon-lock"></span></span>
{{ form.password }}{{ form.password.errors }}
<span class="input-group-btn">
<button type="button" class="btn btn-default" onmousedown="document.getElementById('id_password').type='text'" onmouseup="document.getElementById('id_password').type='password'">
<span class="glyphicon glyphicon-eye-open"></span>
</button>
</span>
</div>
</div>

2
abonapp/templates/abonapp/editAbon.html

@ -35,7 +35,7 @@
<div class="form-group-sm">
<label for="id_ip" class="col-sm-4 control-label">{% trans 'Ip Address' %}</label>
<div class="col-sm-8">
<input type="text" value="{{ ip|default:'' }}" class="form-control" name="ip" placeholder="192.168.0.101" pattern="^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"/>
<input type="text" value="{{ ip|default:'' }}" class="form-control" name="ip" placeholder="{% trans 'Not assigned' %}" pattern="^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"/>
</div>
</div>

60
abonapp/views.py

@ -33,7 +33,7 @@ def peoples(request, gid):
return render(request, 'abonapp/peoples.html', {
'peoples': peoples_list,
'abon_group': get_object_or_404(models.AbonGroup, id=gid),
'abon_group': get_object_or_404(models.AbonGroup, pk=gid),
'dir': dr,
'order_by': request.GET.get('order_by')
})
@ -65,7 +65,7 @@ def addgroup(request):
@login_required
@mydefs.only_admins
def grouplist(request):
groups = models.AbonGroup.objects.annotate(usercount=Count('abon'))
groups = models.AbonGroup.objects.annotate(usercount=Count('abon')).order_by('title')
# фильтр
directory, field = mydefs.order_helper(request)
@ -86,7 +86,7 @@ def grouplist(request):
def delgroup(request):
try:
agd = mydefs.safe_int(request.GET.get('id'))
get_object_or_404(models.AbonGroup, id=agd).delete()
get_object_or_404(models.AbonGroup, pk=agd).delete()
messages.success(request, _('delete group success msg'))
return mydefs.res_success(request, 'abonapp:group_list')
except (NasFailedResult, NasNetworkError) as e:
@ -103,7 +103,7 @@ def addabon(request, gid):
frm = None
group = None
try:
group = get_object_or_404(models.AbonGroup, id=gid)
group = get_object_or_404(models.AbonGroup, pk=gid)
if request.method == 'POST':
frm = forms.AbonForm(request.POST, initial={'group': group})
if frm.is_valid():
@ -141,7 +141,7 @@ def delentity(request):
if typ == 'a':
if not request.user.has_perm('abonapp.delete_abon'):
raise PermissionDenied
abon = get_object_or_404(models.Abon, id=uid)
abon = get_object_or_404(models.Abon, pk=uid)
gid = abon.group.id
abon.delete()
messages.success(request, _('delete abon success msg'))
@ -149,7 +149,7 @@ def delentity(request):
elif typ == 'g':
if not request.user.has_perm('abonapp.delete_abongroup'):
raise PermissionDenied
get_object_or_404(models.AbonGroup, id=uid).delete()
get_object_or_404(models.AbonGroup, pk=uid).delete()
messages.success(request, _('delete group success msg'))
return mydefs.res_success(request, 'abonapp:group_list')
else:
@ -167,7 +167,7 @@ def delentity(request):
@login_required
@permission_required('abonapp.can_add_ballance')
def abonamount(request, gid, uid):
abon = get_object_or_404(models.Abon, id=uid)
abon = get_object_or_404(models.Abon, pk=uid)
try:
if request.method == 'POST':
abonid = mydefs.safe_int(request.POST.get('abonid'))
@ -186,14 +186,14 @@ def abonamount(request, gid, uid):
messages.add_message(request, messages.constants.ERROR, err)
return render_to_text('abonapp/modal_abonamount.html', {
'abon': abon,
'abon_group': get_object_or_404(models.AbonGroup, id=gid)
'abon_group': get_object_or_404(models.AbonGroup, pk=gid)
}, request=request)
@login_required
@mydefs.only_admins
def invoice_for_payment(request, gid, uid):
abon = get_object_or_404(models.Abon, id=uid)
abon = get_object_or_404(models.Abon, pk=uid)
invoices = models.InvoiceForPayment.objects.filter(abon=abon)
invoices = mydefs.pag_mn(request, invoices)
return render(request, 'abonapp/invoiceForPayment.html', {
@ -206,7 +206,7 @@ def invoice_for_payment(request, gid, uid):
@login_required
@mydefs.only_admins
def pay_history(request, gid, uid):
abon = get_object_or_404(models.Abon, id=uid)
abon = get_object_or_404(models.Abon, pk=uid)
pay_history = models.AbonLog.objects.filter(abon=abon).order_by('-id')
pay_history = mydefs.pag_mn(request, pay_history)
return render(request, 'abonapp/payHistory.html', {
@ -219,7 +219,7 @@ def pay_history(request, gid, uid):
@login_required
@mydefs.only_admins
def abon_services(request, gid, uid):
abon = get_object_or_404(models.Abon, id=uid)
abon = get_object_or_404(models.Abon, pk=uid)
abon_tarifs = models.AbonTariff.objects.filter(abon=uid)
active_abontariff = abon_tarifs.exclude(time_start=None)
@ -235,8 +235,8 @@ def abon_services(request, gid, uid):
@login_required
@mydefs.only_admins
def abonhome(request, gid, uid):
abon = get_object_or_404(models.Abon, id=uid)
abon_group = get_object_or_404(models.AbonGroup, id=gid)
abon = get_object_or_404(models.Abon, pk=uid)
abon_group = get_object_or_404(models.AbonGroup, pk=gid)
frm, passw = None, None
try:
if request.method == 'POST':
@ -298,8 +298,8 @@ def terminal_pay(request):
@permission_required('abonapp.add_invoiceforpayment')
def add_invoice(request, gid, uid):
uid = mydefs.safe_int(uid)
abon = get_object_or_404(models.Abon, id=uid)
grp = get_object_or_404(models.AbonGroup, id=gid)
abon = get_object_or_404(models.Abon, pk=uid)
grp = get_object_or_404(models.AbonGroup, pk=gid)
try:
if request.method == 'POST':
@ -334,8 +334,8 @@ def add_invoice(request, gid, uid):
@login_required
@permission_required('abonapp.can_buy_tariff')
def pick_tariff(request, gid, uid):
grp = get_object_or_404(models.AbonGroup, id=gid)
abon = get_object_or_404(models.Abon, id=uid)
grp = get_object_or_404(models.AbonGroup, pk=gid)
abon = get_object_or_404(models.Abon, pk=uid)
tariffs = grp.tariffs.all()
try:
if request.method == 'POST':
@ -367,7 +367,7 @@ def chpriority(request, gid, uid):
t = request.GET.get('t')
act = request.GET.get('a')
current_abon_tariff = get_object_or_404(models.AbonTariff, id=t)
current_abon_tariff = get_object_or_404(models.AbonTariff, pk=t)
try:
if act == 'up':
@ -386,7 +386,7 @@ def chpriority(request, gid, uid):
@login_required
@permission_required('abonapp.can_complete_service')
def complete_service(request, gid, uid, srvid):
abtar = get_object_or_404(models.AbonTariff, id=srvid)
abtar = get_object_or_404(models.AbonTariff, pk=srvid)
abon = abtar.abon
# считаем не использованные ресурсы
calc_obj = abtar.tariff.get_calc_type()(abtar)
@ -436,7 +436,7 @@ def complete_service(request, gid, uid, srvid):
'abtar': abtar,
'abon': abon,
'time_use': time_use,
'abon_group': get_object_or_404(models.AbonGroup, id=gid),
'abon_group': get_object_or_404(models.AbonGroup, pk=gid),
'tcost': round(res_amount, 4),
'cashback': round(cashback, 4)
})
@ -445,7 +445,7 @@ def complete_service(request, gid, uid, srvid):
@login_required
@permission_required('abonapp.can_activate_service')
def activate_service(request, gid, uid, srvid):
abtar = get_object_or_404(models.AbonTariff, id=srvid)
abtar = get_object_or_404(models.AbonTariff, pk=srvid)
amount = abtar.calc_amount_service()
try:
@ -479,7 +479,7 @@ def activate_service(request, gid, uid, srvid):
@permission_required('abonapp.delete_abontariff')
def unsubscribe_service(request, gid, uid, srvid):
try:
get_object_or_404(models.AbonTariff, id=int(srvid)).delete()
get_object_or_404(models.AbonTariff, pk=int(srvid)).delete()
messages.success(request, _('User has been detached from service'))
except NasFailedResult as e:
messages.error(request, e)
@ -539,11 +539,11 @@ def update_nas(request, group_id):
@mydefs.only_admins
def task_log(request, gid, uid):
from taskapp.models import Task
abon = get_object_or_404(models.Abon, id=uid)
abon = get_object_or_404(models.Abon, pk=uid)
tasks = Task.objects.filter(abon=abon)
return render(request, 'abonapp/task_log.html', {
'tasks': tasks,
'abon_group': get_object_or_404(models.AbonGroup, id=gid),
'abon_group': get_object_or_404(models.AbonGroup, pk=gid),
'abon': abon
})
@ -551,9 +551,9 @@ def task_log(request, gid, uid):
@login_required
@mydefs.only_admins
def passport_view(request, gid, uid):
abon = get_object_or_404(models.Abon, id=uid)
abon = get_object_or_404(models.Abon, pk=uid)
return render(request, 'abonapp/passport_view.html', {
'abon_group': get_object_or_404(models.AbonGroup, id=gid),
'abon_group': get_object_or_404(models.AbonGroup, pk=gid),
'abon': abon
})
@ -578,14 +578,14 @@ def chgroup_tariff(request, gid):
def abons(request):
ablist = [{
'id': abn.id,
'tarif_id': abn.active_tariff().id if abn.active_tariff() else 0,
'id': abn.pk,
'tarif_id': abn.active_tariff().pk if abn.active_tariff() else 0,
'ip': abn.ip_address.int_ip(),
'is_active': abn.is_active
} for abn in models.Abon.objects.all()]
tarlist = [{
'id': trf.id,
'id': trf.pk,
'speedIn': trf.speedIn,
'speedOut': trf.speedOut
} for trf in Tariff.objects.all()]
@ -601,5 +601,5 @@ def abons(request):
def search_abon(request):
word = request.GET.get('s')
results = models.Abon.objects.filter(fio__icontains=word)[:8]
results = [{'id':usr.id, 'name':usr.username, 'fio':usr.fio} for usr in results]
results = [{'id':usr.pk, 'name':usr.username, 'fio':usr.fio} for usr in results]
return HttpResponse(dumps(results, ensure_ascii=False))

4
agent/core.py

@ -43,7 +43,7 @@ class BaseTransmitter(metaclass=ABCMeta):
@abstractmethod
@check_input_type(AbonStruct)
def add_user(self, user):
def add_user(self, user, *args):
"""добавляем абонента"""
@abstractmethod
@ -53,7 +53,7 @@ class BaseTransmitter(metaclass=ABCMeta):
@abstractmethod
@check_input_type(AbonStruct)
def update_user(self, user):
def update_user(self, user, *args):
"""чтоб обновить абонента можно изменить всё кроме его uid, по uid абонент будет найден"""
@abstractmethod

31
agent/mod_mikrotik.py

@ -6,7 +6,7 @@ from .core import BaseTransmitter, NasFailedResult, NasNetworkError
from mydefs import ping
from .structs import TariffStruct, AbonStruct, IpStruct, ShapeItem
from . import settings
from djing.settings import DEBUG
#from djing.settings import DEBUG
import re
@ -293,20 +293,27 @@ class QueueManager(TransmitterManager):
class IpAddressListManager(TransmitterManager):
def add(self, list_name, ip):
def add(self, list_name, ip, timeout=None):
assert isinstance(ip, IpStruct)
return self._exec_cmd([
commands = [
'/ip/firewall/address-list/add',
'=list=%s' % list_name,
'=address=%s' % ip.get_str()
])
]
if type(timeout) is int:
commands.append('=timeout=%d' % timeout)
return self._exec_cmd(commands)
def _edit(self, ip, mk_id):
def _edit(self, ip, mk_id, timeout=None):
assert isinstance(ip, IpStruct)
return self._exec_cmd([
commands = [
'/ip/firewall/address-list/set', '=.id=' + str(mk_id),
'?address=%s' % ip.get_str()
])
]
if type(timeout) is int:
commands.append('=timeout=%d' % timeout)
return self._exec_cmd(commands)
def remove(self, mk_id):
return self._exec_cmd([
@ -357,11 +364,11 @@ class MikrotikTransmitter(QueueManager, IpAddressListManager):
if len(ip_list_entity) > 1:
IpAddressListManager.remove(self, ip_list_entity[0]['=.id'])
def add_user(self, user):
def add_user(self, user, ip_timeout=None):
assert isinstance(user.tariff, TariffStruct)
assert isinstance(user.ip, IpStruct)
QueueManager.add(self, user)
IpAddressListManager.add(self, LIST_USERS_ALLOWED, user.ip)
IpAddressListManager.add(self, LIST_USERS_ALLOWED, user.ip, ip_timeout)
# удаляем из списка заблокированных абонентов
firewall_ip_list_obj = IpAddressListManager.find(self, user.ip, LIST_USERS_BLOCKED)
if len(firewall_ip_list_obj) > 1:
@ -374,7 +381,7 @@ class MikrotikTransmitter(QueueManager, IpAddressListManager):
IpAddressListManager.remove(self, firewall_ip_list_obj[0]['=.id'])
# обновляем основную инфу абонента
def update_user(self, user):
def update_user(self, user, ip_timeout=None):
assert isinstance(user.tariff, TariffStruct)
assert isinstance(user.ip, IpStruct)
@ -384,12 +391,12 @@ class MikrotikTransmitter(QueueManager, IpAddressListManager):
# если не найден (mikrotik возвращает пустой словарь в списке если ничего нет)
if len(find_res) < 2:
# добавим запись об абоненте
IpAddressListManager.add(self, LIST_USERS_ALLOWED, user.ip)
IpAddressListManager.add(self, LIST_USERS_ALLOWED, user.ip, ip_timeout)
else:
# если ip абонента в биллинге не такой как в mikrotik
if find_res[0]['=address'] != user.ip.get_str():
# то обновляем запись в mikrotik
IpAddressListManager._edit(self, user.ip, find_res[0]['=.id'])
IpAddressListManager._edit(self, user.ip, find_res[0]['=.id'], ip_timeout)
# Проверяем шейпер
queue = QueueManager.find(self, 'uid%d' % user.uid)

92
dhcp_lever.py

@ -0,0 +1,92 @@
#!/usr/bin/env python3
import os
import sys
import django
from django.core.exceptions import MultipleObjectsReturned, ValidationError
from django.utils.translation import ugettext as _
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djing.settings")
django.setup()
from agent import NasFailedResult, NasNetworkError
from abonapp.models import Abon, Opt82
from ip_pool.models import IpPoolItem
def die(text):
print(text)
exit(1)
def get_82_opts(switch_mac, switch_port):
try:
opt82 = Opt82.objects.get(mac=switch_mac, port=switch_port)
except MultipleObjectsReturned:
Opt82.objects.filter(mac=switch_mac, port=switch_port)[1:].delete()
return get_82_opts(switch_mac, switch_port)
except Opt82.DoesNotExist:
opt82 = Opt82.objects.create(mac=switch_mac, port=switch_port)
return opt82
def get_or_create_pool_item(ip):
try:
ip_item = IpPoolItem.objects.get(ip=ip)
except IpPoolItem.DoesNotExist:
ip_item = IpPoolItem.objects.create(ip=ip)
except MultipleObjectsReturned:
IpPoolItem.objects.filter(ip=ip)[1:].delete()
return get_or_create_pool_item(ip)
return ip_item
def dhcp_commit(client_ip, client_mac, switch_mac, switch_port):
opt82 = get_82_opts(switch_mac, switch_port)
if opt82 is None:
print(_("ERROR: opt82 with mac:%s and port:%d does not exist in db") % (switch_mac, switch_port))
return
try:
abon = Abon.objects.get(opt82=opt82)
abon.ip_address = get_or_create_pool_item(client_ip)
Abon.is_dhcp = True
abon.save(update_fields=['ip_address'])
print(_('Ip address update for %s successfull') % abon.get_short_name())
except Abon.DoesNotExist:
print('ERROR: abon with option82(%s-%d) does not exist' % (opt82.mac, opt82.port))
def dhcp_expiry(client_ip):
try:
ip_item = IpPoolItem.objects.get(ip=client_ip)
abon = Abon.objects.get(ip_address=ip_item)
abon.ip_address = None
abon.save(update_fields=['ip_address'])
except IpPoolItem.DoesNotExist:
pass
except Abon.DoesNotExist:
pass
def dhcp_release(client_ip):
dhcp_expiry(client_ip)
def main(argv):
if len(argv) < 3:
die(_('Too few arguments, exiting...'))
action = argv[1]
if action == 'commit':
if len(argv) < 6:
die(_('Too few arguments, exiting...'))
dhcp_commit(argv[2], argv[3], argv[4], int(argv[5]))
elif action == 'expiry':
dhcp_expiry(argv[2])
elif action == 'release':
dhcp_release(argv[2])
if __name__ == "__main__":
try:
main(sys.argv)
except (NasNetworkError, NasFailedResult) as e:
print('NAS:', e)
except (ValidationError, ValueError) as e:
print('ERROR:', e)

2
mydefs.py

@ -150,7 +150,7 @@ def only_admins(fn):
def ping(hostname):
response = os.system("`which ping` -c 1 " + hostname)
response = os.system("`which ping` -c 1 %s > /dev/null" % hostname)
return True if response == 0 else False

2
requirements.txt

@ -1,6 +1,8 @@
Django==1.9
Pillow
telepot
# for mac address field
netaddr
# for testing required xmltodict
xmltodict
mysqlclient

2
taskapp/locale/ru/LC_MESSAGES/django.po

@ -364,4 +364,4 @@ msgid "Tasks to be performed"
msgstr "Задачи, которые необходимо выполнить"
msgid "locality %s. Task type - %s. "
msgstr "с. %s. Тип задачи - %s. "
msgstr "с. %s. Тип задачи - %s. "
Loading…
Cancel
Save