Jump to content
Sign in to follow this  
Hans R. Steiner

Billing...

Recommended Posts

Есть идея - написание бесплатной биллинговой системы, которая могла бы составить конкуренцию коммерческим собратьям,

сертификация этой системы.

Чего в этом интересного?

1. создание бесплатной КАЧЕСТВЕННОЙ альтернативы дешевым и не

-- качественным системам.

2. возможность, в последствии, на этом заработать... если конечно

-- что-то хорошее из этого получится.

3. можно будет еще что-нибудь написать, если вместе работать

-- понравится.

Почему сам не напишу?

1. собственной фантазии не хватает

2. проблемы с организацией работ (пишу пару дней, после, думаю,

-- что вообще изначально не правильно все придумал, киляю все и

-- пишу опять)

Share this post


Link to post
Share on other sites

Сертификация, насколько я знаю, не бесплатна Sad

Идею поддерживаю, могу предоставить свои наработки. Сейчас как раз пишу под себя биллинг. Заточен под ВПН. Могу привести список того, что он умеет сейчас и что будет уметь через два-три дня Smile

Share this post


Link to post
Share on other sites

Сертификация не бесплатна, но если получится хорошая вещь то,

даже сделав эту вещь бесплатной, можно заработать на ней денег и

как минимум, окупить сертификацию и пиво Smile

Приводи Smile Интересно посмотреть Smile Только я вот хочу сделать

что-нибудь без привязок т.е., чтобы привязка была не

обязательной... т.е., представлять подключеник к vpn, к примеру,

как один из параметров услуги "передача трафика", как это сделано

в той же UTM.

Share this post


Link to post
Share on other sites

Пиво? Кто сказал ПИВО?? Smile

ААА реализуется на радиусе, в моем случае - rlm_perl. Пока учет ведется тем же перловым скриптом, что и две другие АА Smile, но хочу вынести в отдельный демон.

Алгоритм таков:

1. Авторизация

Клиент поднимает pptp-соединение. PPPD через radius.so обращается к радиусу. Вызывается функция authorize, которая при удачной авторизации отдает NAS'у ИП для ВПН-соединения, маску, помещает в RAD_CHECK плэйнтекстовый пароль пользователя. После этого вызывается радиусовский модуль mschap, который хэширует пароль в MD4 (или что там в MSCHAP используется) и отдает хэш NAS'у.

2. Аутентификация

Тут используется только модуль MSCHAP, который проверяет хэши и по результатам впускает пользователя.

3. Аккаунтинг

Это должно реализоваться отдельным демоном, не зависящим от радиусовского Interim-Update-Interval, но сейчас работает в том же перловом скрипте.

Сведенья о тарифах хранятся в трех SQL-таблицах - tar_base, tar_time, tar_traf.

В первой - базовые ставки за вход/исх трафик и время и потолок трафика для данного тарифа (для предопл. трафика)

Во второй - почасовые множители для трафика и времени.

В третьей - множители для трафика по направлениям/портам.

Т.о. сумма, ежеминутно снимаемая с пользовательского счета, определяется как:

my $minus = $time * $row->{'hour'} * $row->{'hmul'} +

$inb * $row->{'inb'} * $row->{'inbmul'} / MB +

$outb * $row->{'outb'} * $row->{'outbmul'} / MB;

Здесь

$time - интервал тарифицируемого времени

$inb - вход. трафик

$outb - исход. трафик

$row->{'hour'} - базовая ставка за час

$row->{'inb'} - базовая ставка за метр вход.

$row->{'outb'} - базовая ставка за метр исх.

$row->{'hmul'} - временной множитель для данного тарифа

$row->{'inbmul'} - вход. траф. множитель для данного тарифа

$row->{'outbmul'} - исх. траф. множитель для данного тарифа

Тут не учитываются хосты и порты, при их введении нужно дополнить формулу. Правда, с т.з. производительности это нужно делать именно в отдельном демоне, причем только с агрегированным трафиком, иначе биллинг захлебнется:)

Share this post


Link to post
Share on other sites

Интересная тема Smile Надо будет почитать Smile

Сейчас разбираюсь с модульностью в своей реализации и возникла вот такая проблема:

есть некая таблица `user` и некая таблица `account`

процесс заведения пользователя состоит из звух частей:

1. занесение записи в таблицу `user`

2. занесение записи в таблицу `account`

INSERT INTO `user` ( `name`, `address`, `telephone` ) VALEUS ( '$username', '$address', '$telephone' );

INSERT INTO `user` ( `uid`, `login`, `password` ) VALUES ( '$uid', '#login', $password );

При этом, в таблице `user` есть поле

`id` INT AUTO_INCREMENT

и соответственно, во второи INSERT в базу, поле `uid`, должно

соответствовать инкрементному полю `id` для первого INSERT.

В данной ситуации, можно воспользоваться LAST_INCREMENT_ID, но

тогда, мы получаем последнюю инкрементную величину, при этом,

не зависимо от того, в какую таблицу была вставлена запись, а

в этот момент, так же, ведется запись трафика в базу (при помощи

того же ulogd).

вот и сижу, не понимаю... как быть...

CREATE TABLE `pandora_user` (
  `id`            INT   AUTO_INCREMENT        COMMENT 'Уникальный идентификатор пользователя',
  `name`          TEXT  NOT NULL  DEFAULT ''  COMMENT 'Полное имя пользователя',
  `address`       TEXT  NOT NULL  DEFAULT ''  COMMENT 'Адрес проживания',
  `telephone`     TEXT  NOT NULL  DEFAULT ''  COMMENT 'Контактный телефон',
  `email`         TEXT  NOT NULL  DEFAULT ''  COMMENT 'Адрес эллектронной почты',
  `pasport_get`   TEXT  NOT NULL  DEFAULT ''  COMMENT 'Кем выдан пасспорт',
  `pasport_num`   TEXT  NOT NULL  DEFAULT ''  COMMENT 'Серия и номер паспорта',
  
  PRIMARY KEY (`id`)
) ENGINE = MYISAM;

CREATE TABLE `pandora_account` (
  `id`            INT   AUTO_INCREMENT        COMMENT 'Уникальный идентификатор аккаунта',
  `uid`           INT   NOT NULL  DEFAULT '0' COMMENT 'Ссылка на пользователя',
  `login`         TEXT  NOT NULL  DEFAULT ''  COMMENT 'Логин',
  `group`         TEXT  NOT NULL  DEFAULT ''  COMMENT 'Группа',
  `password`      TEXT  NOT NULL  DEFAULT ''  COMMENT 'Пароль',
  
  PRIMARY KEY (`id`)
) ENGINE = MYISAM;

Делать это в однойтаблице тоже крайне не хочется так, как

хочется реализовать вариант, когда у одного пользоваетеля, может

быть несколько различных логинов.

Share this post


Link to post
Share on other sites

Наверное, все-таки

Цитата:

INSERT INTO `user` ( `name`, `address`, `telephone` ) VALEUS ( '$username', '$address', '$telephone' );

INSERT INTO `account` ( `uid`, `login`, `password` ) VALUES ( '$uid', '#login', $password );

При этом, в таблице `user` есть поле

`id` INT AUTO_INCREMENT

и соответственно, во второи INSERT в базу, поле `uid`, должно

соответствовать инкрементному полю `id` для первого INSERT.

В данной ситуации, можно воспользоваться LAST_INCREMENT_ID, но

тогда, мы получаем последнюю инкрементную величину, при этом,

не зависимо от того, в какую таблицу была вставлена запись, а

в этот момент, так же, ведется запись трафика в базу

Попробуй так:

CREATE TRIGGER test AFTER INSERT ON user FOR EACH ROW INSERT INTO account ( `uid`, `login`, `password` ) VALUES ( NEW.id, '#login', $password );

Share this post


Link to post
Share on other sites

Да, это интересное решение. Есть только одно "но":

поледовательность должно быть примерно следующая:

СРЕАТЕ ТАБЛЕ `user` ( ... );
CREATE TABLE `account` ( ... );
CREATE TRIGGER `account` AFTER INSERT ON `user` FOR EACH ROW \
  INSERT INTO `account` ( `uid` ... ) VALUES ( NEW.id, ... );

INSERT INTO `user` ( ... ) VALUES ( ... );

... и при этом, автоматически выполняется вставка зависи в

`account`, а чего вставляется? ведь вставляться должна информация

которая так же получена из скрипта...

Решение: перед инсертом в `user` делать что-то вроде:

SET @myLogin := '$login';
...
SET @myPassword := '$pasword';

и тогда можно делать INSERT в `user`... но еще одно "но", все же,

остается... у меня мания на "гибкость" самописных приложений...

при таком подходе, становится невозможным записать в базу инфу о

пользователе, не создав для него аккаунт.

Интересен был бы вариант запроса, который или в который

инкапсулировался бы INSERT в `user` и после выполнения, возвращал

бы значение инкрементного поля.

Пробывал наворотить SELECT'ов в одном запросе, но ничего

хорошего из этого не получается... не клеются они у меня так,

как надо.

Напрашивеется вариант с кучей тупобональных селектов после записи

но это крайне не красивое, не интересное и думаю, в перспективе,

многожрущее решение, которое, так же еще и не верно так, как

в базе (это конечно крайне мало вероятно, но всеже), существует

вероятность одновременного существования разных людей, но с

одинаковыми данными (а учитывая то, что паспортные данные, если

они раньше не фиксировались, будут собираться очень долго,

найти двух-трех людей с одинаковыми именами, работающих в одном

здании и у которых один и тот же контактный рабочий телефон - не

так уж и сложно) (ЗЫ: у нас в офисе, три Николая и два с

половиной Андрея... благо хоть все остальные данные разные).

Share this post


Link to post
Share on other sites

Ну... кажется, практически придумал решение:

CREATE TRIGGER myTrigger AFTER INSERT ON `user` FOR EACH ROW \
  SET @lastId := NEW.id;
INSERT INTO `user` ...;
INSERT INTO `account` ( `uid`, ...) VALUES ( @lastId, ... );
DROP TRIGGER `user`;

Единственное, что мне тут не нравится, у меня не получилось создать больше одного триггера на таблицу и создавать/удалять триггер, приходится при каждом таком запросе.

Может есть более изящный вариант? Smile

Share this post


Link to post
Share on other sites

Блииин... вот засада...

минут десять не мог понять, где же гадость залегла...

пытаюсь создать юзверя:

CREATE TRIGGER myTrigger AFTER INSERT ON `pandora_user` FOR EACH ROW SET @myUid := NEW.id;
INSERT INTO `pandora_user` ( `name`, `address`, `telephone`, `email`, `pasport_get`, `pasport_num` )
VALUES ( '123', '234', '345', '', '', '' );
INSERT INTO `pandora_account` ( `uid`, `login`, `group`, `password` )
VALUES ( @myUid, '123', 'Пользователь', '123' );
DROP TRIGGER myTrigger

Не проходит... точнее, из скрипта не проходит... в базе, от имени

root, проходит... дооолго думал... селекты из скрипта делаются, а

запись - нет...

Захожу в базу как скриптоюзверь и повторяю запрос на запись - фиг

вам - национальная индейская изба... а запарка на моменте

создания триггера - "ERROR 1227 (42000): Access denied; you need

the SUPER privilege for this operation" :(

Цитата:

18.1. CREATE TRIGGER Syntax

CREATE

[DEFINER = { user | CURRENT_USER }]

TRIGGER trigger_name trigger_time trigger_event

ON tbl_name FOR EACH ROW trigger_stmt

This statement creates a new trigger. A trigger is a named database object that is associated with a table, and that activates when a particular event occurs for the table. CREATE TRIGGER was added in MySQL 5.0.2. Currently, its use requires the SUPER privilege.

Share this post


Link to post
Share on other sites

В принципе, решил проблему путем более внимательного рассмотра

LAST_INSERT_ID. После более подробного разбора данный функции,

стало ясно, что опасности в ее таковом использовании нет так,

как несмотря на отсутствие ее привязки к конкретной таблице,

ее значение не передается между "сессиями" т.е., подключившись

несколько раз к баде, даже под одним пользователем, эта функция,

является, своего рода, "переменной сеанса" т.е, не зависимой от

других сессий.

И так, запрос вида:

CREATE TRIGGER myTrigger AFTER INSERT ON `pandora_user` FOR EACH ROW SET @myUid := NEW.id;
INSERT INTO `pandora_user` ( `name`, `address`, `telephone`, `email`, `pasport_get`, `pasport_num` )
VALUES ( '123', '234', '345', '', '', '' );
INSERT INTO `pandora_account` ( `uid`, `login`, `group`, `password` )
VALUES ( @myUid, '123', 'Пользователь', '123' );
DROP TRIGGER myTrigger;

был успешно заменен на:

INSERT INTO `pandora_user` ( `name`, `address`, `telephone`, `email`, `pasport_get`, `pasport_num` )
VALUES ( '123', '234', '345', '', '', '' );
INSERT INTO `pandora_account` ( `uid`, `login`, `group`, `password` )
VALUES ( LAST_INSERT_ID(), '123', 'Пользователь', '123' );

Smile

Share this post


Link to post
Share on other sites

Блин... чего-то у меня в последнее время вопросов много...

совсем разучился думать самостоятельно... на пенсию пора...

или как минимум, на отдых...

Вопрос следующего плана: есть у меня ряд запросов типа INSERT...

к примеру, допустим, наверное, три :)

my $queru = "INSERT ...; INSERT ...; INSERT ...;";
my $sth = $dbh -> prepare ( "$sql" ); $sth -> execute;

Не выполняется :(

my $queru = "INSERT ...";
my $sth = $dbh -> prepare ( "$sql" );
$sth -> execute;
my $queru = "INSERT ...";
my $sth = $dbh -> prepare ( "$sql" );
$sth -> execute;
my $queru = "INSERT ...";
my $sth = $dbh -> prepare ( "$sql" );
$sth -> execute;

Выполняется :/

Не понимаю :(

Share this post


Link to post
Share on other sites

Ганс, зачем при каждом запросе триггер-то создавать? Это ж такой же элемент логики, как и хранимые процедуры, их создал раз при создании самой базы - и они дальше сами работают. Я, по крайней мере, не могу себе представить ситуации, в которой бы возникла необходимость создавать/изменять/удалять триггеры во время работы с базой.

Цитата:

Вопрос следующего плана: есть у меня ряд запросов типа INSERT...

к примеру, допустим, наверное, три Smile

Не выполняется Sad

И не выполнится. DBI рассчитан на атомарные операции:

# perldoc DBI

...

Some command-line SQL tools use statement terminators, like a semicolon, to indicate the end of a statement. Such terminators should not normally be used with the DBI.

...

Если хочется свести к минимуму общение с базой, можно попробовать запихнуть множественные операции в одну хранимую процедуру.

Share this post


Link to post
Share on other sites

Цитата:

Ганс, зачем при каждом запросе триггер-то создавать? Это ж такой же элемент логики, как и хранимые процедуры, их создал раз при создании самой базы - и они дальше сами работают. Я, по крайней мере, не могу себе представить ситуации, в которой бы возникла необходимость создавать/изменять/удалять триггеры во время работы с базой.

Все очень просто. Триггер - очень удобная штука, но... уменя

получилось создать только один триггер, на одну таблицу базы, а

это, крайне не удобно. Долго мучался с созданием второго, но так

ничего и не вышло... в документации по мускулю сказано, что если

мускуль говорит мол "триггер с таким именем уже есть" - удалите

его и будет вам счастье... на практике - опять национальная

индейская изба... при попытке удалить триггер, который уже есть,

но который никто не создавал и который мешает создать новый

триггер, мускуль говорит - "триггер с таким именем еще не создан"

Smile

Share this post


Link to post
Share on other sites

Цитата:

mysql> create database test; use test;

Query OK, 1 row affected (0.01 sec)

Database changed

mysql> create table a(x int);

Query OK, 0 rows affected (0.02 sec)

mysql> create table b(y int);

Query OK, 0 rows affected (0.01 sec)

mysql> create trigger t1 after insert on a for each row update b set y=new.x;

Query OK, 0 rows affected (0.00 sec)

mysql> create trigger t2 after update on a for each row update b set y=new.x;

Query OK, 0 rows affected (0.01 sec)

mysql> create trigger t3 after update on b for each row update a set x=old.y;

Query OK, 0 rows affected (0.01 sec)

mysqld Ver 5.0.26 for pc-linux-gnu on i686 (Gentoo Linux mysql-5.0.26-r1)

Share this post


Link to post
Share on other sites

В общем, нахожусь в велком ступоре Sad

Суть ступора в том, что не могу написать интерфейс ко всей этой

пакости...

Логика (если она есть) рассуждений:

- Есть некий программный продукт, состоящий из "ядра" и модулей.

в данном случае, "ядро" берет на себя функции "конфигурирования",

всей этой системы, управления модулями, предоставления прав на

их использование и т.д.

- Соответственно, чтобы все это нормально можно было использовать

у этого должен быть какой-то общий интерфейс. Вписывать HTML в

перловый код, как-то очень не хочется...

-- В общем... я в шаблонах и интерфейсах потерялся...

Share this post


Link to post
Share on other sites

Я щас прикручиваю морду к биллингу, html+php. Вообще, имхо, писать морду до более-менее полного оформления биллинга не стоит, очень много потом менять придется. Меня просто с биллингом, заточенным под мою ситуация, сроки торопят, поэтому все делается максимально быстро и соотв. не особо продумано Smile

Как запущу этот биллинг в продакшн, можно будет уже думать о теоретической части другого биллинга, большого и красивого Smile

Share this post


Link to post
Share on other sites
Ну вот у меня, как раз, задача - большой и красивый... но главное
- это даже не красота, а универсальность и следовательно, модуль-
ность.

Сейчас застрял на самой "концепции".
Есть несколько вариантов реализации такой системы:
1. Одним из вариантов, мне видится написание некого приложения,
   с которым будет работать пользователь, а уже это приложение,
   будет работать с модулями.
   Соответственно, это приложение должно предоставлять этим при-
   ложениям соответствующие функции, такие как:
   - работа с базой данных
   - отображение результатов работы модуля
   - и т.д.
   На этапе начала работы с этим же приложением, должна проходить
   аутентификация/авторизация пользователей, общее конфигурирова-
   ние системы и т.п.
   Проблема, с которой я столкнулся, в процессе реализации данно-
   го решения - организация и стандартизация методов общения
   основного приложения и модулей. Еще одной проблемой явилось то
   что в этом приложении, должны быть определены все имеющиеся
   модули, набор которых, может изменяться и для добавления ново-
   го модуля, не должно требоваться, пусть даже мелкое, но пере-
   писывание основного приложения/системы.
2. Вторым вариантом, является то, как построен webmin т.е., есть
   множество модулей, которые являются вполне не зависимыми друг
   от друга, а их общение с пользователем/системой, проходит
   посредством использования библиотек webmin'а. По сути, вариант
   великолепный, но он крайне усложняет использование шаблонов в
   пользовательском интерфейсе так, как за ход работы модуля,
   происходит нескольхо действий типа "стандартный_модуль(принтни
   заголовак)", "стандартный_модуль(принтни_еще_чего-нибудь)",
   стандартный_модуль(заверши_страницу). Другими словами, вывод
   пользовательского интерфейса происходит не единовременно, а
   в процессе всей работы. Так же, в ходе работы, модуль так же
   выводит информацию, не обращаясь к стандартным библиотекам
   webmin, что с одной стороны, позволяет реализовать такую вещь,
   как "интерактивность" приложения (в моем понимании, это неме-
   дленное отображение информации, к примеру, результат ping'а),
   но с другой стороны, делает унификацию пользовательского
   интерфейса, тошлько условной, оставляя правильность работы
   интерфейса исключительно на совести модуля (и дело касается
   но только таких вещей, как интерфейс... ошибка в модуле, при
   такой реализации, может поставить под угрозу, как минимум
   безопасность, а то и работоспособность всей системы, а это,
   как мне кажется, в таком программном продукте, с открытым
   кодом, просто не допустимо).

Вот и сижу я в ступоре... пока писал этот текст (овообще, всей
этой писаниной на форумах, я занимаюсь даже не для того, чтобы
получить от кого-то решение моей задачи, а чтобы мысли по поло-
чкам разложить... да и может кто идейку какую подкинет)... в
общем, пока все это писал, от второго варианта, решил действи-
тельно отказаться (до этого, я и не думал о таких его минусах).

Буду думать над первым вариантом.
По сути, основной траблой для меня, сейчас является реализация
единовременного вывода поьзовательского интерфейса.

заполнение шаблона, у меня происходит сейчас следующим образом:
sub Template {
  my %VAR = %main::VAR;
  my $template = shift;
  open ( TEMPLATE, "$template" );
  $template = join ( '', <TEMPLATE> );
  close ( TEMPLATE );
  while ( $template =~ m/\$VAR\{([0-9,A-Z,a-z,_])\}/ ) {
    for ( $template ) { s\$VAR\{([0-9,A-Z,a-z,_])\}/$VAR{$1}/eg }
  }
  return $template;
}
$VAR{MENU} = Template ( "../htdocs/tpl.menu.html" );
$VAR{MAIN} = Template ( "../htdocs/tpl.main.html" );

недостаток данного метода заключается в том... а не знаю в чем...
еще сам не понял, че мне надо... в общем, еще чего-нибудь приду-
маю, отпишусь 

Share this post


Link to post
Share on other sites
А вот такой, можно сказать, культурный или эстетический, или как
там еще его можно назвать, вопрос - какой код нагляднее:

for ( param ) { $export{$_} = param ($_) if param ($_) ne "" }

или

for ( param ) { if ( param ($_) ne "" ) { $export{$_} = param ($_) } }

? 

Share this post


Link to post
Share on other sites

Hans R. Steiner писал(а) Sun, 19 November 2006 14:45

А вот такой, можно сказать, культурный или эстетический, или как
там еще его можно назвать, вопрос - какой код нагляднее:

for ( param ) { $export{$_} = param ($_) if param ($_) ne "" }

или

for ( param ) { if ( param ($_) ne "" ) { $export{$_} = param ($_) } }

? 

В перловых доках рекомендуется первая форма, но я ее терпеть не выношу Smile Разница, как мне кажется, что и с конструкциями do {} while и while {}

Поэтому я обычно использую do_something unless something, но if something {do something} Smile

Share this post


Link to post
Share on other sites

Составил простенький язык описания тарифов и перловый скрипт для парсенья его. В аттаче.

Share this post


Link to post
Share on other sites
Думаю, к окончанию недели, усперю выложить уже что-нибудь рабочее
но я сейчас акцентирую свое внимание все больше не на автономной
части системы т.е., не на биллинге как таковом, а на пользова-
тельском интерфейсе... с одной стороны, это не совсем верный под-
ход, но с другой, именно эта часть является для меня наиболее
проблемной и от этой проблемы, я хочу избавится в первую очередь.

Share this post


Link to post
Share on other sites

Долго думал, как использовать одну и ту же логику для работы с ВПНом и прямым подключением. Решил трактовать прямое как такую же сессию, как и ВПН. Время начала, окончания сессии и состояние ее определяются по факту наличия машины в сети, то есть по потенциальной возможности пользоваться инетом. Тем легче это осуществить, что от счетчиков интерфейсов отказался, приделываю netflow.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
Sign in to follow this  

×
×
  • Create New...