Он реализует перенос списка классов трафика (требуется исправление ошибки в api.xml), перенос списка тарифов, перенос списка услуг, перенос списка пользователей со статическими и vpn-адресами, а также перенос базы карточек оплаты.
Он не реализует перенос групп пользователей, в связи с отсутствием поддержки повышенных привилегий несистемных пользователей в UTM5, а так-же ряда других данных.
Он реализует лишь частичное воспроизведение логики UTM4, требует "допиливания" и не предназначен для сдачи системы "под ключ"

Фактически это только заготовка, которую можно использовать в целях обучения.
utm5_import.pl
Код: Выделить всё
#!/usr/bin/perl
use Mysql;
use Ourfa;
use Socket;
use Net::Netmask;
$prefix="/netup"; # путь к файлу конфигурации UTM4
open (CONFIG, "$prefix/utm/utm.cfg"); @config = <CONFIG>; close (CONFIG);
foreach $line (@config) {
if ($line =~ m/^([^#].+?)=(.*)$/) {
$$1 = $2;
};
};
$dbh = Mysql->connect($database_host,$database,$database_login,$database_password); $dbh->Query("SET NAMES utf8"); # подключение к базе UTM
my $ourfa = Ourfa->new( # подключение к ядру UTM5 по протоколу URFA
api_xml_dir => "/netup/utm5/xml",
server => 'localhost',
login => $ENV{OURFA_LOGIN} || 'init',
password => $ENV{OURFA_PASSWORD} || 'init',
ssl_cert => '/netup/utm5/admin.crt',
ssl_key => '/netup/utm5/admin.crt',
ssl => 'rsa_cert'
);
$services = $ourfa->rpcf_get_services_list(); if($services->{services_count}>0){ die "This script intended to work on clean systems only.\n"; } # проверка отсутствия услуг, работаем только на чистой системе
#==============================================================================================================================================================================================================================================
$result = $ourfa->rpcf_get_tclasses(); foreach my $tcls (@{$result->{'array-1'}}){ $ourfa->rpcf_remove_tclass(tclass_id=>$tcls->{tclass_id}); } # удаление существующих классов трафика
$sth1 = $dbh->query("SELECT * FROM traffic_classes WHERE tag LIKE 'name'"); # получение списка названий классов
while(my %tclass1 = $sth1->fetchhash){
my @list;
my $display = 0;
my $graph = 0;
my $graph_color = 0;
my $class_id = $tclass1{cid};
my $tclass_name = $tclass1{value}; print "tclass: $tclass_name\n";
$sth2 = $dbh->query("SELECT value FROM traffic_classes WHERE cid = $class_id AND tag LIKE 'graph'"); # выборка атрибута заливки/отображения (заливка не работает)
if(my %tclass2 = $sth2->fetchhash){ $graph = 1-$tclass2{value}; $display = 1; } else { $graph = 0; $display = 0; }
$sth2 = $dbh->query("SELECT value FROM traffic_classes WHERE cid = $class_id AND tag LIKE 'graph_color'"); # выборка цвета кривой
if(my %tclass2 = $sth2->fetchhash){ $graph_color = $tclass2{value}; }
$sth2 = $dbh->query("SELECT * FROM traffic_classes_nets WHERE cid = $class_id"); # получение списка подклассов трафика
while(my %tclass2 = $sth2->fetchhash){
push @list, { # построение списка хешей подклассов трафика
saddr => inet_aton($tclass2{ip_from}), # ip адрес подсети источника
saddr_mask => inet_aton($tclass2{netmask_from}), # маска подсети источника
daddr => inet_aton($tclass2{ip_to}), # ip адрес подсети адресата
daddr_mask => inet_aton($tclass2{netmask_to}), # маска подсети адресата
src_as => inet_aton($tclass2{src_as}), # номер автономной системы источника
dst_as => inet_aton($tclass2{dst_as}), # номер автономной системы адресата
sport => $tclass2{srcport}, # порт источника
dport => $tclass2{dstport}, # порт назначения
input => $tclass2{input}, # входящий интерфейс
output => $tclass2{output}, # исходящий интерфейс
tcp_flags => $tclass2{tcp_flags}, # флаги протокола TCP
proto => $tclass2{prot}, # протокол
tos => $tclass2{tos}, # type of service
nexthop => inet_aton($tclass2{nexthop}), # следующий маршрутизатор
ip_from => inet_aton('0.0.0.0'), # ip адрес маршрутизатора
use_src_as => 0, # не использовать номер АС источника
use_dst_as => 0, # не использовать номер АС адресата
use_sport => 0, # не использовать порт источника
use_dport => 0, # не использовать порт адресата
use_input => 0, # не использовать номер входящего интерфейса
use_output => 0, # не использовать номер исходящего интерфейса
use_tcp_flags => 0, # не использовать флаги TCP
use_proto => 0, # не использовать номер протокола
use_tos => 0, # не использовать TOS
use_nexthop => 0, # не использовать следующий маршрутизатор
skip => 0 # не пропускать классификацию
};
};
my %tclass=( # подготовка хеша описателя класса трафика
tclass_id => $class_id, # класс трафика
tclass_name => $tclass_name, # название класса трафика
graph_color => hex $graph_color, # цвет кривой на графике
is_display => $display, # статус отображения кривой
is_fill => $graph, # статус заливки (не используется)
time_range_id => 0, # номер временной зоны
dont_save => 0, # сохранять детальную статистику
local_traf_policy => 0, # метод учёта межабонентского траффика [0 -- на получателя]
tclass_count => $#list+1, # длина списка подклассов
subclasses => [@list] # список подклассов
);
$ourfa->rpcf_add_tclass2(%tclass); # запись класса в UTM5
};
#==============================================================================================================================================================================================================================================
$ourfa->rpcf_add_iptraffic_service_ex( # запись первичной услуги передачи данных, являющейся родителем для дочерних услуг, связанных с тарифными планами
# -1 -- шаблон услуги (используется для создания дочерних услуг, связанных с тарифными планами)
parent_id => -1, # 0 -- обычная услуга (может быть подключена непосредственно пользователю)
# n -- номер родительской услуги (шаблона) при связывании с тарифным планом
tariff_id => 0, # номер родительского тарифного плана
service_name => 'Передача данных', # описание услуги
comment => 'корневая услуга', # комментарий
link_by_default => 0, # 1 -- подключать по умолчанию при добавлении тарифа (в интерфейсе администратора)
is_dynamic => 0, # 1 -- использование динамических адресов
cost => 0, # периодическая составлющая стоимости
discount_method => 3, # метод списания (1 -- в начале; 2 -- в конце; 3 -- в течение всего учётного периода)
null_service_prepaid => 0, # 1 -- обнуление предоплаченного трафика
num_of_borders => 0,
num_of_prepaid => 0,
num_of_groups => 0
);
$sth1 = $dbh->query("SELECT * FROM products_services"); # импорт тарифных планов
while(my %utm_product = $sth1->fetchhash){
my $root_service = $ourfa->rpcf_add_periodic_service_ex( # запись услуг абонентской платы, являющихся родителями для дочерних услуг, связанных с тарифными планами
# -1 -- шаблон услуги (используется для создания дочерних услуг, связанных с тарифными планами)
parent_id => -1, # 0 -- обычная услуга (может быть подключена непосредственно пользователю)
# n -- номер родительской услуги (шаблона) при связывании с тарифным планом
tariff_id => 0, # номер родительского тарифного плана
service_name => $utm_product{prod_name}, # описание услуги
comment => 'периодическая услуга', # комментарий
link_by_default => 0, # 1 -- подключать по умолчанию при добавлении тарифа (в интерфейсе администратора)
cost => $utm_product{price}, # периодическая составлющая стоимости
discount_method => 3 # метод списания (1 -- в начале; 2 -- в конце; 3 -- в течение всего учётного периода)
);
%new_product_id = (%new_product_id, $utm_product{id} => $root_service->{service_id}); # построение ассоциативного массива старый_product_id->новый_product_id
};
$sth1 = $dbh->query("SELECT * FROM tariffs_current"); # импорт тарифных планов
while(my %utm = $sth1->fetchhash){
my $tariff = $ourfa->rpcf_add_tariff_new( # запись тарифных планов
name => $utm{name}, # название тарифа
balance_rollover => $utm{endperiod_zeroing_balance}, # 1 -- обнуление счёта в конце учётного периода
comments => '' # комментарии
);
%new_tp = (%new_tp, $utm{tid} => $tariff->{tp_id}); # построение ассоциативного массива старый_tariff_id->новый_tariff_id
print "tariff: $utm{name}\n";
my @borders_list; $sth2 = $dbh->query("SELECT * FROM tariff_traffic_borders WHERE tid = $utm{tid}"); # импорт границ тарификации трафика
while(my %utm_traffic_borders = $sth2->fetchhash){
push @borders_list, {tclass_b => $utm_traffic_borders{tc_id}, size_b => $utm_traffic_borders{border}, cost_b => $utm_traffic_borders{cost}};
};
$ourfa->rpcf_add_service_to_tariff( # запись услуг передачи данных, связанных с тарифными планами
parent_id => 1, # родительская услуга-шаблон 'Передача данных'
tariff_id => $tariff->{tp_id}, # номер тарифного плана
service_name => 'Аренда IP адреса', # название услуги
service_type => 3, # тип услуги [3 -- 'передача IP-трафика']
comment => '', # комментарий
link_by_default => 1, # подключать по умолчанию
is_dynamic => 0, # не использовать динамические адреса
cost => 0, # стоимость
periodic_type => 0, # периодичность списания (не используется)
discount_method => 3, # метод списания [3 -- в течение всего учётного периода]
null_service_prepaid => 0, # не обнулять предоплаченный трафик
num_of_borders => $#borders_list+1, # число границ тарификации для [платного] трафика
borders_list => [@borders_list], # список границ тарификации трафика
num_of_prepaid => 0,
num_of_groups => 0
);
$sth2 = $dbh->query( # Внимание! cost >= price !
"SELECT products_services_tariffs.prod_code as prod_code, products_services.prod_name as name, products_services.is_periodic as is_periodic, products_services.discount_at_begin as discount_at_begin,
products_services.recalc_prod_infact as recalc_prod_infact, products_services.price * products_services_tariffs.qnt as cost
FROM tariffs_current, products_services_tariffs, products_services
WHERE tariffs_current.tid = products_services_tariffs.tariff_id AND products_services_tariffs.prod_code = products_services.id AND products_services_tariffs.tariff_id = $utm{tid}"
);
while(my %utm_periodics = $sth2->fetchhash){
$ourfa->rpcf_add_service_to_tariff( # запись периодических услуг (абонентской платы), связанных с тарифными планами
parent_id => $new_product_id{$utm_periodics{prod_code}}, # родительская услуга-шаблон
tariff_id => $tariff->{tp_id}, # номер тарифного плана
service_name => $utm_periodics{name}, # название услуги
service_type => $utm_periodics{is_periodic}+1, # тип услуги [1 -- разовая услуга, 2 -- периодическая услуга]
comment => '', # комментарий
link_by_default => 1, # подключать по умолчанию
is_dynamic => 0, # не использовать динамические адреса
cost => $utm_periodics{cost}, # стоимость
periodic_type => 0, # периодичность списания (не используется)
discount_method => 3-2*$utm_periodics{discount_at_begin} # метод списания [1 -- в начале учётного периода; 3 -- в течение всего учётного периода]
);
};
};
#==============================================================================================================================================================================================================================================
$search = $ourfa->rpcf_search_users_new( # получение списка существующих пользователей
poles_count => 1, # длина списка возвращаемых полей
poles => [{pole_code_array=>2}], # список возвращаемых полей (2 -- 'login')
select_type => 1,
patterns_count => 1, # длина списка шаблонов поиска
patterns => [{what_id=>2, criteria_id=>1, pattern=>''}] # список полей (2 -- 'login'), критериев (1 -- 'LIKE') и образцов для поиска
);
for(my $i = 0; $i < $search->{user_data_size}; $i++){ %exist_users = (%exist_users, @{$search->{'array-1'}}[$i]->{login} => @{$search->{'array-1'}}[$i]->{user_id}); }; # построение ассоциативного массива логин->идентификатор
$sth = $dbh->query("SELECT * FROM users"); # импорт списка пользователей UTM
while(my %utm = $sth->fetchhash){ my $ip_service_id; my @ip_groups; $count++; $login=lc($utm{login}); print "user: $login\n";
if(! defined $exist_users{$login}){ # проверка отсутствия добавляемого пользователя в базе UTM5
my $user = $ourfa->rpcf_add_user( # добавление пользователя
login => $login, # login
password => $utm{password}, # пароль доступа
full_name => $utm{full_name}, # полное имя
comments => $utm{comments}, # комментарии
email => $utm{email}, # адрес электронной почты
is_juridical => $utm{is_juridical}, # статус юридического лица
act_address => $utm{actual_address}, # фактический адрес
jur_address => $utm{juridical_address}, # юридический адрес
tax_number => $utm{tax_number} # ИНН
);
my $account = $ourfa->rpcf_add_account( # добавление учётной записи
user_id => $user->{user_id}, # идентификатор пользователя
balance => $utm{bill}, # баланс
credit => $utm{credit}, # кредит
is_blocked => $utm{block}, # статус блокировки
int_status => $utm{fw_on} # статус доступа в интернет
);
my $discount_period = $ourfa->rpcf_add_discount_period_return( # добавление учётного периода
periodic_type => 1048576, # [1048576 -- другая длина]
start_date => $utm{ab_pstart}, # начало периода
expire_date => $utm{ab_pend}, # окончание периода
custom_duration => $utm{ab_pend}-$utm{ab_pstart}, # длительность периода
discount_interval => 168 # число списаний в неделю [168 -- каждый час] !!!FIXME!!!
);
my $tariff_link = $ourfa->rpcf_link_user_tariff( # подключение пользователю тарифного плана
user_id => $user->{user_id}, # идентификатор пользователя
account_id => $account->{account_id}, # номер учётной записи (в общем случае у одного пользователя может быть несколько учётных записей)
tariff_current => $new_tp{$utm{tariff}}, # текущий тарифный план
tariff_next => $new_tp{$utm{tariff_next}}, # тарифный план следующего учётного периода
discount_period_id => $discount_period->{discount_period_id}, # номер учётного периода
tariff_link_id => 0 # номер тарифной связки
);
my $tariff = $ourfa->rpcf_get_tariff(tariff_id => $new_tp{$utm{tariff}}); # получение списка услуг, связанных с текущим тарифом
for(my $i = 0; $i < $tariff->{services_count}; $i++){
if(@{$tariff->{'array-1'}}[$i]->{service_type_array} == 2){ # получение периодических услуг
my %new_service_link = (
user_id => $user->{user_id}, # идентификатор пользователя
account_id => $account->{account_id}, # номер учётной записи
service_id => @{$tariff->{'array-1'}}[$i]->{service_id_array}, # номер услуги
service_type => 2, # тип услуги [2 -- периодическая услуга]
tariff_link_id => $tariff_link->{tariff_link_id}, # номер тарифной связки
slink_id => 0, # номер сервисной связки
is_blocked => 0, # статус блокировки
discount_period_id => $discount_period->{discount_period_id}, # номер учётного периода
# start_date => $utm{ab_pstart}, # начало действия услуги !!!FIXME!!!
# expire_date => $utm{ab_pend}, # окончание действия услуги !!!FIXME!!!
unabon => 1, # пересчёт абонентской платы (необходимо для корректного переноса суммы)
unprepay => 1 # пересчёт предоплаченных услуг
);
eval{ # перехват ошибок
$ourfa->rpcf_add_service_to_user(%new_service_link); # запись периодических услуг пользователю
};
}; if(@{$tariff->{'array-1'}}[$i]->{service_type_array} == 3){ $ip_service_id = @{$tariff->{'array-1'}}[$i]->{service_id_array} }; # получение id услуги передачи ip трафика
};
my %new_service_link = (
user_id => $user->{user_id}, # идентификатор пользователя
account_id => $account->{account_id}, # номер учётной записи
service_id => $ip_service_id, # номер услуги
service_type => 3, # тип услуги [3 -- передача IP траффика]
tariff_link_id => $tariff_link->{tariff_link_id}, # номер тарифной связки
slink_id => 0, # номер сервисной связки
is_blocked => 0, # статус блокировки
discount_period_id => $discount_period->{discount_period_id}, # номер учётного периода
# start_date => $utm{ab_pstart}, # начало действия услуги !!!FIXME!!!
# expire_date => $utm{ab_pend}, # окончание действия услуги !!!FIXME!!!
unabon => 1, # пересчёт абонентской платы
unprepay => 1 # пересчёт предоплаченных услуг
);
my @utm_net = split(' ', $utm{ip}); # разбор списка статических IP-адресов
foreach $net (@utm_net){ my $block = new Net::Netmask ($net); # добавление в список не-VPN адресов
push @ip_groups, {
ip_address => inet_aton($block->base()), # ip адрес
mask => inet_aton($block->mask()), # маска ip адреса
iptraffic_login => '', # login
iptraffic_password => '', # пароль
ip_not_vpn => 1, # тип адреса [1 -- не VPN]
dont_use_fw => 0 # применять правила firewall
};
};
$sth2 = $dbh->query("SELECT * FROM ip_addr WHERE uid = $utm{id}"); # получение списка VPN адресов
while(my %vpn = $sth2->fetch_hash){ # добавление в список VPN адресов
push @ip_groups, {
ip_address => inet_aton($vpn{ip_addr}), # ip адрес
mask => inet_aton($vpn{netmask}), # маска ip адреса
iptraffic_login => '', # login
iptraffic_password => '', # пароль
ip_not_vpn => 0, # тип адреса [0 -- VPN]
dont_use_fw => 1 # не применять правила firewall
};
};
eval{ # экранирование ошибок
if($#ip_groups>=0){ # запись ip адресов пользователю
$ourfa->rpcf_add_service_to_user(%new_service_link, ip_groups_list => [@ip_groups]);
};
};
$ourfa->rpcf_save_account( # дополнение учётной записи
account_id => $account->{account_id}, # номер учётной записи
discount_period_id => $discount_period->{discount_period_id}, # номер учётного периода
credit => $utm{credit}, # кредит
is_blocked => $utm{block}, # статус блокировки !!!FIXME!!!
dealer_account_id => 0, # номер учётной записи дилера
vat_rate => 0, # НДС
sale_tax_rate => 0, # НСП
int_status => $utm{fw_on}, # статус доступа в интернет
block_recalc_abon => 0, # пересчитывать абонентскую плату при системной блокировке !!!FIXME!!!
block_recalc_prepaid => 0, # пересчитывать предоплаченные услуги при системной блокировке !!!FIXME!!!
unlimited => $utm{discount_eq}
);
} else { print "Modifying of existing users are not implemented...\n"; }
};
#==============================================================================================================================================================================================================================================
$sth = $dbh->query("SELECT * FROM icards"); # импорт базы карт оплаты
while(my %card = $sth->fetchhash){
$ourfa->rpcf_card_add(
secret => $card{card_serial}, # секретный код
balance => $card{card_nominal}, # номинал карты
currency => 810, # валюта: российские рубли
expire => $card{card_expire_date}, # срок действия карты
days => 0,
is_used => 1-$card{card_status}, # статус активации карты
tp_id => 0 # 0 -- применима на любом тарифном плане
);
};