commit
2e888db44d
190 changed files with 6690 additions and 0 deletions
-
6.gitignore
-
65Doc.txt
-
0abonapp/__init__.py
-
8abonapp/admin.py
-
68abonapp/forms.py
-
110abonapp/migrations/0001_initial.py
-
0abonapp/migrations/__init__.py
-
349abonapp/models.py
-
50abonapp/tests.py
-
21abonapp/urls.py
-
19abonapp/urls_abon.py
-
439abonapp/views.py
-
0accounts_app/__init__.py
-
4accounts_app/admin.py
-
42accounts_app/migrations/0001_initial.py
-
0accounts_app/migrations/__init__.py
-
107accounts_app/models.py
-
16accounts_app/tests.py
-
27accounts_app/urls.py
-
258accounts_app/views.py
-
12agent.py
-
3agent/__init__.py
-
27agent/db.py
-
48agent/firewall.py
-
72agent/ipfw.sh
-
120agent/main.py
-
143agent/models.py
-
0agent/netflow/__init__.py
-
49agent/netflow/netflow_handler.py
-
11agent/netflow/netflow_handler.sh
-
7agent/netflow/start_netflow.sh
-
17agent/settings.py
-
226agent/sslTransmitter.py
-
1agent/tariff.py
-
11bugs.txt
-
18cron.py
-
0devapp/__init__.py
-
6devapp/admin.py
-
7devapp/apps.py
-
109devapp/base_intr.py
-
30devapp/dev_types.py
-
32devapp/forms.py
-
49devapp/migrations/0001_initial.py
-
0devapp/migrations/__init__.py
-
52devapp/models.py
-
3devapp/tests.py
-
10devapp/urls.py
-
59devapp/views.py
-
0djing/__init__.py
-
143djing/settings.py
-
26djing/urls.py
-
9djing/views.py
-
16djing/wsgi.py
-
17install.sql
-
1ip_pool/__init__.py
-
4ip_pool/admin.py
-
6ip_pool/apps.py
-
21ip_pool/forms.py
-
24ip_pool/migrations/0001_initial.py
-
0ip_pool/migrations/__init__.py
-
60ip_pool/models.py
-
3ip_pool/tests.py
-
13ip_pool/urls.py
-
76ip_pool/views.py
-
10manage.py
-
0mapapp/__init__.py
-
4mapapp/admin.py
-
7mapapp/apps.py
-
25mapapp/migrations/0001_initial.py
-
0mapapp/migrations/__init__.py
-
10mapapp/models.py
-
3mapapp/tests.py
-
7mapapp/urls.py
-
18mapapp/views.py
-
111mydefs.py
-
0photo_app/__init__.py
-
4photo_app/admin.py
-
25photo_app/migrations/0001_initial.py
-
0photo_app/migrations/__init__.py
-
69photo_app/models.py
-
16photo_app/tests.py
-
12photo_app/urls.py
-
1photo_app/views.py
-
0privatemessage/__init__.py
-
5privatemessage/admin.py
-
7privatemessage/context_processors.py
-
39privatemessage/migrations/0001_initial.py
-
0privatemessage/migrations/__init__.py
-
34privatemessage/models.py
-
22privatemessage/tests.py
-
8privatemessage/urls.py
-
54privatemessage/views.py
-
4requirements.txt
-
0searchapp/__init__.py
-
3searchapp/admin.py
-
7searchapp/apps.py
-
0searchapp/migrations/__init__.py
-
5searchapp/models.py
-
3searchapp/tests.py
-
7searchapp/urls.py
@ -0,0 +1,6 @@ |
|||
*.pyc |
|||
*.db |
|||
media/* |
|||
media/min/* |
|||
~*/migrations/000*.py |
|||
.idea/ |
|||
@ -0,0 +1,65 @@ |
|||
// Формат общения NAS с базой |
|||
[ |
|||
{ |
|||
"toa": 1, /* Тип события: |
|||
0 - ничего не надо (пустое) |
|||
1 - Активировать клиента (включить его) |
|||
2 - Выключить клиента |
|||
3 - Поставить заглушку |
|||
4 - Открыть доступ в интернет |
|||
5 - Закрыть доступ в интернет |
|||
6 - Перечитать всю инфу (полная перезагрузка NAS) |
|||
7 - Изменилась инфа об абоненте, переприменить его |
|||
*/ |
|||
"id": 12, // ID объекта о котором событие (абонент там, или тариф) |
|||
"dt": "data" // Разная инфа, содержимое зависит от поля 'toa' |
|||
}, |
|||
{ |
|||
"id": 13, |
|||
"toa": 3 |
|||
} |
|||
] |
|||
|
|||
// Формат передачи инфы об абонентах |
|||
{ |
|||
"subscribers": [ |
|||
{ |
|||
"is_active": true, // Активен-ли абонент |
|||
"ip": 168558850, // Его ip |
|||
"tarif_id": 1, // id тарифа |
|||
"id": 2 // id абонента |
|||
}, |
|||
{ |
|||
"is_active": true, |
|||
"ip": 168558850, |
|||
"tarif_id": 1, |
|||
"id": 2 |
|||
} |
|||
], |
|||
"tariffs": [ |
|||
{ |
|||
"tid": 1, // id тарифа |
|||
"amount": 0.0, // стоимость |
|||
"speedOut": 0.0, // Исходящая скорость |
|||
"speedIn": 0.0 // Входящая скорость |
|||
} |
|||
] |
|||
} |
|||
|
|||
|
|||
------------------------------- |
|||
Состояние оплаты абонента определяется на основе присутствия у него тарифного плана, |
|||
если он есть - то значит всё оплачено (абонент его купил) и может пользоваться услугами. |
|||
Тарифный план имеет срок действия и стоимость. Его можно купить, как билет в интернет :) |
|||
|
|||
|
|||
ТАРИФНЫЙ ПЛАН С НАИМЕНЬШИМ ЧИСЛОМ ПРИОРИТЕТА ИМЕЕТ НАИВЫСШИЙ ПРИОРИТЕТ |
|||
В общем чем ближе приоритет к 0 тем он выше |
|||
0 - текущий тариф |
|||
|
|||
|
|||
Свою логику расчёта по тарифу можно добавить в файле tariff_app/custom_tariffs.py |
|||
Там надо добавить класс, наследованный от TariffBase и реализовать его абстрактные методы, |
|||
потом добавить этот класс в кортеж TARIFF_CHOICES указав: |
|||
код из 2х букв, сочетание должно быть уникальным |
|||
и ваш класс для своей логики расчёта тарифа |
|||
@ -0,0 +1,8 @@ |
|||
from django.contrib import admin |
|||
import models |
|||
|
|||
admin.site.register(models.AbonGroup) |
|||
admin.site.register(models.Abon) |
|||
admin.site.register(models.InvoiceForPayment) |
|||
admin.site.register(models.AbonLog) |
|||
admin.site.register(models.AbonTariff) |
|||
@ -0,0 +1,68 @@ |
|||
# -*- coding: utf-8 -*- |
|||
from django import forms |
|||
from django.core.validators import RegexValidator |
|||
import models |
|||
from mydefs import ip_addr_regex |
|||
|
|||
|
|||
class AbonForm(forms.Form): |
|||
username = forms.CharField(max_length=127, required=False, widget=forms.TextInput(attrs={ |
|||
'placeholder': u'Логин', |
|||
'class': "form-control", |
|||
'id': "login" |
|||
})) |
|||
fio = forms.CharField(max_length=256, widget=forms.TextInput(attrs={ |
|||
'placeholder': u'ФИО', |
|||
'class': "form-control", |
|||
'id': "fio" |
|||
}), required=False) |
|||
ip_address = forms.GenericIPAddressField(protocol='IPv4', required=False, widget=forms.TextInput(attrs={ |
|||
'pattern': ip_addr_regex, |
|||
'placeholder': u'127.0.0.1', |
|||
'class': "form-control", |
|||
'id': "ip" |
|||
})) |
|||
|
|||
telephone = forms.CharField( |
|||
max_length=16, |
|||
validators=[RegexValidator(r'^\+[7,8,9,3]\d{10,11}$')], |
|||
widget=forms.TextInput(attrs={ |
|||
'placeholder': u'+[7,8,9,3] и 10,11 цифр', |
|||
'pattern': r'^\+[7,8,9,3]\d{10,11}$', |
|||
'required': '', |
|||
'class': 'form-control', |
|||
'id': 'telephone' |
|||
}) |
|||
) |
|||
is_active = forms.BooleanField( |
|||
required=False, |
|||
widget=forms.Select(attrs={'class': 'form-control', 'id': 'isactive'}) |
|||
) |
|||
|
|||
group = forms.ModelChoiceField( |
|||
queryset=models.AbonGroup.objects.all(), |
|||
required=False, |
|||
widget=forms.Select(attrs={'class': 'form-control', 'id': 'grp'}) |
|||
) |
|||
address = forms.CharField( |
|||
max_length=256, |
|||
required=False, |
|||
widget = forms.TextInput(attrs={'class': 'form-control', 'id': 'address'}) |
|||
) |
|||
|
|||
|
|||
class AbonGroupForm(forms.ModelForm): |
|||
class Meta: |
|||
model = models.AbonGroup |
|||
fields = ['title', 'address'] |
|||
widgets = { |
|||
'class': 'form-control' |
|||
} |
|||
|
|||
|
|||
class BuyTariff(forms.Form): |
|||
tariff = forms.ModelChoiceField( |
|||
queryset=models.Tariff.objects.all(), |
|||
required=True, |
|||
widget=forms.Select(attrs={'class': 'form-control', 'id': 'tariff'}) |
|||
) |
|||
@ -0,0 +1,110 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.9 on 2016-06-28 23:51 |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.conf import settings |
|||
import django.core.validators |
|||
from django.db import migrations, models |
|||
import django.db.models.deletion |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
initial = True |
|||
|
|||
dependencies = [ |
|||
('accounts_app', '0001_initial'), |
|||
migrations.swappable_dependency(settings.AUTH_USER_MODEL), |
|||
('ip_pool', '0001_initial'), |
|||
('tariff_app', '0001_initial'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.CreateModel( |
|||
name='Abon', |
|||
fields=[ |
|||
('userprofile_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to=settings.AUTH_USER_MODEL)), |
|||
('ballance', models.FloatField(default=0.0, validators=[django.core.validators.DecimalValidator])), |
|||
('address', models.CharField(max_length=256)), |
|||
], |
|||
options={ |
|||
'db_table': 'abonent', |
|||
}, |
|||
bases=('accounts_app.userprofile',), |
|||
), |
|||
migrations.CreateModel( |
|||
name='AbonGroup', |
|||
fields=[ |
|||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
|||
('title', models.CharField(max_length=127)), |
|||
('address', models.CharField(blank=True, max_length=256, null=True)), |
|||
], |
|||
options={ |
|||
'db_table': 'abonent_groups', |
|||
}, |
|||
), |
|||
migrations.CreateModel( |
|||
name='AbonLog', |
|||
fields=[ |
|||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
|||
('amount', models.FloatField(default=0.0)), |
|||
('comment', models.CharField(max_length=128)), |
|||
('date', models.DateTimeField(auto_now_add=True)), |
|||
('abon', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='abonapp.Abon')), |
|||
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL)), |
|||
], |
|||
options={ |
|||
'db_table': 'abonent_log', |
|||
}, |
|||
), |
|||
migrations.CreateModel( |
|||
name='AbonTariff', |
|||
fields=[ |
|||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
|||
('tariff_priority', models.PositiveSmallIntegerField(default=0)), |
|||
('time_start', models.DateTimeField(blank=True, default=None, null=True)), |
|||
('abon', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='abonapp.Abon')), |
|||
('tariff', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='linkto_tariff', to='tariff_app.Tariff')), |
|||
], |
|||
options={ |
|||
'ordering': ('tariff_priority',), |
|||
'db_table': 'abonent_tariff', |
|||
}, |
|||
), |
|||
migrations.CreateModel( |
|||
name='InvoiceForPayment', |
|||
fields=[ |
|||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
|||
('status', models.BooleanField(default=False)), |
|||
('amount', models.FloatField(default=0.0)), |
|||
('comment', models.CharField(max_length=128)), |
|||
('date_create', models.DateTimeField(auto_now_add=True)), |
|||
('date_pay', models.DateTimeField(blank=True, null=True)), |
|||
('abon', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='abonapp.Abon')), |
|||
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL)), |
|||
], |
|||
options={ |
|||
'ordering': ('date_create',), |
|||
'db_table': 'abonent_inv_pay', |
|||
}, |
|||
), |
|||
migrations.AddField( |
|||
model_name='abon', |
|||
name='current_tariffs', |
|||
field=models.ManyToManyField(through='abonapp.AbonTariff', to='tariff_app.Tariff'), |
|||
), |
|||
migrations.AddField( |
|||
model_name='abon', |
|||
name='group', |
|||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='abonapp.AbonGroup'), |
|||
), |
|||
migrations.AddField( |
|||
model_name='abon', |
|||
name='ip_address', |
|||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='ip_pool.IpPoolItem'), |
|||
), |
|||
migrations.AlterUniqueTogether( |
|||
name='abontariff', |
|||
unique_together=set([('abon', 'tariff', 'tariff_priority')]), |
|||
), |
|||
] |
|||
@ -0,0 +1,349 @@ |
|||
# -*- coding: utf-8 -*- |
|||
from django.core.exceptions import MultipleObjectsReturned |
|||
from django.http import Http404 |
|||
from django.shortcuts import get_object_or_404 |
|||
from django.utils import timezone |
|||
from django.utils.datetime_safe import datetime |
|||
from agent import get_TransmitterClientKlass, Abonent, Tariff as AgentTariff |
|||
from ip_pool.models import IpPoolItem |
|||
from tariff_app.models import Tariff |
|||
from django.db import models |
|||
from djing import settings |
|||
from django.core.validators import DecimalValidator |
|||
from accounts_app.models import UserProfile |
|||
|
|||
|
|||
class LogicError(Exception): |
|||
def __init__(self, value): |
|||
self.value = value |
|||
def __unicode__(self): |
|||
return repr(self.value) |
|||
|
|||
|
|||
class AbonGroup(models.Model): |
|||
title = models.CharField(max_length=127) |
|||
address = models.CharField(max_length=256, blank=True, null=True) |
|||
|
|||
class Meta: |
|||
db_table = 'abonent_groups' |
|||
|
|||
def __unicode__(self): |
|||
return self.title |
|||
|
|||
|
|||
class AbonLog(models.Model): |
|||
abon = models.ForeignKey('Abon') |
|||
amount = models.FloatField(default=0.0) |
|||
author = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='+') |
|||
comment = models.CharField(max_length=128) |
|||
date = models.DateTimeField(auto_now_add=True) |
|||
|
|||
class Meta: |
|||
db_table = 'abonent_log' |
|||
|
|||
def __unicode__(self): |
|||
return self.comment |
|||
|
|||
|
|||
class AbonTariffManager(models.Manager): |
|||
|
|||
def update_priorities(self, abonent): |
|||
abon_tariff_list = AbonTariff.objects.filter(abon=abonent).order_by('tariff_priority') |
|||
|
|||
# Обновляем приоритеты, чтоб по порядку были |
|||
at_pr = 0 |
|||
for at in abon_tariff_list: |
|||
at.tariff_priority = at_pr |
|||
at_pr += 1 |
|||
at.save() |
|||
|
|||
|
|||
class AbonTariff(models.Model): |
|||
abon = models.ForeignKey('Abon') |
|||
tariff = models.ForeignKey(Tariff, related_name='linkto_tariff') |
|||
tariff_priority = models.PositiveSmallIntegerField(default=0) |
|||
|
|||
# время начала действия, остальные что не начали действие - NULL |
|||
time_start = models.DateTimeField(null=True, blank=True, default=None) |
|||
|
|||
objects = AbonTariffManager() |
|||
|
|||
def priority_up(self): |
|||
# ищем услугу с большим приоритетом(число приоритета меньше) |
|||
target_abtar = AbonTariff.objects.filter( |
|||
abon=self.abon, |
|||
tariff_priority__lt=self.tariff_priority |
|||
).order_by('-tariff_priority')[:1] |
|||
if target_abtar.count() > 0: |
|||
target_abtar = target_abtar[0] |
|||
else: |
|||
return |
|||
|
|||
# Ищем текущий тариф абонента |
|||
active_abtar = AbonTariff.objects.filter( |
|||
abon=self.abon |
|||
)[:1] |
|||
if active_abtar.count() > 0: |
|||
active_abtar = active_abtar[0] |
|||
else: |
|||
return |
|||
|
|||
# Если услуга с которой хотим поменяться приоритетом является текущей то нельзя меняться |
|||
if active_abtar == target_abtar: |
|||
return |
|||
|
|||
# Swap приоритетов у текущего и найденного с меньшим tariff_priority (большим приоритетом) |
|||
tmp_prior = target_abtar.tariff_priority |
|||
target_abtar.tariff_priority = self.tariff_priority |
|||
target_abtar.save() |
|||
self.tariff_priority = tmp_prior |
|||
self.save() |
|||
|
|||
def priority_down(self): |
|||
# ищем услугу с меньшим приоритетом |
|||
target_abtar = AbonTariff.objects.filter( |
|||
abon=self.abon, |
|||
tariff_priority__gt=self.tariff_priority |
|||
)[:1] |
|||
if target_abtar.count() > 0: |
|||
target_abtar = target_abtar[0] |
|||
else: |
|||
# меньше нет, это самая последняя услуга |
|||
return |
|||
|
|||
# Swap приоритетов у текущего и найденного с большим tariff_priority (меньшим приоритетом) |
|||
tmp_pr = self.tariff_priority |
|||
self.tariff_priority = target_abtar.tariff_priority |
|||
target_abtar.tariff_priority = tmp_pr |
|||
target_abtar.save() |
|||
self.save() |
|||
|
|||
# Считает текущую стоимость услуг согласно выбранной для тарифа логики оплаты (см. в документации) |
|||
def calc_amount_service(self): |
|||
calc_obj = self.tariff.get_calc_type() |
|||
# calc_obj - instance of tariff_app.custom_tariffs.TariffBase |
|||
amount = calc_obj.calc_amount(self) |
|||
return round(amount, 2) |
|||
|
|||
# досрочно завершает услугу |
|||
def finish_and_activate_next_tariff(self, author): |
|||
|
|||
# выберем следующие по приоритету услуги |
|||
next_tarifs = AbonTariff.objects.filter(tariff_priority__gt = self.tariff_priority, abon=self.abon)[:1] |
|||
|
|||
if next_tarifs.count() < 1: |
|||
raise LogicError(u'У абонента нет следующих назначенных услуг') |
|||
|
|||
# 0й элемент это следующая подключаемая услуга |
|||
next_tarifs[0].time_start = timezone.now() |
|||
next_tarifs[0].save() |
|||
|
|||
# сколько денег стоят потраченные ресурсы |
|||
used_services = self.calc_amount_service() |
|||
|
|||
#теперь к текущему баллансу добавляем сумму не потраченных ресурсов, т.к. полная сумма тарифа списывается при покупке тарифа |
|||
ret_amount = self.tariff.amount - used_services |
|||
self.abon.ballance += ret_amount |
|||
self.abon.save() |
|||
|
|||
AbonLog.objects.create( |
|||
abon = self.abon, |
|||
amount = ret_amount, |
|||
author = author, |
|||
comment = u'Досрочное завершение услуги %s' % (self.tariff.title) |
|||
) |
|||
|
|||
def __unicode__(self): |
|||
return "%d: '%s' - '%s'" % ( |
|||
self.tariff_priority, |
|||
self.tariff.title, |
|||
self.abon.get_short_name() |
|||
) |
|||
|
|||
class Meta: |
|||
ordering = ('tariff_priority',) |
|||
db_table = 'abonent_tariff' |
|||
unique_together = (('abon', 'tariff', 'tariff_priority'),) |
|||
|
|||
|
|||
class Abon(UserProfile): |
|||
current_tariffs = models.ManyToManyField(Tariff, through=AbonTariff) |
|||
group = models.ForeignKey(AbonGroup, models.SET_NULL, blank=True, null=True) |
|||
ballance = models.FloatField(default=0.0, validators=[DecimalValidator]) |
|||
ip_address = models.OneToOneField(IpPoolItem, on_delete=models.SET_NULL, null=True, blank=True) |
|||
address = models.CharField(max_length=256) |
|||
|
|||
_act_tar_cache = None |
|||
|
|||
def active_tariff(self): |
|||
if self._act_tar_cache: |
|||
return self._act_tar_cache |
|||
|
|||
ats = AbonTariff.objects.filter(abon=self)[:1] |
|||
|
|||
if ats.count() > 0: |
|||
self._act_tar_cache = ats[0].tariff |
|||
return ats[0].tariff |
|||
else: |
|||
self._act_tar_cache = None |
|||
return |
|||
|
|||
def save_form(self, abonform_instance): |
|||
try: |
|||
cd = abonform_instance.cleaned_data |
|||
tel = cd['telephone'] |
|||
self.username = cd['username'] or tel[1:] |
|||
self.fio = cd['fio'] |
|||
self.telephone = tel |
|||
self.is_admin = False |
|||
self.ip_address = get_object_or_404(IpPoolItem, ip=cd['ip_address']) |
|||
self.is_active = True |
|||
self.group = cd['group'] |
|||
self.address = cd['address'] |
|||
except Http404: |
|||
raise LogicError(u'Введённый IP адрес не добавлен в ip pool') |
|||
except MultipleObjectsReturned: |
|||
raise LogicError(u'Введённый IP адрес не определён') |
|||
|
|||
class Meta: |
|||
db_table = 'abonent' |
|||
|
|||
def make_pay(self, curuser, how_match_to_pay=0.0): |
|||
AbonLog.objects.create( |
|||
abon = self, |
|||
amount = -how_match_to_pay, |
|||
author = curuser, |
|||
comment = u'Снятие со счёта средств' |
|||
) |
|||
self.ballance -= how_match_to_pay |
|||
|
|||
def add_ballance(self, current_user, amount): |
|||
AbonLog.objects.create( |
|||
abon = self, |
|||
amount = amount, |
|||
author = current_user, |
|||
comment = u'Пополнение счёта через админку' |
|||
) |
|||
self.ballance += amount |
|||
|
|||
def buy_tariff(self, tariff, author): |
|||
if self.ballance >= tariff.amount: |
|||
# денег достаточно, можно покупать |
|||
self.ballance -= tariff.amount |
|||
|
|||
# выбераем связь ТарифАбонент с самым низким приоритетом |
|||
abtrf = AbonTariff.objects.filter(abon=self).order_by('-tariff_priority')[:1] |
|||
abtrf = abtrf[0] if abtrf.count() > 0 else None |
|||
|
|||
# создаём новую связь с приоритетом ещё ниже |
|||
new_abtar = AbonTariff( |
|||
abon=self, |
|||
tariff=tariff, |
|||
tariff_priority=abtrf.tariff_priority+1 if abtrf else -1 |
|||
) |
|||
|
|||
# Если это первая услуга в списке (фильтр по приоритету ничего не вернул) |
|||
if not abtrf: |
|||
# значит она сразу стаёт активной |
|||
new_abtar.time_start = timezone.now() |
|||
|
|||
new_abtar.save() |
|||
|
|||
# шлём сигнал о том что абонент купил первую услугу, а значит можно пользоваться инетом |
|||
# сигнал можно слать только после того как будет сохранён новый объект AbonTariff |
|||
if self.is_active and not abtrf: |
|||
tc = get_TransmitterClientKlass()() |
|||
act_tar = self.active_tariff() |
|||
agent_abon = Abonent( |
|||
self.id, |
|||
self.ip_address.int_ip(), |
|||
AgentTariff( |
|||
act_tar.id if act_tar else 0, |
|||
act_tar.speedIn if act_tar else 0.0, |
|||
act_tar.speedOut if act_tar else 0.0 |
|||
) |
|||
) |
|||
tc.signal_abon_refresh_info(agent_abon) |
|||
tc.signal_abon_open_inet(agent_abon) |
|||
|
|||
# Запись об этом в лог |
|||
AbonLog.objects.create( |
|||
abon = self, amount = -tariff.amount, |
|||
author = author, |
|||
comment = u'Покупка тарифного плана через админку, тариф "%s"' % tariff.title |
|||
) |
|||
else: |
|||
raise LogicError(u'Недостаточно денег на счету абонента') |
|||
|
|||
# Пробует подключить новую услугу если пришло время |
|||
def activate_next_tariff(self, author): |
|||
ats = AbonTariff.objects.filter(abon=self).order_by('tariff_priority') |
|||
|
|||
nw = datetime.now(tz=timezone.get_current_timezone()) |
|||
|
|||
for at in ats: |
|||
# Если времени активации нет, то это ещё не активированный тариф |
|||
if not at.time_start: |
|||
return |
|||
|
|||
# время к началу месяца |
|||
to_start_month = datetime(nw.year, nw.month, 1, tzinfo=timezone.get_current_timezone()) |
|||
|
|||
# проверяем расстояние от Сегодня до начала этого месяца. |
|||
# И от заказа тарифа до начала этого месяца |
|||
if (nw - at.time_start) > (nw - to_start_month): |
|||
# Заказ из прошлого месяца, срок действия закончен |
|||
print u'Заказ из прошлого месяца, срок действия закончен' |
|||
|
|||
# выберем следующую по приоритету |
|||
#next_tarifs = AbonTariff.objects.filter(tariff_priority__gt = self.tariff_priority, abon=self.abon) |
|||
next_tarifs = filter(lambda tr: tr.tariff_priority > at.tariff_priority, ats)[:2] |
|||
|
|||
# и если что-нибудь вернулось то активируем, давая время начала действия |
|||
if next_tarifs.count() > 0: |
|||
next_tarifs[0].time_start = nw |
|||
next_tarifs[0].save() |
|||
|
|||
# завершаем текущую услугу. |
|||
at.delete() |
|||
|
|||
# Создаём лог о завершении услуги |
|||
AbonLog.objects.create( |
|||
abon = self, |
|||
amount = 0, |
|||
author = author, |
|||
comment = u'Завершение услуги по истечению срока действия' |
|||
) |
|||
|
|||
|
|||
class InvoiceForPayment(models.Model): |
|||
abon = models.ForeignKey(Abon) |
|||
status = models.BooleanField(default=False) |
|||
amount = models.FloatField(default=0.0) |
|||
comment = models.CharField(max_length=128) |
|||
date_create = models.DateTimeField(auto_now_add=True) |
|||
date_pay = models.DateTimeField(blank=True, null=True) |
|||
author = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='+') |
|||
|
|||
def __unicode__(self): |
|||
return "%s -> %d $" % (self.abon.username, self.amount) |
|||
|
|||
def set_ok(self): |
|||
self.status = True |
|||
self.date_pay = datetime.now() |
|||
|
|||
def get_prev_invoice(self): |
|||
return self.objects.order |
|||
|
|||
class Meta: |
|||
ordering = ('date_create',) |
|||
db_table = 'abonent_inv_pay' |
|||
|
|||
|
|||
#def abon_save_signal(sender, instance, **kwargs): |
|||
# if not kwargs['created']: |
|||
# # if not create (change only) |
|||
# print "Kw1", instance.username, instance.is_active |
|||
|
|||
|
|||
#models.signals.post_save.connect(abon_save_signal, sender=Abon) |
|||
@ -0,0 +1,50 @@ |
|||
# -*- coding: utf-8 -*- |
|||
from django.shortcuts import get_object_or_404 |
|||
from django.test import TestCase |
|||
from models import Abon, AbonTariff |
|||
from tariff_app.models import Tariff |
|||
|
|||
|
|||
class AbonTariffTestCase(TestCase): |
|||
|
|||
def setUp(self): |
|||
abon1 = Abon.objects.create( |
|||
telephone='+79784653751', |
|||
fio=u'ФИО абона', |
|||
username='аго мучич' |
|||
) |
|||
tarif1 = Tariff.objects.create( |
|||
title=u'Тариф 1', |
|||
speedIn=120.3, |
|||
speedOut=53, |
|||
amount=38 |
|||
) |
|||
tarif2 = Tariff.objects.create( |
|||
title=u'Тариф 2', |
|||
speedIn=130.3, |
|||
speedOut=23, |
|||
amount=82 |
|||
) |
|||
AbonTariff.objects.create( |
|||
abon=abon1, |
|||
tariff=tarif1, |
|||
tariff_priority=0 |
|||
) |
|||
AbonTariff.objects.create( |
|||
abon=abon1, |
|||
tariff=tarif2, |
|||
tariff_priority=1 |
|||
) |
|||
|
|||
def test_activate_next(self): |
|||
# возьмём абонента для опытов |
|||
abn = get_object_or_404(Abon, username=u'аго мучич') |
|||
|
|||
# берём купленные услуги |
|||
ats = AbonTariff.objects.filter(abon=abn) |
|||
for at in ats: |
|||
|
|||
# и пробуем назначить |
|||
at.activate_next_tariff() |
|||
|
|||
AbonTariff.objects.update_priorities(ats) |
|||
@ -0,0 +1,21 @@ |
|||
from django.conf.urls import url, include |
|||
import views |
|||
|
|||
urlpatterns = [ |
|||
|
|||
url(r'^$', views.grouplist, name='abongroup_list_link'), |
|||
url(r'^addgroup$', views.addgroup, name='addgroup_link'), |
|||
url(r'^delgroup', views.delgroup, name='people_delgroup_link'), |
|||
|
|||
url(r'^(?P<gid>\d+)/', include('abonapp.urls_abon')), |
|||
|
|||
url(r'^log$', views.log_page, name='abonapp_log_link'), |
|||
|
|||
url(r'^del$', views.delentity, name='abonapp_del_link'), |
|||
|
|||
url(r'^pay$', views.terminal_pay, name='abonapp_terminalpay_link'), |
|||
|
|||
# Api's |
|||
url(r'^api/abons$', views.abons) |
|||
|
|||
] |
|||
@ -0,0 +1,19 @@ |
|||
from django.conf.urls import url |
|||
import views |
|||
|
|||
urlpatterns = [ |
|||
url(r'^$', views.peoples, name='pays_people_list_link'), |
|||
url(r'^addabon$', views.addabon, name='addabon_link'), |
|||
url(r'^(?P<uid>\d+)$', views.abonhome, name='abonhome_link'), |
|||
|
|||
url(r'^(?P<uid>\d+)/services$', views.abon_services, name='abon_services_link'), |
|||
url(r'^(?P<uid>\d+)/amount', views.abonamount, name='abon_amount_link'), |
|||
url(r'^(?P<uid>\d+)/debts', views.invoice_for_payment, name='abon_debts_link'), |
|||
url(r'^(?P<uid>\d+)/pay_history', views.pay_history, name='abon_phistory_link'), |
|||
|
|||
|
|||
url(r'^(?P<uid>\d+)/addinvoice$', views.add_invoice, name='abonapp_addinvoice_link'), |
|||
url(r'^(?P<uid>\d+)/buy$', views.buy_tariff, name='abonapp_buy_tariff'), |
|||
url(r'^(?P<uid>\d+)/chpriority$', views.chpriority, name='abonapp_chpriority_tariff'), |
|||
url(r'^(?P<uid>\d+)/complete_service_(?P<srvid>\d+)$', views.complete_service, name='abonapp_compl_srv'), |
|||
] |
|||
@ -0,0 +1,439 @@ |
|||
# -*- coding: utf-8 -*- |
|||
from json import dumps |
|||
from django.db import IntegrityError |
|||
from django.db.models import Count |
|||
from django.shortcuts import render, redirect, get_object_or_404 |
|||
from django.contrib.auth.decorators import login_required |
|||
from django.utils import timezone |
|||
from accounts_app.models import UserProfile |
|||
from ip_pool.models import IpPoolItem |
|||
from tariff_app.models import Tariff |
|||
from django.template.context_processors import csrf |
|||
from django.http import HttpResponse, Http404 |
|||
from agent import get_TransmitterClientKlass, NetExcept |
|||
import forms |
|||
import models |
|||
import mydefs |
|||
|
|||
|
|||
@login_required |
|||
def peoples(request, gid): |
|||
peopleslist = models.Abon.objects.filter(group=gid) |
|||
|
|||
peopleslist = mydefs.pag_mn(request, peopleslist) |
|||
|
|||
return render(request, 'abonapp/peoples.html', { |
|||
'peoples': peopleslist, |
|||
'gid': gid |
|||
}) |
|||
|
|||
|
|||
@login_required |
|||
# @permission_required('abonapp.add_abongroup') |
|||
def addgroup(request): |
|||
warntext = '' |
|||
frm = forms.AbonGroupForm() |
|||
if request.method == 'POST': |
|||
frm = forms.AbonGroupForm(request.POST) |
|||
if frm.is_valid(): |
|||
frm.save() |
|||
return redirect('abongroup_list_link') |
|||
else: |
|||
warntext = u'Исправьте ошибки' |
|||
return render(request, 'abonapp/addGroup.html', { |
|||
'csrf_token': csrf(request)['csrf_token'], |
|||
'form': frm, |
|||
'warntext': warntext |
|||
}) |
|||
|
|||
|
|||
@login_required |
|||
def grouplist(request): |
|||
groups = models.AbonGroup.objects.annotate(usercount=Count('abon')) |
|||
|
|||
groups = mydefs.pag_mn(request, groups) |
|||
|
|||
return render(request, 'abonapp/group_list.html', { |
|||
'groups': groups |
|||
}) |
|||
|
|||
|
|||
@login_required |
|||
def delgroup(request): |
|||
agd = mydefs.safe_int(request.GET.get('id')) |
|||
get_object_or_404(models.AbonGroup, id=agd).delete() |
|||
return mydefs.res_success(request, 'abongroup_list_link') |
|||
|
|||
|
|||
@login_required |
|||
# @permission_required('abonapp.add_abon') |
|||
# @permission_required('abonapp.change_abon') |
|||
def addabon(request, gid): |
|||
warntext = '' |
|||
frm = None |
|||
try: |
|||
grp = get_object_or_404(models.AbonGroup, id=gid) |
|||
if request.method == 'POST': |
|||
frm = forms.AbonForm(request.POST) |
|||
if frm.is_valid(): |
|||
prf = models.Abon() |
|||
prf.group = grp |
|||
prf.save_form(frm) |
|||
prf.save() |
|||
return redirect('pays_people_list_link', gid) |
|||
else: |
|||
warntext = u'Некоторые поля заполнены не правильно, проверте ещё раз' |
|||
|
|||
except IntegrityError, e: |
|||
warntext = "%s: %s" % (warntext, e) |
|||
|
|||
except models.LogicError as e: |
|||
warntext = e.value |
|||
|
|||
if not frm: |
|||
frm = forms.AbonForm(initial={ |
|||
'ip_address': IpPoolItem.objects.get_free_ip(), |
|||
'group': grp |
|||
}) |
|||
|
|||
return render(request, 'abonapp/addAbon.html', { |
|||
'warntext': warntext, |
|||
'csrf_token': csrf(request)['csrf_token'], |
|||
'form': frm, |
|||
'gid': gid |
|||
}) |
|||
|
|||
|
|||
@login_required |
|||
# @permission_required('abonapp.delete_abon') |
|||
# @permission_required('abonapp.delete_abongroup') |
|||
def delentity(request): |
|||
typ = request.GET.get('t') |
|||
uid = request.GET.get('id') |
|||
|
|||
if typ == 'a': |
|||
abon = get_object_or_404(models.Abon, id=uid) |
|||
abon.delete() |
|||
return mydefs.res_success(request, 'pays_people_list_link') |
|||
elif typ == 'g': |
|||
get_object_or_404(models.AbonGroup, id=uid).delete() |
|||
return mydefs.res_success(request, 'pays_people_list_link') |
|||
|
|||
|
|||
@login_required |
|||
def abonamount(request, gid, uid): |
|||
warntext='' |
|||
if request.method == 'POST': |
|||
abonid = mydefs.safe_int(request.POST.get('abonid')) |
|||
if abonid == int(uid): |
|||
amnt = mydefs.safe_float(request.POST.get('amount')) |
|||
abon = get_object_or_404(models.Abon, id=abonid) |
|||
abon.add_ballance(request.user, amnt) |
|||
abon.save() |
|||
return redirect('abonhome_link', gid=gid, uid=uid) |
|||
else: |
|||
warntext = u'Не правильно выбран абонент как цель для пополнения' |
|||
return render(request, 'abonapp/abonamount.html', { |
|||
'abon_id': int(uid), |
|||
'gid': gid, |
|||
'warntext': warntext |
|||
}) |
|||
|
|||
|
|||
@login_required |
|||
def invoice_for_payment(request, gid, uid): |
|||
#abon = get_object_or_404(models.Abon, id=uid) |
|||
invoices = models.InvoiceForPayment.objects.filter(abon_id=uid) |
|||
invoices = mydefs.pag_mn(request, invoices) |
|||
return render(request, 'abonapp/invoiceForPayment.html', { |
|||
'invoices': invoices, |
|||
'gid': gid, |
|||
'abon_id': uid |
|||
}) |
|||
|
|||
|
|||
@login_required |
|||
def pay_history(request, gid, uid): |
|||
#abon = get_object_or_404(models.Abon, id=uid) |
|||
pay_history = models.AbonLog.objects.filter(abon_id=uid).order_by('-id') |
|||
pay_history = mydefs.pag_mn(request, pay_history) |
|||
|
|||
return render(request, 'abonapp/payHistory.html', { |
|||
'pay_history': pay_history, |
|||
'gid': gid, |
|||
'abon_id': uid |
|||
}) |
|||
|
|||
|
|||
@login_required |
|||
def abon_services(request, gid, uid): |
|||
#abon = get_object_or_404(models.Abon, id=uid) |
|||
abon_tarifs = models.AbonTariff.objects.filter(abon_id=uid).order_by('tariff_priority') |
|||
|
|||
return render(request, 'abonapp/services.html', { |
|||
'abon_id': uid, |
|||
'abon_tarifs': abon_tarifs, |
|||
'active_abontariff_id': abon_tarifs[0].id if abon_tarifs.count() > 0 else None, |
|||
'gid': gid |
|||
}) |
|||
|
|||
|
|||
@login_required |
|||
def abonhome(request, gid, uid): |
|||
abon = get_object_or_404(models.Abon, id=uid) |
|||
warntext = '' |
|||
ballance = abon.ballance |
|||
frm = None |
|||
init_frm_dat = { |
|||
'username': abon.username, |
|||
'fio': abon.fio, |
|||
'ip_address': abon.ip_address or u'Не назначен', |
|||
'telephone': abon.telephone, |
|||
'group': abon.group, |
|||
'address': abon.address, |
|||
'is_active': abon.is_active |
|||
} |
|||
|
|||
try: |
|||
if request.method == 'POST': |
|||
frm = forms.AbonForm(request.POST) |
|||
if frm.is_valid(): |
|||
cd = frm.cleaned_data |
|||
abon.username = cd['username'] |
|||
abon.fio = cd['fio'] |
|||
|
|||
ip_address = abon.ip_address |
|||
abon.ip_address = get_object_or_404(IpPoolItem, ip=cd['ip_address']) |
|||
|
|||
abon.telephone = cd['telephone'] |
|||
abon.group = cd['group'] |
|||
abon.address = cd['address'] |
|||
abisactive = abon.is_active |
|||
abon.is_active = 1 if cd['is_active'] else 0 |
|||
abon.save() |
|||
|
|||
# Если включили то шлём событие от этом |
|||
if cd['is_active'] and not abisactive: |
|||
tc = get_TransmitterClientKlass()() |
|||
tc.signal_abon_enable(abon) |
|||
|
|||
# Если выключили |
|||
elif not cd['is_active'] and abisactive: |
|||
tc = get_TransmitterClientKlass()() |
|||
tc.signal_abon_disable(abon) |
|||
|
|||
# Если изменили инфу, важную для NAS то говорим NAS'у перечитать инфу об абоненте |
|||
if abon.ip_address != ip_address: |
|||
tc = get_TransmitterClientKlass()() |
|||
tc.signal_abon_refresh_info(abon) |
|||
|
|||
#return redirect('abonhome_link', gid, uid) |
|||
else: |
|||
warntext = u'Не правильные значения, проверте поля и попробуйте ещё' |
|||
else: |
|||
frm = forms.AbonForm(initial=init_frm_dat) |
|||
except IntegrityError, e: |
|||
warntext = u'Проверте введённые вами значения, скорее всего такой ip уже у кого-то есть. А вообще: %s' % e |
|||
frm = forms.AbonForm(initial=init_frm_dat) |
|||
|
|||
except Http404: |
|||
warntext = u'Ip адрес не найден в списке IP адресов' |
|||
frm = forms.AbonForm(initial=init_frm_dat) |
|||
|
|||
except NetExcept as e: |
|||
warntext = e.value |
|||
|
|||
return render(request, 'abonapp/editAbon.html', { |
|||
'warntext': warntext, |
|||
'form': frm or forms.AbonForm(initial=init_frm_dat), |
|||
'abon_id': uid, |
|||
'ballance': ballance, |
|||
'gid': gid |
|||
}) |
|||
|
|||
|
|||
def terminal_pay(request): |
|||
username = request.GET.get('username') |
|||
amount = mydefs.safe_float(request.GET.get('amount')) |
|||
|
|||
kernel_user = get_object_or_404(UserProfile, username='kernel') |
|||
abon = get_object_or_404(models.Abon, username=username) |
|||
|
|||
abon.add_ballance(kernel_user, amount) |
|||
|
|||
abon.save() |
|||
return HttpResponse('ok') |
|||
|
|||
|
|||
@login_required |
|||
# @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) |
|||
|
|||
if request.method == 'POST': |
|||
curr_amount = mydefs.safe_int(request.POST.get('curr_amount')) |
|||
comment = request.POST.get('comment') |
|||
|
|||
newinv = models.InvoiceForPayment() |
|||
newinv.abon = abon |
|||
newinv.amount = curr_amount |
|||
newinv.comment = comment |
|||
|
|||
if request.POST.get('status') == u'on': |
|||
newinv.status = True |
|||
|
|||
newinv.author = request.user |
|||
newinv.save() |
|||
return redirect('abonhome_link', gid=gid, uid=uid) |
|||
else: |
|||
return render(request, 'abonapp/addInvoice.html', { |
|||
'csrf_token': csrf(request)['csrf_token'], |
|||
'abon': abon, |
|||
'invcount': models.InvoiceForPayment.objects.filter(abon=abon).count(), |
|||
'gid': gid |
|||
}) |
|||
|
|||
|
|||
@login_required |
|||
def buy_tariff(request, gid, uid): |
|||
warntext = '' |
|||
frm = None |
|||
try: |
|||
if request.method == 'POST': |
|||
frm = forms.BuyTariff(request.POST) |
|||
if frm.is_valid(): |
|||
cd = frm.cleaned_data |
|||
abon = get_object_or_404(models.Abon, id=uid) |
|||
abon.buy_tariff(cd['tariff'], request.user) |
|||
abon.save() |
|||
return redirect('abonhome_link', uid=uid) |
|||
else: |
|||
warntext = u'Что-то не так при покупке услуги, проверьте и попробуйте ещё' |
|||
else: |
|||
frm = forms.BuyTariff() |
|||
except models.LogicError as e: |
|||
warntext = e.value |
|||
|
|||
except NetExcept as e: |
|||
warntext = e.value |
|||
|
|||
return render(request, 'abonapp/buy_tariff.html', { |
|||
'warntext': warntext, |
|||
'form': frm or forms.BuyTariff(), |
|||
'uid': uid, |
|||
'gid': gid |
|||
}) |
|||
|
|||
|
|||
@login_required |
|||
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) |
|||
|
|||
if act == 'up': |
|||
current_abon_tariff.priority_up() |
|||
elif act == 'down': |
|||
current_abon_tariff.priority_down() |
|||
|
|||
return redirect('abonhome_link', gid=gid, uid=uid) |
|||
|
|||
|
|||
@login_required |
|||
def complete_service(request, gid, uid, srvid): |
|||
abtar = get_object_or_404(models.AbonTariff, id=srvid) |
|||
|
|||
if abtar.abon.id != int(uid): |
|||
return HttpResponse('<h1>uid not equal uid from service</h1>') |
|||
|
|||
try: |
|||
if request.method == 'POST': |
|||
# досрочно завершаем услугу |
|||
try: |
|||
abtar.finish_and_activate_next_tariff(request.user) |
|||
# завершаем текущую услугу. |
|||
abtar.delete() |
|||
|
|||
except models.LogicError: |
|||
# Значит у абонента нет следующих услуг. Сигналим о закрытии инета в NAS |
|||
tc = get_TransmitterClientKlass()() |
|||
tc.signal_abon_close_inet(abtar.abon) |
|||
|
|||
# Переупорядочиваем приоритеты |
|||
models.AbonTariff.objects.update_priorities(abtar.abon) |
|||
|
|||
return redirect('abonhome_link', gid, uid) |
|||
|
|||
next_tariff = models.AbonTariff.objects.filter( |
|||
abon=abtar.abon, |
|||
tariff_priority__gt=abtar.tariff_priority |
|||
)[:1] |
|||
|
|||
if not abtar.time_start: |
|||
abtar.time_start = timezone.now() |
|||
abtar.save() |
|||
|
|||
time_use = timezone.now() - abtar.time_start |
|||
time_use = { |
|||
'days': time_use.days, |
|||
'hours': time_use.seconds / 3600, |
|||
'minutes': time_use.seconds / 60 % 60 |
|||
} |
|||
return render(request, 'abonapp/complete_service.html', { |
|||
'csrf_token': csrf(request)['csrf_token'], |
|||
'abtar': abtar, |
|||
'uid': uid, |
|||
'next_tariff': next_tariff[0] if next_tariff.count() > 0 else None, |
|||
'time_use': time_use, |
|||
'gid': gid |
|||
}) |
|||
|
|||
except models.LogicError as e: |
|||
warntext = e.value |
|||
|
|||
except NetExcept as e: |
|||
warntext = e.value |
|||
|
|||
return render(request, 'abonapp/complete_service.html', { |
|||
'csrf_token': csrf(request)['csrf_token'], |
|||
'abtar': abtar, |
|||
'uid': uid, |
|||
'warntext': warntext |
|||
}) |
|||
|
|||
|
|||
@login_required |
|||
def log_page(request): |
|||
logs = models.AbonLog.objects.all() |
|||
|
|||
logs = mydefs.pag_mn(request, logs) |
|||
|
|||
return render(request, 'abonapp/log.html', { |
|||
'logs': logs |
|||
}) |
|||
|
|||
|
|||
# API's |
|||
|
|||
def abons(request): |
|||
ablist = map(lambda abn: { |
|||
'id': abn.id, |
|||
'tarif_id': abn.active_tariff().id if abn.active_tariff() else 0, |
|||
'ip': abn.ip_address.int_ip(), |
|||
'is_active': abn.is_active |
|||
}, models.Abon.objects.all()) |
|||
|
|||
tarlist = map(lambda trf: { |
|||
'id': trf.id, |
|||
'speedIn': trf.speedIn, |
|||
'speedOut': trf.speedOut |
|||
}, Tariff.objects.all()) |
|||
|
|||
data = { |
|||
'subscribers': ablist, |
|||
'tariffs': tarlist |
|||
} |
|||
del ablist, tarlist |
|||
return HttpResponse(dumps(data)) |
|||
@ -0,0 +1,4 @@ |
|||
from django.contrib import admin |
|||
from models import UserProfile |
|||
|
|||
admin.site.register(UserProfile) |
|||
@ -0,0 +1,42 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.9 on 2016-06-28 23:51 |
|||
from __future__ import unicode_literals |
|||
|
|||
import django.core.validators |
|||
from django.db import migrations, models |
|||
import django.db.models.deletion |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
initial = True |
|||
|
|||
dependencies = [ |
|||
('auth', '0007_alter_validators_add_error_messages'), |
|||
('photo_app', '0001_initial'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.CreateModel( |
|||
name='UserProfile', |
|||
fields=[ |
|||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
|||
('password', models.CharField(max_length=128, verbose_name='password')), |
|||
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), |
|||
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), |
|||
('username', models.CharField(max_length=127, unique=True)), |
|||
('fio', models.CharField(max_length=256)), |
|||
('birth_day', models.DateField(auto_now_add=True)), |
|||
('is_active', models.BooleanField(default=True)), |
|||
('is_admin', models.BooleanField(default=False)), |
|||
('telephone', models.CharField(max_length=16, unique=True, validators=[django.core.validators.RegexValidator(b'^\\+[7,8,9,3]\\d{10,11}$')], verbose_name=b'Telephone number')), |
|||
('skype', models.CharField(blank=True, max_length=20)), |
|||
('avatar', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='photo_app.Photo')), |
|||
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')), |
|||
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')), |
|||
], |
|||
options={ |
|||
'abstract': False, |
|||
}, |
|||
), |
|||
] |
|||
@ -0,0 +1,107 @@ |
|||
# -*- coding:utf-8 -*- |
|||
from django.db import models |
|||
from djing.settings import DEFAULT_PICTURE |
|||
from photo_app.models import Photo |
|||
from django.contrib.auth.models import BaseUserManager, AbstractBaseUser, PermissionsMixin |
|||
from django.core.validators import RegexValidator |
|||
|
|||
|
|||
class MyUserManager(BaseUserManager): |
|||
|
|||
def create_user(self, telephone, username, password=None): |
|||
""" |
|||
Creates and saves a User with the given email, date of |
|||
birth and password. |
|||
""" |
|||
if not telephone: |
|||
raise ValueError('Users must have an telephone number') |
|||
|
|||
user = self.model( |
|||
telephone=telephone, |
|||
username=username, |
|||
) |
|||
|
|||
user.set_password(password) |
|||
user.save(using=self._db) |
|||
return user |
|||
|
|||
def create_superuser(self, telephone, username, password): |
|||
""" |
|||
Creates and saves a superuser with the given email, date of |
|||
birth and password. |
|||
""" |
|||
user = self.create_user(telephone, |
|||
password=password, |
|||
username=username |
|||
) |
|||
user.is_admin = True |
|||
user.is_superuser = True |
|||
user.save(using=self._db) |
|||
return user |
|||
|
|||
|
|||
class UserProfile(AbstractBaseUser, PermissionsMixin): |
|||
username = models.CharField(max_length=127, unique=True) |
|||
fio = models.CharField(max_length=256) |
|||
birth_day = models.DateField(auto_now_add=True) |
|||
is_active = models.BooleanField(default=True) |
|||
is_admin = models.BooleanField(default=False) |
|||
telephone = models.CharField( |
|||
max_length=16, |
|||
verbose_name='Telephone number', |
|||
unique=True, |
|||
validators=[RegexValidator(r'^\+[7,8,9,3]\d{10,11}$')] |
|||
) |
|||
skype = models.CharField(max_length=20, blank=True) |
|||
avatar = models.ForeignKey(Photo, null=True, blank=True) |
|||
|
|||
USERNAME_FIELD = 'username' |
|||
REQUIRED_FIELDS = ['telephone'] |
|||
|
|||
def get_full_name(self): |
|||
if self.fio: |
|||
return "%s: %s" % (self.username, self.fio) |
|||
else: |
|||
return self.username |
|||
|
|||
def get_short_name(self): |
|||
return self.username or self.telephone |
|||
|
|||
|
|||
# Use UserManager to get the create_user method, etc. |
|||
objects = MyUserManager() |
|||
|
|||
@property |
|||
def is_staff(self): |
|||
"Is the user a member of staff?" |
|||
# Simplest possible answer: All admins are staff |
|||
return self.is_admin |
|||
|
|||
def get_big_ava(self): |
|||
if self.avatar: |
|||
return self.avatar.big() |
|||
else: |
|||
return DEFAULT_PICTURE |
|||
|
|||
def get_min_ava(self): |
|||
if self.avatar: |
|||
return self.avatar.min() |
|||
else: |
|||
return DEFAULT_PICTURE |
|||
|
|||
def __unicode__(self): |
|||
return self.username |
|||
|
|||
|
|||
#from django.db.models.signals import post_save |
|||
|
|||
|
|||
'''def create_custom_user(sender, instance, created, **kwargs): |
|||
if created: |
|||
values = {} |
|||
for field in sender._meta.local_fields: |
|||
values[field.attname] = getattr(instance, field.attname) |
|||
user = UserProfile(**values) |
|||
user.save()''' |
|||
|
|||
#post_save.connect(create_custom_user, User) |
|||
@ -0,0 +1,16 @@ |
|||
""" |
|||
This file demonstrates writing tests using the unittest module. These will pass |
|||
when you run "manage.py test". |
|||
|
|||
Replace this with more appropriate tests for your application. |
|||
""" |
|||
|
|||
from django.test import TestCase |
|||
|
|||
|
|||
class SimpleTest(TestCase): |
|||
def test_basic_addition(self): |
|||
""" |
|||
Tests that 1 + 1 always equals 2. |
|||
""" |
|||
self.assertEqual(1 + 1, 2) |
|||
@ -0,0 +1,27 @@ |
|||
# -*- coding:utf-8 -*- |
|||
from django.conf.urls import url |
|||
import views |
|||
|
|||
urlpatterns = [ |
|||
|
|||
url(r'^login/', views.to_signin, name='login_link'), |
|||
url(r'^logout/', views.sign_out, name='logout_link'), |
|||
|
|||
url(r'^me$', views.profile_show, name='profile'), |
|||
|
|||
url(r'^$', views.acc_list, name='accounts_list'), |
|||
|
|||
url(r'^add$', views.create_profile, name='create_profile_link'), |
|||
|
|||
url(r'^settings$', views.ch_info, name='settings_chinfo_link'), |
|||
url(r'^settings/change_ava$', views.ch_ava, name='settings_chava_link'), |
|||
|
|||
url(r'^(?P<id>\d+)$', views.profile_show, name='other_profile'), |
|||
url(r'^(?P<id>\d+)/perms$', views.perms, name='profile_perms_link'), |
|||
url(r'^(?P<uid>\d+)/chgroup$', views.chgroup, name='profile_chgroup_link'), |
|||
url(r'^(?P<uid>\d+)/del$', views.delete_profile, name='delete_profile_link'), |
|||
|
|||
url(r'^group/$', views.groups, name='profile_groups_list'), |
|||
url(r'^group/(?P<id>\d+)$', views.group, name='profile_group_link') |
|||
|
|||
] |
|||
@ -0,0 +1,258 @@ |
|||
# -*- coding: utf-8 -*- |
|||
from django.contrib.auth.decorators import login_required#, permission_required |
|||
from django.contrib.auth import authenticate, login, logout |
|||
from photo_app.models import Photo |
|||
from django.core.urlresolvers import NoReverseMatch |
|||
from models import UserProfile |
|||
from django.shortcuts import render, redirect, get_object_or_404 |
|||
from django.template.context_processors import csrf |
|||
from django.http import Http404 |
|||
from django.contrib.auth.models import Group, Permission |
|||
import mydefs |
|||
|
|||
|
|||
@login_required |
|||
def home(request): |
|||
return redirect('profile') |
|||
|
|||
|
|||
def to_signin(request): |
|||
nextl = request.GET.get('next') |
|||
|
|||
try: |
|||
if request.POST: |
|||
auser = authenticate(username=request.POST.get('login'), password=request.POST.get('password')) |
|||
if auser: |
|||
login(request, auser) |
|||
if nextl == 'None' or nextl == None or nextl == '': |
|||
return redirect('profile') |
|||
else: |
|||
return redirect(nextl) |
|||
else: |
|||
return render(request, 'accounts/login.html', { |
|||
'csrf_token': csrf(request)['csrf_token'], |
|||
'next': nextl, |
|||
'errmsg': u'Неправильный логин или пароль, попробуйте ещё раз' |
|||
}) |
|||
except NoReverseMatch: |
|||
raise Http404(u"Destination page does not exist") |
|||
return render(request, 'accounts/login.html', { |
|||
'csrf_token': csrf(request)['csrf_token'], |
|||
'next': nextl |
|||
}) |
|||
|
|||
|
|||
def sign_out(request): |
|||
logout(request) |
|||
return redirect('login_link') |
|||
|
|||
|
|||
@login_required |
|||
def profile_show(request, id=0): |
|||
id = mydefs.safe_int(id) |
|||
|
|||
if id == 0: |
|||
usr = request.user |
|||
else: |
|||
usr = get_object_or_404(UserProfile, id=id) |
|||
|
|||
if request.method == 'POST': |
|||
usr.username = request.POST.get('username') |
|||
usr.fio = request.POST.get('fio') |
|||
usr.telephone = request.POST.get('telephone') |
|||
usr.is_active = request.POST.get('stat') |
|||
usr.is_admin = request.POST.get('is_admin') |
|||
usr.save() |
|||
return redirect('other_profile', id=id) |
|||
|
|||
return render(request, 'accounts/index.html', { |
|||
'uid': id, |
|||
'userprofile': usr |
|||
}) |
|||
|
|||
|
|||
@login_required |
|||
def chgroup(request, uid): |
|||
usr = get_object_or_404(UserProfile, id=uid) |
|||
usergroups = usr.groups.all() |
|||
othergroups = filter(lambda g: g not in usergroups, Group.objects.all()) |
|||
#Group.objects.exclude(user__in=usergroups) |
|||
|
|||
return render(request, 'accounts/profile_chgroup.html', { |
|||
'uid': uid, |
|||
'userprofile': usr, |
|||
'allgroups': othergroups, |
|||
'usergroups': usergroups |
|||
}) |
|||
|
|||
|
|||
@login_required |
|||
def ch_ava(request): |
|||
if request.method == 'POST': |
|||
user = request.user |
|||
if user.avatar: |
|||
user.avatar.delete() |
|||
photo = Photo() |
|||
photo.image = request.FILES.get('avatar') |
|||
photo.save() |
|||
user.avatar = photo |
|||
user.save() |
|||
request.user = user |
|||
|
|||
return render(request, 'accounts/settings/ch_info.html', { |
|||
'user': request.user |
|||
}) |
|||
|
|||
|
|||
@login_required |
|||
def ch_info(request): |
|||
warntext='' |
|||
if request.method == 'POST': |
|||
user = request.user |
|||
user.username = request.POST.get('username') |
|||
user.fio = request.POST.get('fio') |
|||
user.email = request.POST.get('email') |
|||
user.telephone = request.POST.get('telephone') |
|||
user.skype = request.POST.get('skype') |
|||
|
|||
psw = request.POST.get('oldpasswd') |
|||
if psw != '': |
|||
if user.check_password(psw): |
|||
newpasswd = request.POST.get('newpasswd') |
|||
user.set_password(newpasswd) |
|||
else: |
|||
warntext = u'Неправильный пароль' |
|||
user.save() |
|||
request.user = user |
|||
|
|||
return render(request, 'accounts/settings/ch_info.html', { |
|||
'user': request.user, |
|||
'warntext': warntext |
|||
}) |
|||
|
|||
|
|||
@login_required |
|||
##@permission_required('accounts_app.add_userprofile') |
|||
def create_profile(request): |
|||
if request.method == 'POST': |
|||
username = request.POST.get('username') |
|||
|
|||
user = UserProfile() |
|||
user.username = username |
|||
user.fio = request.POST.get('fio') |
|||
user.email = request.POST.get('email') |
|||
user.telephone = request.POST.get('telephone') |
|||
user.skype = request.POST.get('skype') |
|||
user.is_admin = True |
|||
|
|||
passwd = request.POST.get('passwd') |
|||
conpasswd = request.POST.get('conpasswd') |
|||
if not passwd: |
|||
return render(request, 'accounts/create_acc.html',{ |
|||
'warntext': u'Забыли указать пароль для нового аккаунта', |
|||
'csrf_token': csrf(request)['csrf_token'], |
|||
'newuser': user |
|||
}) |
|||
if not conpasswd: |
|||
return render(request, 'accounts/create_acc.html',{ |
|||
'warntext': u'Забыли повторить пароль для нового аккаунта', |
|||
'csrf_token': csrf(request)['csrf_token'], |
|||
'newuser': user |
|||
}) |
|||
|
|||
if passwd == conpasswd: |
|||
user_qs = UserProfile.objects.filter(username=username)[:1] |
|||
if user_qs.count() == 0: |
|||
user.set_password(passwd) |
|||
user.save() |
|||
return redirect('accounts_list') |
|||
else: |
|||
return render(request, 'accounts/create_acc.html',{ |
|||
'warntext': u'Пользователь с таким именем уже есть', |
|||
'csrf_token': csrf(request)['csrf_token'], |
|||
'newuser': user |
|||
}) |
|||
else: |
|||
return render(request, 'accounts/create_acc.html',{ |
|||
'warntext': u'Пароли не совпадают, попробуйте ещё раз', |
|||
'csrf_token': csrf(request)['csrf_token'], |
|||
'newuser': user |
|||
}) |
|||
return render(request, 'accounts/create_acc.html', {'csrf_token': csrf(request)['csrf_token'],}) |
|||
|
|||
|
|||
@login_required |
|||
#@permission_required('accounts_app.del_userprofile') |
|||
def delete_profile(request, uid): |
|||
prf = get_object_or_404(UserProfile, id=uid) |
|||
prf.delete() |
|||
return redirect('accounts_list') |
|||
|
|||
|
|||
@login_required |
|||
def acc_list(request): |
|||
users = UserProfile.objects.filter(is_admin=True) |
|||
|
|||
users = mydefs.pag_mn(request, users) |
|||
|
|||
return render(request, 'accounts/acc_list.html', { |
|||
'users': users |
|||
}) |
|||
|
|||
|
|||
@login_required |
|||
#@permission_required('accounts_app.change_userprofile') |
|||
def perms(request, id): |
|||
ingroups = filter(lambda x: x[0] == 'ingroups', request.POST.lists())[0][1] |
|||
id = mydefs.safe_int(id) |
|||
|
|||
profile = get_object_or_404(UserProfile, id=id) |
|||
profile.groups.clear() |
|||
|
|||
for group_id in ingroups: |
|||
gid = mydefs.safe_int(group_id) |
|||
profile.groups.add(gid) |
|||
profile.save() |
|||
|
|||
return redirect('other_profile', id) |
|||
|
|||
|
|||
@login_required |
|||
def groups(request): |
|||
grps = Group.objects.all() |
|||
|
|||
grps = mydefs.pag_mn(request, grps) |
|||
|
|||
return render(request, 'accounts/group_list.html', { |
|||
'groups': grps |
|||
}) |
|||
|
|||
|
|||
@login_required |
|||
#@permission_required('auth.change_group') |
|||
def group(request, id): |
|||
id = mydefs.safe_int(id) |
|||
grp = get_object_or_404(Group, id=id) |
|||
|
|||
if request.method == 'POST': |
|||
group_rights = filter(lambda x: x[0] == 'group_rights', request.POST.lists())[0][1] |
|||
grp.permissions.clear() |
|||
for grr in group_rights: |
|||
rid = mydefs.safe_int(grr) |
|||
grp.permissions.add(rid) |
|||
grp.save() |
|||
return redirect('profile_group_link', id=id) |
|||
|
|||
grp_rights = grp.permissions.all() |
|||
all_rights = Permission.objects.exclude(group=grp) |
|||
|
|||
#prms = Permission.objects.all() |
|||
#for pr in prms: |
|||
# print u"%s | %s" % (pr.name, pr.codename) |
|||
|
|||
return render(request, 'accounts/group.html', { |
|||
'csrf_token': csrf(request)['csrf_token'], |
|||
'group': grp, |
|||
'all_rights': all_rights, |
|||
'grp_rights': grp_rights |
|||
}) |
|||
@ -0,0 +1,12 @@ |
|||
#!/bin/env python |
|||
|
|||
import os |
|||
from agent import main |
|||
|
|||
|
|||
if __name__ == "__main__": |
|||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djing.settings") |
|||
|
|||
while True: |
|||
main(debug=True) |
|||
print "Exit from main, reload..." |
|||
@ -0,0 +1,3 @@ |
|||
from models import Abonent, Tariff |
|||
from main import main |
|||
from sslTransmitter import get_TransmitterClientKlass, NetExcept |
|||
@ -0,0 +1,27 @@ |
|||
# -*- coding:utf-8 -*- |
|||
import requests |
|||
from json import loads |
|||
from models import deserialize_tariffs, deserialize_abonents |
|||
import settings |
|||
|
|||
|
|||
def load_from_db(): |
|||
r = requests.get('%s://%s:%d/abons/api/abons' % ( |
|||
'https' if settings.IS_USE_SSL else 'http', |
|||
settings.SERVER_IP, |
|||
settings.SERVER_PORT |
|||
), verify=False) |
|||
try: |
|||
user_data = loads(r.text) |
|||
del r |
|||
# Получаем тарифы |
|||
tariffs = deserialize_tariffs(user_data) |
|||
|
|||
# Получаем пользователей |
|||
abons = deserialize_abonents(user_data, tariffs) |
|||
|
|||
return abons, tariffs |
|||
|
|||
except ValueError as e: |
|||
print 'Error:', e, r.text |
|||
return |
|||
@ -0,0 +1,48 @@ |
|||
# -*- coding:utf-8 -*- |
|||
|
|||
|
|||
class FirewallManager(object): |
|||
|
|||
f = r'/sbin/ipfw -q' |
|||
|
|||
# вызывает комманду shell |
|||
def exec_cmd(self, cmd): |
|||
print cmd |
|||
#os.execv(cmd, ['']) |
|||
|
|||
# ставит заглушку на абонента |
|||
def set_cap(self, user): |
|||
pass |
|||
|
|||
# Открывает доступ в интернет |
|||
def open_inet_door(self, user): |
|||
if not user.tariff: |
|||
print u'WARNING: User does not have a tariff' |
|||
return |
|||
cmd = r"%s table 12 add %s/32 %d && %s table 13 add %s/32 %d" % ( |
|||
self.f, user.ip_str(), user.tariff.tid, |
|||
self.f, user.ip_str(), user.tariff.tid+1000 |
|||
) |
|||
self.exec_cmd(cmd) |
|||
|
|||
# Закрывает доступ в интернет |
|||
def close_inet_door(self, user): |
|||
cmd = r"%s table 12 del %s/32 && %s table 13 del %s/32" % ( |
|||
self.f, user.ip_str(), |
|||
self.f, user.ip_str() |
|||
) |
|||
self.exec_cmd(cmd) |
|||
|
|||
# Создаёт тариф (пайпы, режущие скорость |
|||
def make_tariff(self, tariff): |
|||
cmd = r"make ipfw tariff :)" |
|||
self.exec_cmd(cmd) |
|||
|
|||
# Убирает тариф из фаервола |
|||
def destroy_tariff(self, tariff): |
|||
cmd = r"destroy ipfw tariff :)" |
|||
self.exec_cmd(cmd) |
|||
|
|||
def reset(self): |
|||
cmd = r"%s -f flush && %s table all flush" % (self.f, self.f) |
|||
self.exec_cmd(cmd) |
|||
@ -0,0 +1,72 @@ |
|||
#!/bin/sh |
|||
|
|||
######################################################### |
|||
# ВАЖНО! Биллинг пока ограничен количеством тарифных планов |
|||
# не больше 1000 |
|||
######################################################### |
|||
|
|||
|
|||
|
|||
|
|||
f="/sbin/ipfw -q" |
|||
|
|||
lan=em1 # Clients |
|||
wan=em0 # Inet |
|||
|
|||
|
|||
${f} -f flush |
|||
${f} table all flush |
|||
|
|||
sysctl net.inet.ip.fw.one_pass=0 |
|||
|
|||
# dns |
|||
${f} table 100 add 8.8.8.8 # google public dns |
|||
${f} table 100 add 8.8.4.4 # google public dns2 |
|||
${f} table 100 add 77.88.8.8 # yandex base dns |
|||
${f} table 100 add 77.88.8.1 # yandex base dns2 |
|||
|
|||
|
|||
# ssh access |
|||
${f} add 50 allow tcp from any to me 22 |
|||
${f} add 51 allow tcp from me 22 to any |
|||
|
|||
|
|||
# loopback |
|||
${f} add 100 allow ip from any to any via lo0 |
|||
|
|||
|
|||
# в таблице 100 приоритетный траффик. |
|||
# это dns, платёжки.. |
|||
${f} add 500 allow ip from table\(100\) to any |
|||
${f} add 501 allow ip from any to table\(100\) |
|||
|
|||
|
|||
|
|||
# в таблице 10 разрешённые пользователи |
|||
# блокируем трафик всем кто не в ней |
|||
${f} add 1001 deny ip from not table\(10\) to any via $wan |
|||
|
|||
# если у абонентов есть внешние адреса (не через NAT) |
|||
#${f} add 1101 deny ip from any to not table\(10\) via $wan |
|||
|
|||
|
|||
|
|||
|
|||
# по 2 пайпа на тарифный план, на вход и выход |
|||
#${f} pipe 212 config bw 1152Kbit/s mask src-ip 0xffffffff noerror |
|||
#${f} pipe 213 config bw 1152Kbit/s mask dst-ip 0xffffffff noerror |
|||
|
|||
# добавляем пайпы в таблицу |
|||
${f} add 2001 pipe 212 ip from table\(10\) to any via $wan |
|||
${f} add 2002 pipe 213 ip from any to table\(11\) via $wan |
|||
|
|||
#---------------------- |
|||
# так добавляем абонентов чтоб резать скорость, надо указать номер их пайпа |
|||
#${f} table 10 add 10.0.172.138/32 212 |
|||
#${f} table 11 add 10.0.172.138/32 2212 |
|||
#---------------------- |
|||
|
|||
|
|||
|
|||
|
|||
# тут будем поджимать пользователей когда не хватает канала |
|||
@ -0,0 +1,120 @@ |
|||
# -*- coding:utf-8 -*- |
|||
from sys import stdout |
|||
from db import load_from_db |
|||
from firewall import FirewallManager |
|||
from time import sleep |
|||
from sslTransmitter import TransmitServer |
|||
|
|||
|
|||
def filter_user_by_id(users, uid): |
|||
usrs = filter(lambda usr: usr.uid == uid, users) |
|||
if len(usrs) > 0: |
|||
return usrs[0] |
|||
else: |
|||
return |
|||
|
|||
|
|||
def main(debug=False): |
|||
users, tariffs = load_from_db() |
|||
frw = FirewallManager() |
|||
frw.reset() |
|||
|
|||
# Инициализация абонентов |
|||
if debug: |
|||
print u'Инициализация...' |
|||
# Открываем доступ в инет тем кто активен и у кого подключён тариф |
|||
for usr in filter(lambda usr: usr.is_active, users): |
|||
|
|||
# Доступ в интернет происходит по наличию подключённого тарифа |
|||
# если тарифа нет, то и инета нет |
|||
if usr.tariff: |
|||
# Открываем доступ в инет |
|||
frw.open_inet_door(usr) |
|||
|
|||
# Слушем в отдельном процессе сеть на предмет событий |
|||
ts = TransmitServer('127.0.0.1', 2134) |
|||
ts.start() |
|||
|
|||
if debug: |
|||
print u"Загружено %d абонентов" % len(users) |
|||
|
|||
while True: |
|||
|
|||
# Загружаем события для абонентов из сети (список объектов EventNAS из models) |
|||
events = ts.get_data() |
|||
# Проходим по появившимся событиям |
|||
for event in events: |
|||
#event.toa |
|||
#event.id |
|||
#event.dt |
|||
|
|||
# Смотрим тип события |
|||
toa = int(event.toa) |
|||
if toa == 0: continue |
|||
|
|||
# Включаем абонента |
|||
elif toa == 1: |
|||
usr = filter_user_by_id(users, event.id) |
|||
# Открываем доступ в инет |
|||
frw.open_inet_door(usr) |
|||
|
|||
# Выключаем абонента |
|||
elif toa == 2: |
|||
usr = filter_user_by_id(users, event.id) |
|||
# Выключаем интернет |
|||
frw.close_inet_door(usr) |
|||
|
|||
# Сообщение на заглушку |
|||
elif toa == 3: |
|||
usr = filter_user_by_id(users, event.id) |
|||
# Ставим заглушку |
|||
frw.set_cap(usr) |
|||
# Выключаем интернет |
|||
frw.close_inet_door(usr) |
|||
|
|||
# Открываем доступ в инет |
|||
elif toa == 4: |
|||
usr = filter_user_by_id(users, event.id) |
|||
frw.close_inet_door(usr) |
|||
frw.open_inet_door(usr) |
|||
|
|||
# Закрываем доступ в инет |
|||
elif toa == 5: |
|||
usr = filter_user_by_id(users, event.id) |
|||
frw.close_inet_door(usr) |
|||
|
|||
elif toa == 6: |
|||
# Сигнал на перезагрузку |
|||
# Выходим из main, выше она в цикле запустится ещё раз |
|||
return |
|||
|
|||
elif toa == 7: |
|||
# Сигнал о том что инфа об абоненте изменилась, надо перечитать |
|||
usr = filter_user_by_id(users, event.id) |
|||
usr.deserialize(event.dt, tariffs) |
|||
# если абонент активен, и куплен и активирован тариф то можно и в инет |
|||
if usr.is_active and usr.tariff is not None: |
|||
frw.close_inet_door(usr) |
|||
frw.open_inet_door(usr) |
|||
|
|||
elif toa == 8: |
|||
# Сигнал об изменении данных в тарифе |
|||
tariff = filter(lambda trf: trf.tid == event.id, tariffs) |
|||
if len(tariff) > 0: |
|||
tariff = tariff[0] |
|||
tariff.deserialize(event.dt) |
|||
|
|||
# Пересоздаём тариф |
|||
frw.destroy_tariff(tariff) |
|||
frw.make_tariff(tariff) |
|||
else: |
|||
print 'WARNING: не найден тариф для которого сигнал на изменение данных, пробуем перезагрузиться' |
|||
return |
|||
|
|||
# Очищаем очередь событий |
|||
ts.clear() |
|||
|
|||
# ждём время между итерациями проверки 10 сек... |
|||
sleep(10) |
|||
stdout.write('.') |
|||
stdout.flush() |
|||
@ -0,0 +1,143 @@ |
|||
# -*- coding:utf-8 -*- |
|||
import socket |
|||
import struct |
|||
from json import loads, dumps |
|||
from abc import ABCMeta, abstractmethod |
|||
|
|||
|
|||
class Serializer(object): |
|||
__metaclass__ = ABCMeta |
|||
|
|||
@abstractmethod |
|||
def _serializable_obj(self): |
|||
"""Вернуть словарь для сериализации""" |
|||
|
|||
def serialize(self): |
|||
return dumps(self._serializable_obj()) |
|||
|
|||
@abstractmethod |
|||
def deserialize(self, *args): |
|||
"""Надо обязательно этот метод реализовать, он много где используется. |
|||
Из JSON создать объект класса где реализуется метод""" |
|||
|
|||
|
|||
def serialize_tariffs(tariffs): |
|||
dt = map(lambda trf: trf._serializable_obj(), tariffs) |
|||
return dumps({'tariffs': dt}) |
|||
|
|||
|
|||
def deserialize_tariffs(dat): |
|||
dat = loads(dat) if type(dat) == str else dat |
|||
# Распаковываем из JSON массива dat['tariffs'] объекты через метод deserialize |
|||
return map(lambda tariff: Tariff().deserialize(tariff), dat['tariffs']) |
|||
|
|||
|
|||
def serialize_abonents(abonents): |
|||
dt = map(lambda abn: abn._serializable_obj(), abonents) |
|||
return dumps({'subscribers': dt}) |
|||
|
|||
|
|||
def deserialize_abonents(dat, tariffs): |
|||
dat = loads(dat) if type(dat) == str else dat |
|||
# Распаковываем из JSON массива dat['subscribers'] объекты через метод deserialize |
|||
return map(lambda abon: Abonent().deserialize(abon, tariffs), dat['subscribers']) |
|||
|
|||
|
|||
class Tariff(Serializer): |
|||
tid = 0 |
|||
speedIn = 0.0 |
|||
speedOut = 0.0 |
|||
|
|||
def __init__(self, tariff_id=None, speed_in=None, speed_out=None): |
|||
self.tid = tariff_id |
|||
self.speedOut = speed_out |
|||
self.speedIn = speed_in |
|||
|
|||
def is_active(self): |
|||
"""возвращает активность тарифа. Если он не активен то пропустить""" |
|||
return True |
|||
|
|||
def _serializable_obj(self): |
|||
return { |
|||
'id': self.tid, |
|||
'speedIn': self.speedIn, |
|||
'speedOut': self.speedOut |
|||
} |
|||
|
|||
def deserialize(self, dump): |
|||
inf = loads(dump) if type(dump) == str else dump |
|||
self.speedIn = float(inf['speedIn']) |
|||
self.speedOut = float(inf['speedOut']) |
|||
self.tid = int(inf['id']) |
|||
return self |
|||
|
|||
|
|||
class Abonent(Serializer): |
|||
uid = 0 |
|||
tariff = Tariff() |
|||
ip = 0xffffffff |
|||
|
|||
# Включён-ли абонент |
|||
is_active = True |
|||
|
|||
def __init__(self, uid=None, ip=None, tariff=None): |
|||
self.ip = ip |
|||
self.uid = uid |
|||
self.tariff = tariff |
|||
|
|||
def ip_str(self): |
|||
# int2ip, Example out '127.0.0.1' |
|||
return socket.inet_ntoa(struct.pack("!I", self.ip)) |
|||
|
|||
def _serializable_obj(self): |
|||
return { |
|||
'id': self.uid, |
|||
'is_active': self.is_active, |
|||
'ip': self.ip, |
|||
'tarif_id': self.tariff.tid if self.tariff else 0 |
|||
} |
|||
|
|||
def deserialize(self, dump, tariffs): |
|||
inf = loads(dump) if type(dump) == str else dump |
|||
self.uid = int(inf['id']) |
|||
self.is_active = bool(inf['is_active']) |
|||
self.ip = int(inf['ip']) |
|||
|
|||
tarif_id = int(inf['tarif_id']) |
|||
dbtrf = filter(lambda trf: trf.tid == tarif_id, tariffs) |
|||
if len(dbtrf) > 0: |
|||
self.tariff = dbtrf[0] |
|||
else: |
|||
self.tariff = None |
|||
return self |
|||
|
|||
|
|||
class EventNAS(Serializer): |
|||
|
|||
# Type Of Action |
|||
toa = 0 |
|||
|
|||
# id of object |
|||
id = 0 |
|||
|
|||
# extended data |
|||
dt = object() |
|||
|
|||
def __init__(self, type_action=None, obj_id=None, ext_data=None): |
|||
self.toa = type_action |
|||
self.id = obj_id |
|||
self.dt = ext_data |
|||
|
|||
def _serializable_obj(self): |
|||
if self.dt: |
|||
return {'toa': self.toa, 'id': self.id, 'dt': self.dt} |
|||
else: |
|||
return {'toa': self.toa, 'id': self.id} |
|||
|
|||
def deserialize(self, dump): |
|||
print dump |
|||
inf = loads(dump) if type(dump) == str else dump |
|||
self.toa = int(inf['toa']) |
|||
self.id = int(inf['id']) |
|||
self.dt = inf.get('dt') |
|||
return self |
|||
@ -0,0 +1,49 @@ |
|||
#!/bin/env python2 |
|||
import sys |
|||
import socket |
|||
import struct |
|||
from re import sub |
|||
|
|||
|
|||
def ip2int(strip): |
|||
return struct.unpack("!I", socket.inet_aton(strip))[0] |
|||
|
|||
|
|||
def convert(query): |
|||
dat = sub(r'\s+', ' ', query.strip('\n')).split(' ') |
|||
|
|||
if len(dat) == 1: |
|||
return |
|||
|
|||
src_ip = ip2int(dat[0]) |
|||
dst_ip = ip2int(dat[1]) |
|||
proto = int(dat[2]) |
|||
src_port = int(dat[3]) |
|||
dst_port = int(dat[4]) |
|||
octets = int(dat[5]) |
|||
packets = int(dat[6]) |
|||
|
|||
sql = ",(%s,%s,%d,%d,%d,%d,%d)" % ( |
|||
hex(src_ip), hex(dst_ip), proto, src_port, dst_port, octets, packets |
|||
) |
|||
return sql |
|||
|
|||
|
|||
if __name__=='__main__': |
|||
f=sys.stdin |
|||
print("INSERT INTO flowstat(`src_ip`, `dst_ip`, `proto`, `src_port`, `dst_port`, `octets`, `packets`) VALUES") |
|||
|
|||
# always none |
|||
f.readline() |
|||
|
|||
# first line |
|||
rs = convert(f.readline()) |
|||
# without first comma |
|||
print(rs[1:]) |
|||
|
|||
while True: |
|||
rs = convert(f.readline()) |
|||
if not rs: |
|||
break |
|||
print(rs) |
|||
f.close() |
|||
@ -0,0 +1,11 @@ |
|||
#!/bin/sh |
|||
|
|||
DUMP_DIR="/var/db/flows" |
|||
|
|||
DUMP_FILE="$DUMP_DIR/$1" |
|||
PATH=/usr/local/sbin:/usr/local/bin:/usr/bin |
|||
CUR_DIR=`dirname $0` |
|||
|
|||
|
|||
flow-print -f3 < ${DUMP_FILE} | ${CUR_DIR}/netflow_handler.py \ |
|||
| mysql -uroot -p jungagent --password=ps |
|||
@ -0,0 +1,7 @@ |
|||
#!/bin/sh |
|||
|
|||
PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/lib/jvm/default/bin:/home/dn/bin |
|||
|
|||
flow-capture -R /home/dn/bin/netflow_handler.py -w /var/db/flows -n1 -N0 0.0.0.0/0.0.0.0/8888 |
|||
|
|||
softflowd -v 5 -i wlp3s0 -n 127.0.0.1:8888 |
|||
@ -0,0 +1,17 @@ |
|||
# -*- coding: utf-8 -*- |
|||
|
|||
|
|||
# Agent ip and port |
|||
SELF_IP = '127.0.0.1' |
|||
SELF_PORT = 2134 |
|||
|
|||
# Main server ip and port |
|||
SERVER_IP = '127.0.0.1' |
|||
SERVER_PORT = 8000 #8443 |
|||
|
|||
# Certificates |
|||
CERTFILE = "/etc/ssl/server.crt" |
|||
KEYFILE = "/etc/ssl/server.key" |
|||
|
|||
# Использовать-ли при передаче инфы между NAS и основным сервером SSL |
|||
IS_USE_SSL = False |
|||
@ -0,0 +1,226 @@ |
|||
# -*- coding: utf-8 -*- |
|||
import ssl |
|||
import socket |
|||
from multiprocessing import Process, Manager#, Lock |
|||
import settings |
|||
from models import EventNAS, Abonent, Tariff |
|||
|
|||
|
|||
class NetExcept(Exception): |
|||
def __init__(self, value): |
|||
self.value = value |
|||
def __str__(self): |
|||
return repr(self.value) |
|||
|
|||
|
|||
class SSLTransmitterServer(object): |
|||
bindsocket = None |
|||
|
|||
def connect(self, ip, port): |
|||
bindsocket = socket.socket() |
|||
bindsocket.bind((ip, port)) |
|||
bindsocket.listen(5) |
|||
self.bindsocket = bindsocket |
|||
|
|||
def _on_data_recive(self, v, data): |
|||
print "do_something:", data |
|||
#with lock: |
|||
v.append(EventNAS().deserialize(data)) |
|||
return False |
|||
|
|||
def _deal_with_client(self, connstream, v): |
|||
data = connstream.read() |
|||
while data: |
|||
if not self._on_data_recive(v, data): |
|||
break |
|||
data = connstream.read() |
|||
|
|||
def process(self, v): |
|||
while True: |
|||
newsocket, fromaddr = self.bindsocket.accept() |
|||
connstream = ssl.wrap_socket(newsocket, |
|||
server_side=True, |
|||
certfile=settings.CERTFILE, |
|||
keyfile=settings.KEYFILE) |
|||
try: |
|||
self._deal_with_client(connstream, v) |
|||
finally: |
|||
connstream.shutdown(socket.SHUT_RDWR) |
|||
connstream.close() |
|||
|
|||
|
|||
class PlainTransmitterServer(SSLTransmitterServer): |
|||
|
|||
def process(self, v): |
|||
while True: |
|||
newsocket, fromaddr = self.bindsocket.accept() |
|||
dat = newsocket.recv(0xffff) |
|||
if not dat: |
|||
break |
|||
self._on_data_recive(v, dat) |
|||
|
|||
|
|||
# Декоратор переводит классы абонента базы к объекту агента если надо. |
|||
# abonapp.models.Abon -> agent.Abonent |
|||
def agent_abon_typer(fn): |
|||
def wrapped(self, abon): |
|||
if isinstance(abon, Abonent): |
|||
fn(self, abon) |
|||
else: |
|||
abn = Abonent( |
|||
abon.id, |
|||
abon.ip_address.int_ip() if abon.ip_address else 0, |
|||
abon.active_tariff() |
|||
) |
|||
fn(self, abn) |
|||
return wrapped |
|||
|
|||
|
|||
# Декоратор переводит классы тарифа базы к объекту агента если надо. |
|||
# tariff_app.models.Tariff -> agent.Tariff |
|||
def agent_tarif_typer(fn): |
|||
def wrapped(self, tariff): |
|||
if isinstance(tariff, Tariff): |
|||
fn(self, tariff) |
|||
else: |
|||
trf = Tariff( |
|||
tariff.id, |
|||
tariff.speedIn, |
|||
tariff.speedOut |
|||
) |
|||
fn(self, trf) |
|||
return wrapped |
|||
|
|||
|
|||
class SSLTransmitterClient(object): |
|||
s = None |
|||
|
|||
def __init__(self, ip=None, port=None): |
|||
try: |
|||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
|||
# Require a certificate from the server. We used a self-signed certificate |
|||
# so here ca_certs must be the server certificate itself. |
|||
self.s = ssl.wrap_socket(s, |
|||
ca_certs=settings.CERTFILE, |
|||
cert_reqs=ssl.CERT_REQUIRED) |
|||
self.s.connect(( |
|||
ip or settings.SELF_IP, |
|||
port or settings.SELF_PORT |
|||
)) |
|||
except socket.error: |
|||
raise NetExcept(u'Ошибка подключения к NAS агенту') |
|||
|
|||
def write(self, d): |
|||
self.s.write(d) |
|||
|
|||
@agent_abon_typer |
|||
def signal_abon_enable(self, abon): |
|||
self.write( |
|||
EventNAS(1, abon.uid).serialize() |
|||
) |
|||
|
|||
@agent_abon_typer |
|||
def signal_abon_disable(self, abon): |
|||
self.write( |
|||
EventNAS(2, abon.uid).serialize() |
|||
) |
|||
|
|||
@agent_abon_typer |
|||
def signal_abon_set_cap(self, abon): |
|||
self.write( |
|||
EventNAS(3, abon.uid).serialize() |
|||
) |
|||
|
|||
@agent_abon_typer |
|||
def signal_abon_open_inet(self, abon): |
|||
self.write( |
|||
EventNAS(4, abon.uid).serialize() |
|||
) |
|||
|
|||
@agent_abon_typer |
|||
def signal_abon_close_inet(self, abon): |
|||
self.write( |
|||
EventNAS(5, abon.uid).serialize() |
|||
) |
|||
|
|||
@agent_abon_typer |
|||
def signal_agent_reboot(self, abon): |
|||
self.write( |
|||
EventNAS(6, abon.uid).serialize() |
|||
) |
|||
|
|||
@agent_abon_typer |
|||
def signal_abon_refresh_info(self, abon): |
|||
self.write( |
|||
EventNAS(7, abon.uid, abon._serializable_obj()).serialize() |
|||
) |
|||
|
|||
@agent_tarif_typer |
|||
def signal_tarif_refresh_info(self, tariff): |
|||
self.write( |
|||
EventNAS(8, tariff.tid, tariff._serializable_obj()).serialize() |
|||
) |
|||
|
|||
def __del__(self): |
|||
self.s.close() |
|||
|
|||
|
|||
class PlainTransmitterClient(SSLTransmitterClient): |
|||
|
|||
def __init__(self, ip=None, port=None): |
|||
try: |
|||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
|||
s.connect(( |
|||
ip or settings.SELF_IP, |
|||
port or settings.SELF_PORT |
|||
)) |
|||
self.s = s |
|||
except socket.error: |
|||
raise NetExcept(u'Ошибка подключения к NAS агенту') |
|||
|
|||
def write(self, d): |
|||
self.s.send(d) |
|||
|
|||
|
|||
def get_TransmitterClientKlass(): |
|||
if settings.IS_USE_SSL: |
|||
return SSLTransmitterClient |
|||
else: |
|||
return PlainTransmitterClient |
|||
|
|||
|
|||
def get_TransmitterServerKlass(): |
|||
if settings.IS_USE_SSL: |
|||
return SSLTransmitterServer |
|||
else: |
|||
return PlainTransmitterServer |
|||
|
|||
|
|||
def proc_entrypoint(obj, v, lock, ip, port): |
|||
srv = get_TransmitterServerKlass()() |
|||
srv.connect(ip, port) |
|||
srv.process(v) |
|||
|
|||
|
|||
class TransmitServer(object): |
|||
|
|||
def __init__(self, ip, port): |
|||
mngr = Manager() |
|||
self.v = mngr.list() |
|||
#self.lock = Lock() |
|||
self.p = Process(target=proc_entrypoint, args=(self, self.v, None, ip, port))#self.lock)) |
|||
|
|||
def get_data(self): |
|||
if len(self.v) > 0: |
|||
return list(self.v) |
|||
else: |
|||
return [] |
|||
|
|||
def clear(self): |
|||
del self.v[:] |
|||
|
|||
def start(self): |
|||
self.p.start() |
|||
|
|||
def __del__(self): |
|||
self.p.terminate() |
|||
@ -0,0 +1 @@ |
|||
# -*- coding:utf-8 -*- |
|||
@ -0,0 +1,11 @@ |
|||
- (GUI) Иконки возле кнопок не настроены, натыканы случайно |
|||
|
|||
Остановился на управлении купленными услугами(тарифами): |
|||
+ нужен порядок выполнения очереди тарифов (модель зделана) |
|||
+ Управление приоритетом тарифов (порядком выполнения) |
|||
- удаление ещё не активных купленных тарифов |
|||
+ отображение и выделение активного тарифа |
|||
|
|||
Нужно на каждое изменение данных об абоненте слать сигнал об этом на NAS |
|||
|
|||
Фаервол допилить, там с пайпами не понятно, не знаю принцип действия |
|||
@ -0,0 +1,18 @@ |
|||
#!/usr/bin/env python |
|||
# -*- coding: utf-8 -*- |
|||
import os |
|||
import django |
|||
|
|||
|
|||
if __name__ == "__main__": |
|||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djing.settings") |
|||
django.setup() |
|||
from abonapp.models import Abon, AbonTariff |
|||
|
|||
users = Abon.objects.all() |
|||
|
|||
for usr in users: |
|||
|
|||
usr.activate_next_tariff() |
|||
|
|||
AbonTariff.objects.update_priorities(usr) |
|||
@ -0,0 +1,6 @@ |
|||
from django.contrib import admin |
|||
import models |
|||
|
|||
admin.site.register(models.Device) |
|||
admin.site.register(models.Port) |
|||
admin.site.register(models.PortStates) |
|||
@ -0,0 +1,7 @@ |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.apps import AppConfig |
|||
|
|||
|
|||
class DevappConfig(AppConfig): |
|||
name = 'devapp' |
|||
@ -0,0 +1,109 @@ |
|||
# -*- coding: utf-8 -*- |
|||
from abc import ABCMeta, abstractmethod |
|||
from netsnmp import Session, VarList, Varbind |
|||
|
|||
|
|||
class DevBase(object): |
|||
__metaclass__ = ABCMeta |
|||
|
|||
@staticmethod |
|||
def description(): |
|||
"""Возвращает текстовое описание""" |
|||
|
|||
@abstractmethod |
|||
def reboot(self): |
|||
"""Перезагружает устройство""" |
|||
|
|||
@abstractmethod |
|||
def get_statistics(self): |
|||
"""Получаем статистику""" |
|||
|
|||
@abstractmethod |
|||
def get_vlan(self): |
|||
"""Получаем инфу о VLAN""" |
|||
|
|||
@abstractmethod |
|||
def get_ports(self): |
|||
"""Получаем инфу о портах""" |
|||
|
|||
@abstractmethod |
|||
def toggle_port(self, port_num): |
|||
pass |
|||
|
|||
|
|||
class Port(object): |
|||
|
|||
def __init__(self, num, name, status, mac, speed): |
|||
self.num = int(num) |
|||
self.nm = name |
|||
self.st = status |
|||
self._mac = mac |
|||
self.sp = speed |
|||
|
|||
def mac(self): |
|||
m = self._mac |
|||
return "%x:%x:%x:%x:%x:%x" % (ord(m[0]), ord(m[1]), ord(m[2]), ord(m[3]), ord(m[4]), ord(m[5])) |
|||
|
|||
|
|||
class SNMPBaseWorker(object): |
|||
ses = None |
|||
|
|||
oids = { |
|||
'reboot': '.1.3.6.1.4.1.2021.8.1.101.1', |
|||
'get_ports': { |
|||
'names': 'IF-MIB::ifDescr', |
|||
'stats': 'IF-MIB::ifAdminStatus', |
|||
'macs': 'IF-MIB::ifPhysAddress', |
|||
'speeds': 'IF-MIB::ifHighSpeed' |
|||
}, |
|||
'name': 'SNMPv2-SMI::mib-2.47.1.1.1.1.7.1', |
|||
'toggle_port': '.1.3.6.1.2.1.2.2.1.7' |
|||
} |
|||
|
|||
def __init__(self, ip, passw='public', ver=2): |
|||
self.ses = Session(DestHost=ip, Version=ver, Community=passw) |
|||
|
|||
def _get_vars(self, oid): |
|||
vs = VarList(Varbind(oid)) |
|||
return self.ses.walk(vs) |
|||
|
|||
def _get_var(self, oid): |
|||
var = VarList(Varbind(oid)) |
|||
return self.ses.get(var) |
|||
|
|||
# Enable/Disable port |
|||
def toggle_port(self, port_num, status=True): |
|||
vs = VarList(Varbind( |
|||
tag="%s.%d" % (self.oids['toggle_port'], port_num), |
|||
val=1 if status else 2, |
|||
type='INTEGER' |
|||
)) |
|||
return self.ses.set(vs) |
|||
|
|||
def get_ports(self): |
|||
nams = self._get_vars(self.oids['get_ports']['names']) |
|||
stats = self._get_vars(self.oids['get_ports']['stats']) |
|||
macs = self._get_vars(self.oids['get_ports']['macs']) |
|||
speeds = self._get_vars(self.oids['get_ports']['speeds']) |
|||
res = [] |
|||
ln = len(nams) |
|||
for n in range(0, ln): |
|||
res.append(Port(n, nams[n], bool(stats[n]), macs[n], int(speeds[n]))) |
|||
return res |
|||
|
|||
def get_name(self): |
|||
return self._get_var(self.oids['name']) |
|||
|
|||
|
|||
# Example usage |
|||
if __name__ == '__main__': |
|||
wrk = SNMPBaseWorker('10.115.1.104', 'private', 2) |
|||
print(wrk.get_name()) |
|||
for pr in wrk.get_ports(): |
|||
assert isinstance(pr, Port) |
|||
print(pr.nm, pr.st, pr.mac(), pr.sp) |
|||
|
|||
# Enable 2 port |
|||
print wrk.toggle_port(2, True) |
|||
# Disable 2 port |
|||
print wrk.toggle_port(2, False) |
|||
@ -0,0 +1,30 @@ |
|||
# -*- coding: utf-8 -*- |
|||
from base_intr import DevBase, SNMPBaseWorker |
|||
|
|||
|
|||
class DLinkDevice(DevBase): |
|||
|
|||
@staticmethod |
|||
def description(): |
|||
return u"Свич D'Link" |
|||
|
|||
def reboot(self): |
|||
pass |
|||
|
|||
|
|||
DEVICE_TYPES = ( |
|||
('Dl', DLinkDevice), |
|||
) |
|||
|
|||
|
|||
class SNMPDlinkWorker(SNMPBaseWorker): |
|||
oids = { |
|||
'reboot': '.1.3.6.1.4.1.2021.8.1.101.1', |
|||
'get_ports': { |
|||
'names': 'IF-MIB::ifDescr', |
|||
'stats': 'IF-MIB::ifAdminStatus', |
|||
'macs': 'IF-MIB::ifPhysAddress', |
|||
'speeds': 'IF-MIB::ifHighSpeed' |
|||
}, |
|||
'name': 'SNMPv2-SMI::mib-2.47.1.1.1.1.7.1' |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
# -*- coding: utf-8 -*- |
|||
from django import forms |
|||
import models |
|||
from mydefs import ip_addr_regex |
|||
|
|||
|
|||
class DeviceForm(forms.ModelForm): |
|||
class Meta: |
|||
model = models.Device |
|||
fields = '__all__' |
|||
widgets = { |
|||
'ip_address': forms.TextInput(attrs={ |
|||
'pattern': ip_addr_regex, |
|||
'placeholder': '192.168.0.100', |
|||
'required': True, |
|||
'class': 'form-control', |
|||
'id': 'ip_address' |
|||
}), |
|||
'comment': forms.Textarea(attrs={ |
|||
'required': True, |
|||
'class': 'form-control', |
|||
'id': 'comment' |
|||
}), |
|||
'devtype': forms.Select(attrs={ |
|||
'class': 'form-control', |
|||
'id': 'devtype' |
|||
}), |
|||
'man_passw': forms.PasswordInput(attrs={ |
|||
'class': 'form-control', |
|||
'id': 'man_passw' |
|||
}) |
|||
} |
|||
@ -0,0 +1,49 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.9 on 2016-06-28 23:51 |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.db import migrations, models |
|||
import django.db.models.deletion |
|||
import mydefs |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
initial = True |
|||
|
|||
dependencies = [ |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.CreateModel( |
|||
name='Device', |
|||
fields=[ |
|||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
|||
('ip_address', mydefs.MyGenericIPAddressField(max_length=8, protocol=b'IPv4')), |
|||
('comment', models.CharField(max_length=256)), |
|||
('devtype', models.CharField(choices=[(b'Dl', "\u0421\u0432\u0438\u0447 D'Link")], default=b'Dl', max_length=2)), |
|||
('man_passw', models.CharField(blank=True, max_length=16, null=True)), |
|||
], |
|||
), |
|||
migrations.CreateModel( |
|||
name='Port', |
|||
fields=[ |
|||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
|||
('num', models.PositiveSmallIntegerField(default=0)), |
|||
('speed', models.CharField(choices=[(b'h', b'100Mbps'), (b'k', b'1Gbps'), (b'd', b'10Gbps')], default=b'h', max_length=1)), |
|||
('device', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='devapp.Device')), |
|||
], |
|||
), |
|||
migrations.CreateModel( |
|||
name='PortStates', |
|||
fields=[ |
|||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
|||
('state_json_info', models.TextField()), |
|||
('port', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='devapp.Port')), |
|||
], |
|||
), |
|||
migrations.AlterUniqueTogether( |
|||
name='port', |
|||
unique_together=set([('device', 'num')]), |
|||
), |
|||
] |
|||
@ -0,0 +1,52 @@ |
|||
# -*- coding: utf-8 -*- |
|||
from django.db import models |
|||
from base_intr import DevBase |
|||
from mydefs import MyGenericIPAddressField, MyChoicesAdapter |
|||
from dev_types import DEVICE_TYPES |
|||
|
|||
|
|||
class _DeviceChoicesAdapter(MyChoicesAdapter): |
|||
|
|||
def __init__(self): |
|||
super(_DeviceChoicesAdapter, self).__init__(DEVICE_TYPES) |
|||
|
|||
|
|||
class Device(models.Model): |
|||
ip_address = MyGenericIPAddressField() |
|||
comment = models.CharField(max_length=256) |
|||
devtype = models.CharField(max_length=2, default=DEVICE_TYPES[0][0], choices=_DeviceChoicesAdapter()) |
|||
man_passw = models.CharField(max_length=16, null=True, blank=True) |
|||
#map_dot = models.ForeignKey() |
|||
|
|||
def get_abons(self): |
|||
pass |
|||
|
|||
def get_stat(self): |
|||
pass |
|||
|
|||
def get_manager_klass(self): |
|||
klasses = filter(lambda kl: kl[0] == self.devtype, DEVICE_TYPES) |
|||
if len(klasses) > 0: |
|||
res = klasses[0][1] |
|||
if issubclass(res, DevBase): |
|||
return res |
|||
return |
|||
|
|||
|
|||
class Port(models.Model): |
|||
PORT_SPEEDS = ( |
|||
('h', '100Mbps'), |
|||
('k', '1Gbps'), |
|||
('d', '10Gbps') |
|||
) |
|||
device = models.ForeignKey(Device) |
|||
num = models.PositiveSmallIntegerField(default=0) |
|||
speed = models.CharField(max_length=1, default=PORT_SPEEDS[0][0], choices=PORT_SPEEDS) |
|||
|
|||
class Meta: |
|||
unique_together = (('device', 'num')) |
|||
|
|||
|
|||
class PortStates(models.Model): |
|||
port = models.OneToOneField(Port) |
|||
state_json_info = models.TextField() |
|||
@ -0,0 +1,3 @@ |
|||
from django.test import TestCase |
|||
|
|||
# Create your tests here. |
|||
@ -0,0 +1,10 @@ |
|||
from django.conf.urls import url |
|||
import views |
|||
|
|||
urlpatterns = [ |
|||
url(r'^$', views.devices, name='devs_link'), |
|||
url(r'^add$', views.dev, name='devs_add_link'), |
|||
url(r'^(?P<did>\d+)$', views.devview, name='devs_view_link'), |
|||
url(r'^(?P<did>\d+)/del$', views.devdel, name='devs_del_link'), |
|||
url(r'^(?P<devid>\d+)/edit$', views.dev, name='devs_edit_link'), |
|||
] |
|||
@ -0,0 +1,59 @@ |
|||
# -*- coding: utf-8 -*- |
|||
from django.contrib.auth.decorators import login_required |
|||
from django.shortcuts import render, redirect, get_object_or_404 |
|||
from models import Device |
|||
from mydefs import pag_mn, res_success, res_error |
|||
from forms import DeviceForm |
|||
|
|||
|
|||
@login_required |
|||
def devices(request): |
|||
devs = Device.objects.all() |
|||
devs = pag_mn(request, devs) |
|||
|
|||
return render(request, 'devapp/devices.html', { |
|||
'devices': devs |
|||
}) |
|||
|
|||
|
|||
@login_required |
|||
def devdel(request, did): |
|||
try: |
|||
get_object_or_404(Device, id=did).delete() |
|||
return res_success(request, 'devs_link') |
|||
except: |
|||
return res_error(request, u'Неизвестная ошибка при удалении :(') |
|||
|
|||
|
|||
@login_required |
|||
def dev(request, devid=0): |
|||
warntext = '' |
|||
devinst = get_object_or_404(Device, id=devid) if devid != 0 else None |
|||
|
|||
if request.method == 'POST': |
|||
frm = DeviceForm(request.POST, instance=devinst) |
|||
if frm.is_valid(): |
|||
frm.save() |
|||
return redirect('devs_link') |
|||
else: |
|||
warntext = u'Ошибка в данных, проверте их ещё раз' |
|||
else: |
|||
frm = DeviceForm(instance=devinst) |
|||
|
|||
return render(request, 'devapp/dev.html', { |
|||
'warntext': warntext, |
|||
'form': frm, |
|||
'devid': devid |
|||
}) |
|||
|
|||
|
|||
@login_required |
|||
def devview(request, did): |
|||
warntext = '' |
|||
|
|||
dev = get_object_or_404(Device, id=did) |
|||
|
|||
return render(request, 'devapp/ports.html', { |
|||
'warntext': warntext, |
|||
'dev': dev |
|||
}) |
|||
@ -0,0 +1,143 @@ |
|||
# -*- coding: utf-8 -* |
|||
import os |
|||
|
|||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...) |
|||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |
|||
from django.core.urlresolvers import reverse_lazy |
|||
|
|||
# Quick-start development settings - unsuitable for production |
|||
# See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/ |
|||
|
|||
# SECURITY WARNING: keep the secret key used in production secret! |
|||
SECRET_KEY = '$xvppe_5&iu4fgnj2h@eie6+w*n&m=60e7k_6ha5r4rgnfndz1' |
|||
|
|||
# SECURITY WARNING: don't run with debug turned on in production! |
|||
DEBUG = True |
|||
|
|||
ALLOWED_HOSTS = ['*'] |
|||
|
|||
|
|||
# Application definition |
|||
|
|||
INSTALLED_APPS = [ |
|||
'django.contrib.admin', |
|||
'django.contrib.auth', |
|||
'django.contrib.contenttypes', |
|||
'django.contrib.sessions', |
|||
'django.contrib.messages', |
|||
'django.contrib.staticfiles', |
|||
'accounts_app', |
|||
'photo_app', |
|||
'privatemessage', |
|||
'abonapp', |
|||
'tariff_app', |
|||
'ip_pool', |
|||
'searchapp', |
|||
'devapp', |
|||
'mapapp', |
|||
'statistics' |
|||
] |
|||
|
|||
MIDDLEWARE_CLASSES = [ |
|||
'django.middleware.security.SecurityMiddleware', |
|||
'django.contrib.sessions.middleware.SessionMiddleware', |
|||
'django.middleware.common.CommonMiddleware', |
|||
'django.middleware.csrf.CsrfViewMiddleware', |
|||
'django.contrib.auth.middleware.AuthenticationMiddleware', |
|||
'django.contrib.auth.middleware.SessionAuthenticationMiddleware', |
|||
'django.contrib.messages.middleware.MessageMiddleware', |
|||
'django.middleware.clickjacking.XFrameOptionsMiddleware', |
|||
] |
|||
|
|||
ROOT_URLCONF = 'djing.urls' |
|||
|
|||
TEMPLATES = [ |
|||
{ |
|||
'BACKEND': 'django.template.backends.django.DjangoTemplates', |
|||
'DIRS': [os.path.join(BASE_DIR, 'templates')], |
|||
'APP_DIRS': True, |
|||
'OPTIONS': { |
|||
'context_processors': [ |
|||
'django.template.context_processors.debug', |
|||
'django.template.context_processors.request', |
|||
'django.contrib.auth.context_processors.auth', |
|||
'django.contrib.messages.context_processors.messages', |
|||
'mydefs.context_processor_client_ipaddress' |
|||
], |
|||
}, |
|||
}, |
|||
] |
|||
|
|||
WSGI_APPLICATION = 'djing.wsgi.application' |
|||
|
|||
|
|||
# Database |
|||
# https://docs.djangoproject.com/en/1.9/ref/settings/#databases |
|||
|
|||
DATABASES = { |
|||
'default': { |
|||
#'ENGINE': 'django.db.backends.sqlite3', |
|||
#'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), |
|||
'ENGINE': 'django.db.backends.mysql', |
|||
'NAME': 'jungagent', |
|||
'USER': 'root', |
|||
'PASSWORD': 'ps', |
|||
'HOST': 'localhost' |
|||
} |
|||
} |
|||
|
|||
|
|||
# Password validation |
|||
# https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators |
|||
|
|||
AUTH_PASSWORD_VALIDATORS = [ |
|||
{ |
|||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', |
|||
}, |
|||
{ |
|||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', |
|||
}, |
|||
{ |
|||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', |
|||
}, |
|||
{ |
|||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', |
|||
}, |
|||
] |
|||
|
|||
|
|||
# Internationalization |
|||
# https://docs.djangoproject.com/en/1.9/topics/i18n/ |
|||
|
|||
LANGUAGE_CODE = 'ru-RU' |
|||
|
|||
TIME_ZONE = 'Europe/Simferopol' |
|||
|
|||
USE_I18N = True |
|||
|
|||
USE_L10N = False |
|||
|
|||
USE_TZ = True |
|||
|
|||
|
|||
# Static files (CSS, JavaScript, Images) |
|||
# https://docs.djangoproject.com/en/1.9/howto/static-files/ |
|||
|
|||
STATIC_URL = '/static/' |
|||
if DEBUG: |
|||
STATICFILES_DIRS = (os.path.join(BASE_DIR, "static"),) |
|||
|
|||
|
|||
# Пример вывода: 16 сентября 2012 |
|||
DATE_FORMAT = 'd E Y' |
|||
|
|||
|
|||
MEDIA_ROOT = os.path.join(BASE_DIR, 'media') |
|||
MEDIA_URL = '/media/' |
|||
|
|||
DEFAULT_PICTURE = '/static/images/default-avatar.png' |
|||
AUTH_USER_MODEL = 'accounts_app.UserProfile' |
|||
|
|||
LOGIN_URL = reverse_lazy('login_link') |
|||
LOGIN_REDIRECT_URL = reverse_lazy('profile') |
|||
LOGOUT_URL = reverse_lazy('logout_link') |
|||
@ -0,0 +1,26 @@ |
|||
from django.conf.urls import url, include |
|||
from django.contrib import admin |
|||
import settings |
|||
from views import home |
|||
|
|||
urlpatterns = [ |
|||
url(r'^$', home), |
|||
url(r'^accounts/', include('accounts_app.urls')), |
|||
url(r'^im/', include('privatemessage.urls')), |
|||
url(r'^abons/', include('abonapp.urls')), |
|||
url(r'^tarifs/', include('tariff_app.urls')), |
|||
url(r'^ip_pool/', include('ip_pool.urls')), |
|||
url(r'^search/', include('searchapp.urls')), |
|||
url(r'^dev/', include('devapp.urls')), |
|||
url(r'^map/', include('mapapp.urls')), |
|||
url(r'^statistic/', include('statistics.urls')), |
|||
url(r'^admin/', admin.site.urls), |
|||
] |
|||
|
|||
|
|||
if settings.DEBUG: |
|||
from django.conf.urls.static import static |
|||
from django.contrib.staticfiles.urls import staticfiles_urlpatterns |
|||
|
|||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) |
|||
urlpatterns += staticfiles_urlpatterns() |
|||
@ -0,0 +1,9 @@ |
|||
from django.shortcuts import redirect |
|||
|
|||
|
|||
def home(request): |
|||
return redirect('profile') |
|||
|
|||
|
|||
def finance_report(request): |
|||
pass |
|||
@ -0,0 +1,16 @@ |
|||
""" |
|||
WSGI config for djing project. |
|||
|
|||
It exposes the WSGI callable as a module-level variable named ``application``. |
|||
|
|||
For more information on this file, see |
|||
https://docs.djangoproject.com/en/1.9/howto/deployment/wsgi/ |
|||
""" |
|||
|
|||
import os |
|||
|
|||
from django.core.wsgi import get_wsgi_application |
|||
|
|||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djing.settings") |
|||
|
|||
application = get_wsgi_application() |
|||
@ -0,0 +1,17 @@ |
|||
create table flowstat ( |
|||
`id` int(10) AUTO_INCREMENT NOT NULL, |
|||
`src_ip` CHAR(8) NOT NULL, |
|||
`dst_ip` CHAR(8) NOT NULL, |
|||
`proto` smallint(2) unsigned NOT NULL DEFAULT 0, |
|||
`src_port` smallint(5) unsigned NOT NULL DEFAULT 0, |
|||
`dst_port` smallint(5) unsigned NOT NULL DEFAULT 0, |
|||
`octets` INT unsigned NOT NULL DEFAULT 0, |
|||
`packets` INT unsigned NOT NULL DEFAULT 0, |
|||
PRIMARY KEY (`id`) |
|||
) ENGINE=MyISAM DEFAULT CHARSET=utf8; |
|||
|
|||
|
|||
|
|||
INSERT INTO flowstat(`src_ip`, `dst_ip`, `proto`, `src_port`, `dst_port`, `octets`, `packets`) VALUES |
|||
('c0a80201', 'c0a805ba', 6, 49150, 443, 5281, 13), |
|||
('c0a80201', 'c0a805ba', 6, 49150, 443, 5281, 13) |
|||
@ -0,0 +1 @@ |
|||
default_app_config = 'ip_pool.apps.IpPoolConfig' |
|||
@ -0,0 +1,4 @@ |
|||
from django.contrib import admin |
|||
import models |
|||
|
|||
admin.site.register(models.IpPoolItem) |
|||
@ -0,0 +1,6 @@ |
|||
from django.apps import AppConfig |
|||
|
|||
|
|||
class IpPoolConfig(AppConfig): |
|||
name = 'ip_pool' |
|||
verbose_name = u"Ip pool" |
|||
@ -0,0 +1,21 @@ |
|||
# -*- coding: utf-8 -*- |
|||
from django import forms |
|||
from mydefs import ip_addr_regex |
|||
|
|||
|
|||
class PoolForm(forms.Form): |
|||
start_ip = forms.GenericIPAddressField(protocol='IPv4', widget=forms.TextInput(attrs={ |
|||
'pattern': ip_addr_regex, |
|||
'placeholder': u'127.0.0.1', |
|||
'id': 'start_ip', |
|||
'class': 'form-control', |
|||
'required': '' |
|||
}), required=True) |
|||
|
|||
end_ip = forms.GenericIPAddressField(protocol='IPv4', widget=forms.TextInput(attrs={ |
|||
'pattern': ip_addr_regex, |
|||
'placeholder': u'127.0.0.1', |
|||
'id': 'end_ip', |
|||
'class': 'form-control', |
|||
'required': '' |
|||
}), required=True) |
|||
@ -0,0 +1,24 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.9 on 2016-06-28 23:51 |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.db import migrations, models |
|||
import mydefs |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
initial = True |
|||
|
|||
dependencies = [ |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.CreateModel( |
|||
name='IpPoolItem', |
|||
fields=[ |
|||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
|||
('ip', mydefs.MyGenericIPAddressField(max_length=8, protocol=b'IPv4')), |
|||
], |
|||
), |
|||
] |
|||
@ -0,0 +1,60 @@ |
|||
from django.db import models, connection |
|||
from mydefs import ip2int, MyGenericIPAddressField |
|||
|
|||
|
|||
class IpPoolItemManager(models.Manager): |
|||
|
|||
def get_pools(self): |
|||
ips = self.raw(r'SELECT id, ip FROM ip_pool_ippoolitem ORDER BY id') |
|||
ips_len = len(list(ips)) |
|||
if ips_len < 1: |
|||
return |
|||
last_dg = ip2int(ips[0].ip) |
|||
start_pool = last_dg |
|||
res = list() |
|||
cnt = 0 |
|||
for ip in ips: |
|||
ipnt = ip2int(ip.ip) |
|||
if ipnt > last_dg + 1 or ipnt < last_dg - 1: |
|||
res.append((start_pool, last_dg, cnt)) |
|||
start_pool = ipnt |
|||
cnt = 0 |
|||
last_dg = ipnt |
|||
cnt += 1 |
|||
res.append((start_pool, last_dg, cnt)) |
|||
return res |
|||
|
|||
def add_pool(self, start_ip, end_ip): |
|||
start_ip = ip2int(start_ip) |
|||
end_ip = ip2int(end_ip) |
|||
|
|||
if (end_ip - start_ip) > 5000: |
|||
raise Exception(u'Not add over 5000 ip\'s') |
|||
|
|||
sql_strs = map(lambda tip: r"(%d)" % tip, range(start_ip, end_ip+1)) |
|||
sql = r'INSERT INTO ip_pool_ippoolitem (ip) VALUES %s' % r",".join(sql_strs) |
|||
print sql |
|||
|
|||
cursor = connection.cursor() |
|||
cursor.execute(sql) |
|||
|
|||
def get_free_ip(self): |
|||
sql = r'SELECT ip_pool_ippoolitem.id as id, ip_pool_ippoolitem.ip as ip FROM ip_pool_ippoolitem ' \ |
|||
r'LEFT JOIN abonent ON abonent.ip_address_id = ip_pool_ippoolitem.id WHERE ' \ |
|||
r'abonent.ip_address_id IS NULL LIMIT 1' |
|||
|
|||
rs = self.raw(sql) |
|||
rs_len = len(list(rs)) |
|||
return None if rs_len is 0 else rs[0] |
|||
|
|||
|
|||
class IpPoolItem(models.Model): |
|||
ip = MyGenericIPAddressField() |
|||
|
|||
objects = IpPoolItemManager() |
|||
|
|||
def int_ip(self): |
|||
return ip2int(self.ip) |
|||
|
|||
def __unicode__(self): |
|||
return self.ip |
|||
@ -0,0 +1,3 @@ |
|||
from django.test import TestCase |
|||
|
|||
# Create your tests here. |
|||
@ -0,0 +1,13 @@ |
|||
# -*- coding:utf-8 -*- |
|||
from django.conf.urls import url |
|||
import views |
|||
|
|||
urlpatterns = [ |
|||
|
|||
url(r'^$', views.home, name='pool_home_link'), |
|||
url(r'^range$', views.ips, name='pool_ips_link'), |
|||
url(r'^del$', views.del_pool, name='pool_ips_del_link'), |
|||
url(r'^add$', views.add_pool, name='pool_add_link'), |
|||
|
|||
url(r'^delip$', views.delip, name='pool_del_ip_link') |
|||
] |
|||
@ -0,0 +1,76 @@ |
|||
from django.contrib.auth.decorators import login_required |
|||
from django.shortcuts import render, redirect, get_object_or_404 |
|||
from forms import PoolForm |
|||
from models import IpPoolItem |
|||
import mydefs |
|||
|
|||
|
|||
@login_required |
|||
def home(request): |
|||
pools = IpPoolItem.objects.get_pools() |
|||
print pools |
|||
|
|||
if pools: |
|||
pools = map(lambda ip: (mydefs.int2ip(ip[0]), mydefs.int2ip(ip[1]), ip[2]), pools) |
|||
pools = mydefs.pag_mn(request, pools) |
|||
|
|||
return render(request, 'ip_pool/index.html', { |
|||
'pools': pools |
|||
}) |
|||
|
|||
|
|||
@login_required |
|||
def ips(request): |
|||
ip_start = request.GET.get('ips') |
|||
ip_end = request.GET.get('ipe') |
|||
|
|||
pool_ips = IpPoolItem.objects.filter(ip__gte=ip_start) |
|||
pool_ips = pool_ips.filter(ip__lte=ip_end) |
|||
|
|||
pool_ips = mydefs.pag_mn(request, pool_ips) |
|||
|
|||
return render(request, 'ip_pool/ips.html', { |
|||
'pool_ips': pool_ips, |
|||
'ips': ip_start, |
|||
'ipe': ip_end |
|||
}) |
|||
|
|||
|
|||
@login_required |
|||
def del_pool(request): |
|||
ip_start = request.GET.get('ips') |
|||
ip_end = request.GET.get('ipe') |
|||
|
|||
pool_ips = IpPoolItem.objects.filter(ip__gte=ip_start) |
|||
pool_ips = pool_ips.filter(ip__lte=ip_end) |
|||
pool_ips = pool_ips.filter() |
|||
|
|||
pool_ips.delete() |
|||
|
|||
return mydefs.res_success(request, 'pool_home_link') |
|||
|
|||
|
|||
@login_required |
|||
def add_pool(request): |
|||
if request.method == 'POST': |
|||
frm = PoolForm(request.POST) |
|||
if frm.is_valid(): |
|||
cd = frm.cleaned_data |
|||
IpPoolItem.objects.add_pool(cd['start_ip'], cd['end_ip']) |
|||
return redirect('pool_home_link') |
|||
else: |
|||
warntext = u'Form is not valid' |
|||
else: |
|||
frm = PoolForm() |
|||
warntext = '' |
|||
return render(request, 'ip_pool/add_pool.html', { |
|||
'form': frm, |
|||
'warntext': warntext |
|||
}) |
|||
|
|||
|
|||
@login_required |
|||
def delip(request): |
|||
ipid = request.GET.get('id') |
|||
get_object_or_404(IpPoolItem, id=ipid).delete() |
|||
return mydefs.res_success(request, 'pool_home_link') |
|||
@ -0,0 +1,10 @@ |
|||
#!/usr/bin/env python |
|||
import os |
|||
import sys |
|||
|
|||
if __name__ == "__main__": |
|||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djing.settings") |
|||
|
|||
from django.core.management import execute_from_command_line |
|||
|
|||
execute_from_command_line(sys.argv) |
|||
@ -0,0 +1,4 @@ |
|||
from django.contrib import admin |
|||
import models |
|||
|
|||
admin.site.register(models.Dot) |
|||
@ -0,0 +1,7 @@ |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.apps import AppConfig |
|||
|
|||
|
|||
class MapappConfig(AppConfig): |
|||
name = 'mapapp' |
|||
@ -0,0 +1,25 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.9 on 2016-06-28 23:51 |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.db import migrations, models |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
initial = True |
|||
|
|||
dependencies = [ |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.CreateModel( |
|||
name='Dot', |
|||
fields=[ |
|||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
|||
('title', models.CharField(max_length=64)), |
|||
('posX', models.FloatField()), |
|||
('posY', models.FloatField()), |
|||
], |
|||
), |
|||
] |
|||
@ -0,0 +1,10 @@ |
|||
from django.db import models |
|||
|
|||
|
|||
class Dot(models.Model): |
|||
title = models.CharField(max_length=64) |
|||
posX = models.FloatField() |
|||
posY = models.FloatField() |
|||
|
|||
def __unicode__(self): |
|||
return self.title |
|||
@ -0,0 +1,3 @@ |
|||
from django.test import TestCase |
|||
|
|||
# Create your tests here. |
|||
@ -0,0 +1,7 @@ |
|||
from django.conf.urls import url |
|||
import views |
|||
|
|||
urlpatterns = [ |
|||
url(r'^$', views.home, name='maps_home_link'), |
|||
url(r'^get_dots$', views.get_dots, name='maps_get_dots') |
|||
] |
|||
@ -0,0 +1,18 @@ |
|||
from django.contrib.auth.decorators import login_required |
|||
from django.http import HttpResponse |
|||
from django.shortcuts import render |
|||
from models import Dot |
|||
from json import dumps |
|||
|
|||
|
|||
@login_required |
|||
def home(request): |
|||
return render(request, 'maps/index.html') |
|||
|
|||
|
|||
|
|||
def get_dots(r): |
|||
dots = Dot.objects.all() |
|||
return HttpResponse(dumps({ |
|||
'dots': map(lambda d: (d.id, d.posX, d.posY, d.title), dots) |
|||
})) |
|||
@ -0,0 +1,111 @@ |
|||
# -*- coding: utf-8 -*- |
|||
from django.http import HttpResponse, Http404 |
|||
from json import dumps |
|||
from django.shortcuts import redirect |
|||
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage |
|||
import socket |
|||
import struct |
|||
from django.db import models |
|||
from collections import Iterator |
|||
|
|||
|
|||
ip_addr_regex = r'^(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]?)$' |
|||
|
|||
|
|||
def ip2int(addr): |
|||
return struct.unpack("!I", socket.inet_aton(addr))[0] |
|||
|
|||
|
|||
def int2ip(addr): |
|||
return socket.inet_ntoa(struct.pack("!I", addr)) |
|||
|
|||
|
|||
def safe_float(fl): |
|||
return 0.0 if fl is None or fl == '' else float(fl) |
|||
|
|||
|
|||
def safe_int(i): |
|||
return 0 if i is None or i == '' else int(i) |
|||
|
|||
|
|||
def res_success(request, redirect_to='/'): |
|||
if request.is_ajax(): |
|||
return HttpResponse(dumps({'errnum': 0})) |
|||
else: |
|||
return redirect(redirect_to) |
|||
|
|||
|
|||
def res_error(request, text): |
|||
if request.is_ajax(): |
|||
return HttpResponse(dumps({'errnum':1, 'errtext':text})) |
|||
else: |
|||
raise Http404(text) |
|||
|
|||
|
|||
# Pagination |
|||
def pag_mn(request, objs, count_per_page=10): |
|||
page = request.GET.get('p') |
|||
pgn = Paginator(objs, count_per_page) |
|||
try: |
|||
objs = pgn.page(page) |
|||
except PageNotAnInteger: |
|||
objs = pgn.page(1) |
|||
except EmptyPage: |
|||
objs = pgn.page(pgn.num_pages) |
|||
return objs |
|||
|
|||
|
|||
def context_processor_client_ipaddress(request): |
|||
ip = request.META.get('REMOTE_ADDR', '') or request.META.get('HTTP_X_FORWARDED_FOR', '') |
|||
return { |
|||
'client_ipaddress': ip |
|||
} |
|||
#logmodels.debug('%s %s %s'%(request.user, request.path, ip)) |
|||
|
|||
|
|||
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(models.GenericIPAddressField, self).get_prep_value(value) |
|||
return ip2int(value) |
|||
|
|||
def to_python(self, addr): |
|||
return addr |
|||
|
|||
def get_internal_type(self): |
|||
return 'PositiveIntegerField' |
|||
|
|||
@staticmethod |
|||
def from_db_value(value, expression, connection, context): |
|||
return int2ip(value) |
|||
|
|||
|
|||
# Предназначен для Django CHOICES чтоб можно было передавать классы вместо просто описания поля, |
|||
# классы передавать для того чтоб по значению кода из базы понять какой класс нужно взять для нужной функциональности. |
|||
# Например по коду в базе вам нужно определять как считать тариф абонента, что реализовано в возвращаемом классе. |
|||
class MyChoicesAdapter(Iterator): |
|||
chs = tuple() |
|||
current_index = 0 |
|||
_max_index = 0 |
|||
|
|||
# На вход принимает кортеж кортежей, вложенный из 2х элементов: кода и класса, как: TARIFF_CHOICES |
|||
def __init__(self, choices): |
|||
self._max_index = len(choices) |
|||
self.chs = choices |
|||
|
|||
def next(self): |
|||
if self.current_index >= self._max_index: |
|||
raise StopIteration |
|||
else: |
|||
e = self.chs |
|||
ci = self.current_index |
|||
res = e[ci][0], e[ci][1].description() |
|||
self.current_index += 1 |
|||
return res |
|||
@ -0,0 +1,4 @@ |
|||
from django.contrib import admin |
|||
import models |
|||
|
|||
admin.site.register(models.Photo) |
|||
@ -0,0 +1,25 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.9 on 2016-06-28 23:51 |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.db import migrations, models |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
initial = True |
|||
|
|||
dependencies = [ |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.CreateModel( |
|||
name='Photo', |
|||
fields=[ |
|||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
|||
('image', models.ImageField(height_field=b'heigt', upload_to=b'', width_field=b'wdth')), |
|||
('wdth', models.PositiveSmallIntegerField(blank=True, default=b'759', editable=False, null=True)), |
|||
('heigt', models.PositiveSmallIntegerField(blank=True, editable=False, null=True)), |
|||
], |
|||
), |
|||
] |
|||
@ -0,0 +1,69 @@ |
|||
# -*- coding:utf-8 -*- |
|||
from django.db import models |
|||
from djing.settings import BASE_DIR |
|||
import os |
|||
from PIL import Image |
|||
import time |
|||
import hashlib |
|||
|
|||
|
|||
class Photo(models.Model): |
|||
image = models.ImageField(width_field='wdth', height_field='heigt') |
|||
wdth = models.PositiveSmallIntegerField(null=True, blank=True, editable=False, default="759") |
|||
heigt = models.PositiveSmallIntegerField(null=True, blank=True, editable=False) |
|||
|
|||
def __unicode__(self): |
|||
return "{0}".format(self.image) |
|||
|
|||
def big(self): |
|||
return self.image.url |
|||
|
|||
def min(self): |
|||
pth = self.image.url.split('/')[-1:][0] |
|||
return "/media/min/%s" % pth |
|||
|
|||
def save(self, *args, **kwargs): |
|||
if not self.image: |
|||
return |
|||
|
|||
super(Photo, self).save() |
|||
|
|||
im = Image.open(self.image.path) |
|||
im.thumbnail((759, 759), Image.ANTIALIAS) |
|||
|
|||
hs = hashlib.md5(str(time.time())).hexdigest() |
|||
ext = self.image.path.split('.')[1:][0] |
|||
fname = "%s/media/%s.%s" % (BASE_DIR, hs, ext) |
|||
|
|||
im.save(fname) |
|||
os.remove(self.image.path) |
|||
self.image = "%s.%s" % (hs, ext) |
|||
super(Photo, self).save() |
|||
|
|||
|
|||
#class Meta: |
|||
# unique_together = (('image',),) |
|||
|
|||
|
|||
def resize_image(sender, instance, **kwargs): |
|||
if not kwargs['created']: |
|||
im = Image.open(instance.image.path) |
|||
im.thumbnail((200, 121), Image.ANTIALIAS) |
|||
pth = instance.image.path.split('/')[-1:][0] |
|||
fullpath = "%s/media/min/%s" % (BASE_DIR, pth) |
|||
im.save(fullpath) |
|||
|
|||
|
|||
|
|||
def post_delete_photo(sender, instance, **kwargs): |
|||
min_fname = instance.image.path.split('/')[-1:][0] |
|||
try: |
|||
os.remove('%s/media/min/%s' % (BASE_DIR, min_fname)) |
|||
os.remove(instance.image.path) |
|||
except OSError: |
|||
pass |
|||
|
|||
|
|||
|
|||
models.signals.post_save.connect(resize_image, sender=Photo) |
|||
models.signals.post_delete.connect(post_delete_photo, sender=Photo) |
|||
@ -0,0 +1,16 @@ |
|||
""" |
|||
This file demonstrates writing tests using the unittest module. These will pass |
|||
when you run "manage.py test". |
|||
|
|||
Replace this with more appropriate tests for your application. |
|||
""" |
|||
|
|||
from django.test import TestCase |
|||
|
|||
|
|||
class SimpleTest(TestCase): |
|||
def test_basic_addition(self): |
|||
""" |
|||
Tests that 1 + 1 always equals 2. |
|||
""" |
|||
self.assertEqual(1 + 1, 2) |
|||
@ -0,0 +1,12 @@ |
|||
# -*- coding:utf-8 -*- |
|||
from django.conf.urls import patterns, url |
|||
|
|||
urlpatterns = [ |
|||
#url(r'^$', 'photo_albums', name='media_home_link'), |
|||
#url(r'^(?P<id>\d+)$', 'photo_albums', name='media_home_link_by_id'), |
|||
#url(r'^album(?P<id>\d+)$', 'photos', name='media_photos_link'), |
|||
|
|||
|
|||
#url(r'^newalbum/$', 'add_album', name='add_media_album_link'), |
|||
#url(r'^newphoto/$', 'add_photo_to_album', name='add_media_photo_link') |
|||
] |
|||
@ -0,0 +1 @@ |
|||
# -*- coding:utf-8 -*- |
|||
@ -0,0 +1,5 @@ |
|||
from django.contrib import admin |
|||
from models import PrivateMessages, Dialog |
|||
|
|||
admin.site.register(PrivateMessages) |
|||
admin.site.register(Dialog) |
|||
@ -0,0 +1,7 @@ |
|||
from models import PrivateMessages |
|||
|
|||
|
|||
def avail_messages(request): |
|||
return { |
|||
'avail_messages_num': PrivateMessages.objects.get_my_messages(request) |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.9 on 2016-06-28 23:51 |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.conf import settings |
|||
from django.db import migrations, models |
|||
import django.db.models.deletion |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
initial = True |
|||
|
|||
dependencies = [ |
|||
migrations.swappable_dependency(settings.AUTH_USER_MODEL), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.CreateModel( |
|||
name='Dialog', |
|||
fields=[ |
|||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
|||
('title', models.CharField(max_length=127)), |
|||
('date_create', models.DateTimeField(auto_now_add=True)), |
|||
('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL)), |
|||
('recepient', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL)), |
|||
], |
|||
), |
|||
migrations.CreateModel( |
|||
name='PrivateMessages', |
|||
fields=[ |
|||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
|||
('date_send', models.DateTimeField(auto_now_add=True)), |
|||
('text', models.TextField()), |
|||
('is_viewed', models.BooleanField(default=False)), |
|||
('dialog', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='privatemessage.Dialog')), |
|||
], |
|||
), |
|||
] |
|||
@ -0,0 +1,34 @@ |
|||
from django.db import models |
|||
from djing import settings |
|||
|
|||
|
|||
class MessagesManager(models.Manager): |
|||
|
|||
def get_my_messages(self, request): |
|||
if request.user.is_authenticated(): |
|||
num = self.filter(recepient=request.user, is_viewed=False).count() |
|||
else: |
|||
num = 0 |
|||
return int(num) |
|||
|
|||
|
|||
class Dialog(models.Model): |
|||
title = models.CharField(max_length=127) |
|||
owner = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='+') |
|||
recepient = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='+') |
|||
date_create = models.DateTimeField(auto_now_add=True) |
|||
|
|||
def __unicode__(self): |
|||
return self.title |
|||
|
|||
|
|||
class PrivateMessages(models.Model): |
|||
dialog = models.ForeignKey(Dialog) |
|||
date_send = models.DateTimeField(auto_now_add=True) |
|||
text = models.TextField() |
|||
is_viewed = models.BooleanField(default=False) |
|||
|
|||
objects = MessagesManager() |
|||
|
|||
def __unicode__(self): |
|||
return self.text |
|||
@ -0,0 +1,22 @@ |
|||
from django.test import TestCase |
|||
import models |
|||
from django.contrib.auth.models import User |
|||
|
|||
|
|||
class PaysTest(TestCase): |
|||
|
|||
def setUp(self): |
|||
self.msg = models.PrivateMessages.objects.create( |
|||
sender=User.objects.all()[0], |
|||
recepient=User.objects.all()[0], |
|||
text='test init text' |
|||
) |
|||
|
|||
def tearDown(self): |
|||
models.PrivateMessages.objects.all().delete() |
|||
|
|||
def check_ret_msgs(self): |
|||
"""check return messages""" |
|||
request = self.factory.get('/message/') |
|||
self.assertIsInstance(models.PrivateMessages.objects.get_my_messages(request), int, 'checking ret type') |
|||
self.assertGreater(models.PrivateMessages.objects.get_my_messages(request), 0, 'checking msg count') |
|||
@ -0,0 +1,8 @@ |
|||
from django.conf.urls import url |
|||
import views |
|||
|
|||
urlpatterns = [ |
|||
url(r'^$', views.home, name='privmsg_home'), |
|||
url(r'^delitem_(?P<id>\d+)$', views.delitem, name='privmsg_delitem'), |
|||
url(r'^write', views.send_message, name='privmsg_send_message') |
|||
] |
|||
@ -0,0 +1,54 @@ |
|||
# -*- coding: utf-8 -*- |
|||
from django.contrib.auth.decorators import login_required |
|||
from django.shortcuts import render, redirect |
|||
from django.template.loader import render_to_string |
|||
from models import PrivateMessages |
|||
from django.http import HttpResponse |
|||
from json import dumps |
|||
from django.template.context_processors import csrf |
|||
import mydefs |
|||
from accounts_app.models import UserProfile |
|||
|
|||
|
|||
@login_required |
|||
def home(request): |
|||
msgs = PrivateMessages.objects.all() |
|||
return render(request, 'private_messages/index.html', { |
|||
'msgs': msgs |
|||
}) |
|||
|
|||
|
|||
@login_required |
|||
def delitem(request, id=0): |
|||
r = {'errnum': 0,'errtext': u''} |
|||
try: |
|||
PrivateMessages.objects.get(id=id).delete() |
|||
except PrivateMessages.DoesNotExist: |
|||
r = { |
|||
'errnum': 1, |
|||
'errtext': u'Error while deleting, item does not exist' |
|||
} |
|||
return HttpResponse(dumps(r)) |
|||
|
|||
|
|||
@login_required |
|||
def send_message(request): |
|||
if request.method == 'GET': |
|||
return HttpResponse(render_to_string('private_messages/send_form.html',{ |
|||
'csrf_token': csrf(request)['csrf_token'], |
|||
'a': request.GET.get('a') |
|||
})) |
|||
elif request.method == 'POST': |
|||
try: |
|||
a = request.GET.get('a') |
|||
a = 0 if a is None or a == '' else int(a) |
|||
msg = PrivateMessages() |
|||
msg.sender = request.user |
|||
msg.recepient = UserProfile.objects.get(id=a) |
|||
msg.text = request.POST.get('msg_text') |
|||
msg.save() |
|||
return redirect('privmsg_home') |
|||
except UserProfile.DoesNotExist: |
|||
return mydefs.res_error(request, u'Адресат не найден') |
|||
else: |
|||
return mydefs.res_error(request, u'Ошибка типа запроса') |
|||
@ -0,0 +1,4 @@ |
|||
Django==1.9 |
|||
requests |
|||
Pillow |
|||
MySQL-python |
|||
@ -0,0 +1,3 @@ |
|||
from django.contrib import admin |
|||
|
|||
# Register your models here. |
|||
@ -0,0 +1,7 @@ |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.apps import AppConfig |
|||
|
|||
|
|||
class SearchappConfig(AppConfig): |
|||
name = 'searchapp' |
|||
@ -0,0 +1,5 @@ |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.db import models |
|||
|
|||
# Create your models here. |
|||
@ -0,0 +1,3 @@ |
|||
from django.test import TestCase |
|||
|
|||
# Create your tests here. |
|||
@ -0,0 +1,7 @@ |
|||
from django.conf.urls import url |
|||
import views |
|||
|
|||
urlpatterns = [ |
|||
url(r'^$', views.home, name='search_home_link'), |
|||
|
|||
] |
|||
Some files were not shown because too many files changed in this diff
Write
Preview
Loading…
Cancel
Save
Reference in new issue