Проектирование API ISP-биллинга для районной домовой сети.

CRL

Новичок
Проектирование API ISP-биллинга для районной сети.

Здравствуйте.
Столкнулся с проблемой проектирования API биллинга в смысле службы ISP районной сети.
Целевая сеть - это 90-110 машин, на уровне доступа - неуправляемое оборудование.
API должно с одинаковым успехом использоваться и web-мордой, и в CLI.
Проект предполагается обёрткой для iptables + iproute + dhcpd + ip-sentinel.
Не имея опыта проектирования подобных систем, придерживался такой схемы:

1) Описал ряд интерфейсов для базовых классов:
- Интерфейс взаимодействия с хранилищами
- Интерфейс взаимодействия с журналами
- Интерфейс, описывающий механизм проверки входных данных (счёл необходимым в этом случае ввести интерфейс потому, что входные данные могут проверятся, как минимум, 2 способами: средствами РНР и средствами ОС)

2) Создал необходимые реализации интерфейсов
- Класс для MySQL-хранилища
- Класс для работы с текстовыми журналами
- Класс проверки входных данных (ip, mac) средствами РНР

3) Логически разделил всю систему на отдельные модули, каждый из которых отвечает за работу с одним внешним компонентом: iptables, iproute и др. Каждый модуль имеет собственный конфиг.

После этого получилась примерно следующая картина:
- объект класса-модуля читает свой конфиг и выполняет инициализацию необходимых ему объектов служебных классов
- объеты служеных классов сохранятются в виде приватных свойств класса-модуля и используются в публичных методах
- объект главного класса биллинга создаёт объекты классов-модулей и сохраняет их в виде публичных свойств; доступ извне API к её функционалу осуществляется через эти публичные свойства
- каждый публичный метод любого объекта класса-модуля может вернуть следующие значения: FALSE, TRUE или небулевый результат(массив, строка), экземпляр объекта-исключения
- отлов исключений поручается приложению, использующему API

В черновом варианте реализовал всё это дело для двух модулей: GateMod(iptables) и SpeedMod(iproute). Попробовал на тестовой сети из 4 компов - работает. Но при этом у меня почему-то возникает чувство, что я "сам себя где-то н@#бал, а где - не пойму." Отсюда и вопрос: кто сталкивался с проектированием подобных вещей - как такое должно выглядеть? будет ли описанное жизнеспособно? И еще один вопрос - иерархия исключений. Сейчас никакой иерархии нет - исключение выбрасывается в случае фатала во внешних компонентах и в случае нарушения целостности последовательности исполнения команд внешними компонентами. Т.е., уровень критичности ошибок сильно задран и слабо раеализован функционал восстановления. Как быть? Создавать собственное исключение для каждого модуля? Создавать класс, реализующий функционал восстановления?

Для наглядности приведу пример.

Один из интерфейсов:
PHP:
<?php

    interface iStorage
    {
        public function open($params);
        public function close();
        public function exec($query_tpl, $query_params);
        public function res_count($query_tpl, $query_params);
        public function res_row_format($query_tpl, $query_params);
        public function res_assoc_format($query_tpl, $query_params);
    }

 ?>
Он реализуется:
PHP:
<?php
...
class MySQLStorage implements iStorage
    {
        private $db_marker;
        
        private function sql_wrap($query_tpl, $query_params)
        {
            ...
        }
        
        public function __destruct()
        {
            $this->close();
        }
        
        public function open($params)
        {
            ...
            $this->db_marker = $db_marker;
            return true;
        }
        
        public function exec($query_tpl, $query_params)
        {
            $query = $this->sql_wrap($query_tpl, $query_params);
            ...
        }
        ...
        
?>
Класс-модуль:
PHP:
<?php

    ...

    // Модуль шлюза
    class GateMod
    {
        // Настройки
        private $DB_config;
        private $log_config;
        private $gate_config;
	
        private $DB;
        private $gate_log;
	
        private function gate_rollback()
        {
            $restore_comand = "iptables-restore ".$this->gate_config["rules_dir"]."/".$this->gate_config["main_rules"]." 2> /dev/null";
            exec($restore_comand, $box, $retval);
            if($retval != 0)
            {
                return false;
            }
            return true;
        }
	
        private function gate_save()
        {
            $save_comand = "iptables-save >".$this->gate_config["rules_dir"]."/".$this->gate_config["main_rules"]." 2> /dev/null";
            exec($save_comand, $box, $retval);
            if($retval != 0)
            {
                return false;
            }
            return true;
        }
	
        public function __construct()
        {
            // Подключение файла конфигурации
            require_once "GateMod.config.php";
	    
            $this->DB_config = $storage;
            $this->log_config = $log;
            $this->gate_config = $gate;
	    
            $this->common_log = new TextLog;
            $this->common_log->open($log["common"]);
	    
            $DBFactory = new StorageFactory;
            $this->DB = $DBFactory->get_instance($storage["type"]);
            if($this->DB === false)
            {
                throw new Exception("Не удалось загрузить драйвер хранилища ".$storage["type"].".");
            }
            $this->DB->open($this->DB_config);
        }
	
        // Корректное закрытие ресурсов
        public function __destruct()
        {
            $this->DB->close();
            $this->common_log->close();
        }
	
        /// МЕТОДЫ ШЛЮЗА ///
        public function gate_add_user($ip)
        {
            ...
        }
        
        public function gate_del_user($ip)
        {
            ...
        }
        
        ...
	
?>
Основной класс биллинга:
PHP:
<?php
    
    ...
    
    class Billing
    {
       public $gate;
       public $shaper;
       ...
       
       public function __construct()
       {
            ...
            $this->gate = new GateMod;
            $this->shaper = new SpeedMod;
            ...
       }
       ...
    }
   
 ?>
-~{}~ 07.04.09 13:21:

Если не касаться идеологической правильности подхода к построению таких систем, то основная непонятка сейчас сводится к следующему.
Серьёзную проблему представляют ошибки, связанные с нарушением целостности последовательности исполнения внешних команд. Например, метод gate_edit_user() модуля GateMod редактирует уже существующего пользователя подсистемы шлюза. Для простоты будем считать, что редактирование - это просто замена одного ip на другой, и внутри метода происходит последовательный вызов внешнего приложения
(iptables) сначала для удаления юзера с исходным ip, а потом для добавления юзера с конечным ip. И вот если удаления пользователя проходит успешно, а добавление крашится - возникает ошибка нарушения последовательности исполнения. В данном случае, самая главная задача - откат шлюза до состояния, предшествующего началу операции. У меня сейчас это реализовано так: любой метод, вносящий изменения в настройки шлюза, предварительно делает бэкап состояния (приватный метод модуля шлюза gate_save()). В ходе выполнения целостность последовательности проверяется на каждом этапе, если она нарушатся - выполняется откат (приватный метод шлюза gate_rollback()), после этого выбрасывается исключение, которое отлавливается уже за пределами API и носит чисто информационный характер, потому как в его обработке нет никакого кода восстановления. Вот в связи с этим и вопросы - правильно ли держать такие операции, как создание бэкапа и откат, внутри самого класса модуля? Или это должен быть какой-то другой класс? Где выбрасывать исключения? Где их ловить и обрабатывать?
 

DiMA

php.spb.ru
Команда форума
ты не мог бы свой боян чуток укоротить? Второй день мозг отказывается это читать =) И, видимо, не у меня одного...
 

CRL

Новичок
Автор оригинала: DiMA
ты не мог бы свой боян чуток укоротить? Второй день мозг отказывается это читать =) И, видимо, не у меня одного...
Я не ставил цели кого-то запарить - просто хотел, чтобы было понятно, в чём суть проблемы. Если нужно порезать пост - не вопрос: говори, что убрать - я уберу.

А, нет. Не могу чуток укоротить - истекло время редактирования. =(
 

CRL

Новичок
Автор оригинала: iceman
дык, напиши в ответе урезанную версию )
Если коротко, тогда так:
- выдержит ли скриптовый биллинг нагрузку при обработке 90-110 компов
- где должен находиться код создания точки восстановления: в самом методе класса, который выполняет операцию или в другом месте?
- где должен находиться код восстановления (отката на точку восстановления): в методе класса, в котором возникла ошибка, или в обработчике исключений
- нужна ли иерархия исключений и если нужна, как она должна выглядеть
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
я не думаю, что кто-то может дать нормальный ответ на эти вопросы, не зная бизнес-логики, не изучив детально в структуру твоего приложения и оборудования

получается вопрос "есть ли жизнь на Марсе?", ответом на который может быть лишь "а в Киеве дядька"
 

CRL

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

получается вопрос "есть ли жизнь на Марсе?", ответом на который может быть лишь "а в Киеве дядька"
В том черновом виде, в котором система существует сейчас, она использует классы-обёртки для стандартных системных утилит + данные счётчиков пишутся в базу, а логирование идёт не в syslog, а в собственные журналы. Веб-интерфейса пока нет, ручной запуск заданий идёт с терминала, периодически исполняемые задания (съем данных со счётчиков и пр.), крутятся в кроне. Железо используется десктопное: 3ГГц, шина 800, 4 ГБ RAM, SATA II 120 Gb.
Но, в общем, ты прав, наверное - в это всё вникать нужно недосуже, так, навскидку сказать что-то сложно.

Думаю, стоит закрыть тему.

-~{}~ 07.04.09 18:12:

Автор оригинала: grigori
и как же нам понимать фразу "ISP-биллинг"? :)
Биллинг в том смысле, в котором его понимают провайдеры: комплексная система управления шлюзом - управление доступом, управление скоростью, статистика по трафику, оплата и пополнение баланса и пр.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
>Биллинг в том смысле, в котором его понимают провайдеры.
Это не форум профайдеров - мы тебя не понимаем, извини.
 

findnext

Новичок
CRL
биллинг- это сего лишь программа, бухгалтерская книга (ledger)
организовать её можно разными способами. Поэтому будет луче еслт ты всётаки опишешь как должна эта программа работать. Классы и обёртки абсолютно не интересуют.

-~{}~ 07.04.09 14:19:

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

CRL

Новичок
Автор оригинала: findnext
CRL
биллинг- это сего лишь программа, бухгалтерская книга (ledger)
организовать её можно разными способами. Поэтому будет луче еслт ты всётаки опишешь как должна эта программа работать. Классы и обёртки абсолютно не интересуют.
Попробую еще раз...
Всё, о чём идёт речь, если не принимать во внимание веб-интерфейс, - это АПИ для приложений клиентской стороны, набор методов, которые используются приложениями, написанными на РНР.
Например, я хочу Васе Пупкину разрешить ходить в инет через мой сервак. Для этого я руками прописываю для него правила в системном файрволе(iptables), руками создаю правила ограничения его скорости в шейпере(iproute), руками создаю правило выдачи ему ip в дистрибуторе dhcpd ну и так далее... Этих правил могут быть ну просто горы. Чтобы облегчить себе работу по созданию правил, я использую API биллинга - одним методом я создаю весь набор правил, связанных с доступом, другим - весь набор правил, связных с ограничением скорости и т.д. Я ниасилил, может ли РНР самостоятельно загружать\выгружать модули ядра, работать с библиотеками очередей типа netfilter_queue и пр., но РНР может выполнять внешние для себя приложения через exec(), passthru() и пр. и это положил в основу работы системы - в методах находятся цепочки вызовов внешних утилит(iptables, tc и пр.) с проверкой возвращяемых значений. Внутри метода цепочка вызовов неразделима, если последовательность вызовов нарушена, возникает серьезный кикоз и весь шлюз начинает работать некорректно. Отсюда все мои вопросы: код восстановления, иерархии исключений и т.д.

Проще написать не умею. Если еще остались неясности, то я, наверное, ищу стену и убиваюсь.
 

флоппик

promotor fidei
Команда форума
Партнер клуба
Нипанятна. Если тебе непонятно как работают исключения - открой доку, да почитай. Если понятно - не открывай доку. Если тебе интересны методы обработки ошибок - задай вопрос про обработку ошибок. Нам, или в гугл. А ты вывалил кучу текста, с неясными проблемами - впору к психологу идти: «доктор, меня что то беспокоит... но я не понимаю, что!»

Наверное, так.

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

findnext

Новичок
флоппик
насколько я понял, ТС хочет чтобы программа на php могла узнавать mac адреса сетевых карт, блокировать их, выполнять всякие манипуляции и всё в таком духе.

-~{}~ 07.04.09 14:58:

тут нужно не php а DELPHI или C++
 

CRL

Новичок
Автор оригинала: findnext
http://ru.php.net/network всё что может описано в мануале.

для остального нужно писать дополнительные php extensions на C++
Да оно понятно - если чего-то нет, это можно дописать...
Я несколько о другом говорил: РНР может решать задачи такого типа как у меня и стандартными средствами.
PHP:
exec($comand, $out, $retval);
- прекрасно справляется.

Весь вопрос в обработке крашей внешних приложений.
PHP:
// делаем backup шлюза
$this->gate_backup();

// замена IP для пользователя "Вася" c 192.168.0.10 на 192.168.0.20 в методе модуля подсистемы шлюза

// удаляем старый ip
exec($del_old_ip, $out, $retval);
if($retval != 0)
{
    // краш не критичен, т.к., не ведет к непредвиденному изменению конфига шлюза
    return false;
}

// добавляем новый ip
exec($add_new_ip, $out, $retval)
if($retval != 0)
{
    // краш критичен: старый ип удалён, новый не добавлен

    // пробуем всё восстановить
    $rollback_stat = $this->gate_rollback();
    if($rollback_stat === false)
    {
        // восстановить не удалось
        throw new Exception("Шеф! Всё пропало! Гипс снимают! Клиент уезжает!");
    }
}
Вот в чем свысл проблемы - нужно ли мне пытаться восстановить состояние шлюза ЗДЕСЬ же или же сразу кидать исключение, и пусть его обработчик где-то неизвестно где разбирается, что делать?
 

fixxxer

К.О.
Партнер клуба
о специалисты :)

управлять маршрутизаторами все равно откуда :) открыл телнетом да управляй

советую только не заниматься велосипедостроением и купить готовый продукт, типа того же utm, дешевле выйдет.
 

CRL

Новичок
Автор оригинала: fixxxer
о специалисты :)

управлять маршрутизаторами все равно откуда :) открыл телнетом да управляй

советую только не заниматься велосипедостроением и купить готовый продукт, типа того же utm, дешевле выйдет.
У него лицензия, начиная с 50 душ, шибко дорогая, а то и правда купил бы и не елозил бы тут всем по извилинам.
А телнетом - эт если у тебя либо что-то совсем дешовое и там 4 команды на 2 админов, либо совсем дорогое, когда админы ваще не нужны. =)
А в моем случае просто серв.
 
Сверху