пятница, 9 сентября 2011 г.

Радиус-обманка для тестирования ISG


При тестировании одной биллинговой системы (какой - не скажу - не хочу рекламировать откровенно недоделанный продукт) долго не могли определить - кто виноват в запуске дублирующихся сессий ISG - биллинг или Cisco. Вот и возникла идея создать некую "дурилку" - радиус-сервер, отдающий фиксированные ответы, выглядящие с точки зрения ISG как правильные. Создавалось это чудовище за полчаса из свежеустановленного freeradius-1.1.3 (платформа - CentOS 5.6).
Для начала добавляем нашу циску и локалхост (для упрощения тестирования) в /etc/raddb/clients.conf:



client 10.1.60.4 {
secret = mysecr
shortname = tstnas
nastype = cisco
}
client 127.0.0.1 {
        secret = mysecr
        shortname = localhost
        nastype = other
}

А сами ответы помещаем в /etc/raddb/users:

# сперва - пользователи
usr1145 Password == "d038bec6", Service-Type == Framed-User
        Session-Timeout = 86400,
        Framed-Protocol = PPP,
        Framed-IP-Address = 10.1.70.101,
        Framed-IP-Netmask = 255.255.255.255,
        Acct-Interim-Interval = 300,
        Cisco-AVPair = "accounting-list=BILL",
        Filter-Id = "Consum.out",
        Cisco-Account-Info = "ALOCAL",
        Cisco-Account-Info += "NLOCAL",
        Cisco-Account-Info += "AWORLDIN",
        Cisco-Account-Info += "NWORLDIN",
        Cisco-Account-Info += "AWORLDOUT",
        Cisco-Account-Info += "NWORLDOUT"


usr1146 Password == "f577acec", Service-Type == Framed-User
        Session-Timeout = 86400,
        Framed-Protocol = PPP,
        Framed-IP-Address = 10.1.70.102,
        Framed-IP-Netmask = 255.255.255.255,
        Acct-Interim-Interval = 300,
        Cisco-AVPair = "accounting-list=BILL",
        Filter-Id = "Consum.out",
        Cisco-Account-Info = "ALOCAL",
        Cisco-Account-Info += "NLOCAL",
        Cisco-Account-Info += "AWORLDIN",
        Cisco-Account-Info += "NWORLDIN",
        Cisco-Account-Info += "AWORLDOUT",
        Cisco-Account-Info += "NWORLDOUT"  


# потом - реавторизация препейд-сервиса
usr1145 Password == "cisco", Service-Type == Framed-User, Cisco-Service-Info == "NWORLDIN"
        Cisco-Control-Info = "QV1234567895",
        Idle-Timeout = 90


usr1146 Password == "cisco", Service-Type == Framed-User, Cisco-Service-Info == "NWORLDIN"
        Cisco-Control-Info = "QV1234567896",
        Idle-Timeout = 90


# и наконец - сами сервисы
WORLDIN Password == "cisco", Service-Type == Dialout-Framed-User
        Cisco-AVPair = "ip:traffic-class=output access-group name world-out priority 40",
        Cisco-AVPair += "ip:traffic-class=in default drop",
        Cisco-AVPair += "ip:traffic-class=out default drop",
        Cisco-Service-Info = "IALL-INET-IN",
        Cisco-Service-Info += "MC",
        Cisco-Service-Info += "TP",
        Cisco-AVPair += "prepaid-config=BILL",
        Acct-Interim-Interval = 300


WORLDOUT Password == "cisco", Service-Type == Dialout-Framed-User
        Cisco-AVPair = "ip:traffic-class=input access-group name world-in priority 30",
        Cisco-AVPair += "accounting-list=BILL",
        Cisco-AVPair += "ip:traffic-class=in default drop",
        Cisco-AVPair += "ip:traffic-class=out default drop",
        Cisco-Service-Info = "IALL-INET-OUT",
        Cisco-Service-Info += "MC",
        Cisco-Service-Info += "TP",
        Acct-Interim-Interval = 1800


LOCAL   Password == "cisco", Service-Type == Dialout-Framed-User
        Cisco-AVPair = "ip:traffic-class=input access-group name local-in priority 20",
        Cisco-AVPair += "ip:traffic-class=output access-group name local-out priority 20",
        Cisco-AVPair += "accounting-list=BILL",
        Cisco-AVPair += "ip:traffic-class=in default drop",
        Cisco-AVPair += "ip:traffic-class=out default drop",
        Cisco-Service-Info = "ILOCAL-NET",
        Cisco-Service-Info += "MC",
        Cisco-Service-Info += "TP",
        Acct-Interim-Interval = 1800

И теперь можно, запустив на нашем импровизированном радиус-сервере:
cos56# tcpdump -i eth0 -n -vvvv -s 1204 port 1812 or port 1813
или на циске:
c7200# terminal monitor
c7200# debug radius auth
c7200# debug radius acc
(а еще лучше - для полноты картины - и то, и другое) наблюдать за поднимающимися сессиями. Думаю, тот, кто дочитал до этого места, не нуждается в рассказе о том, как запускать pppd (8).

вторник, 26 апреля 2011 г.

Нелегкая борьба с multipath


Имеется - блейд-сервер IBM HS22, единственным диском для которого является том на хранилище EMC CLARIION. Задача: настроить multipath-доступ к этому тому (по умолчанию, имея два оптических свича и два порта на каждом, мы видим этот том в виде 4 дисков /dev/sd[a-d]).
В CentOS 5.5 вроде имеется паспортное средство установки системы с multipath - надо при инсталяции ввести командную строку ядра linux mpath - но этот путь, мягко выражаясь, не совсем работает - т.е. система прекрасно устанавливается  на устройство /dev/mapper/mpath0, но грузиться с него отказывается - не может найти корневой раздел. 
В debian 6.0 дела чуть лучше - действуем так:
  1.  ставим систему, как обычно (я ставил на /dev/sda, но, вероятно, можно с тем же успехом ставить на любой другой диск).
  2. добавляем пакеты для multipath и пересобираем initrd:
    # apt-get install multipath-tools-boot multipath-tools firmware-qlogic
    # update-initramfs -u
  3. перегружаемся - и ищем имя multipath-устройств:
    $ ls /dev/mapper
    3600601600a02a004b0085add64e011
    3600601600a02a004b0085add64e011-part1
    3600601600a02a004b0085add64e011-part2
    control
    Имена, заканчивающиеся на part[12] и есть наши разделы. Прописываем их в /etc/fstab и в  /boot/grub/grub.cfg (там, где root=UUID=...) - вероятно, для второго действия есть более элегантный способ, но я плохо разбирась в grub2.

    После этого перегружаемся - и все должно работать. Состояние дискового массива можно посмотреть командой (и попроверять работоспособность, по очереди отключая питание на оптических свичах):

    # multipath -ll
     
 В итоге - мораль - если нужен беспроблемный мультипас из коробки, лучше ставить Vmware ESXI - тоже своего рода линукс...

    среда, 9 февраля 2011 г.

    Как не переходить на зимнее время на серверах


    Наконец-то случилось то, на что все разумные люди уже много лет робко надеялись - клоунада с переводом стрелок отменена! Осталось понять, как мы сообщим об этом радостном событии серверам. Итак, задача: 30 октября 2011 навсегда остаться в московском летнем времени. Сервера у меня под CentOS и FreeBSD, потому все последующие упражнения проверялись именно для этих систем.
    До сегодняшнего дня я считал, что летнее московское время соответствует времени GMT+4. Ничего подобного! Простые опыты показали, что все как раз наоборот - GMT-4. Убедиться в этом легко с помощью команды zdump:
    $ zdump Europe/Moscow
    Europe/Moscow  Wed Feb  9 09:48:59 2011 MSK
    $ zdump Etc/GMT-3
    Etc/GMT-3  Wed Feb  9 09:49:12 2011 GMT-3
    $ zdump Etc/GMT+3
    Etc/GMT+3  Wed Feb  9 03:49:15 2011 GMT+3
    (проверяется -3, а не -4 потому, что у нас с вами пока зимнее время).
    Дальше все достаточно просто (одинаково и для Linux и для FreeBSD):
    # ln -sf /usr/share/zoneinfo/Etc/GMT-4 /etc/localtime
    (если директории /usr/share/zoneinfo и /etc находятся на разных устройствах, то безопаснее будет использовать не ln, а cp).
    В CentOS можно еще поправить параметр ZONE в файле /etc/sysconfig/clock, но - судя по коменту в том же файле, это значение на реальную работу системы не влияет.
    Потом на каком-нибудь сервачке, который не жалко, можно заранее поставить время без пяти три 30 октября - и убедиться, что с переходом через 3 часа никакого зимнего времени не наступит.

    пятница, 28 января 2011 г.

    Изменение поведения транзакций в SQLite 3

    Транзакция в SQLite может быть явно начата SQL-командой BEGIN TRANSACTION или неявно начинаться вместе с подключением к базе данных (второе поведение бывает, если подключение произведено не в режиме auto_commit. CLI клиент sqlite3 всегда работает в режиме auto_commit, библиотеки sqlite3 в различных языках программирования могут вести себя по-разному - напр. в питоновском модуле sqlite3 (он же pysqlite2.dbapi2 в python 2.4) auto_commit по умолчанию выключен и может быть включен необязательным аргументом isolation_level метода connect(), установленным в None.
    Тут осмелюсь заметить, что - если многопользовательский доступ имеет для вас значение - я бы предложил так и делать - ибо при запуске транзакций явно SQL-командами  BEGIN TRANSACTION они ведут себя так, как описано ниже, а при использовании же других значений isolation_level, кроме None - т.е. при неявном старте транзакций - их поведение показалось мне весьма своеобразным. Судя по всему, неявная транзакция запускается первой командой изменения данных, а при таком поведении разница между DEFERRED и IMMEDIATE (подробнее см. ниже) делается не наблюдаемой. Остается два вида транзакций - EXCLUSIVE и все остальные.
    Если же мы подключились в режиме auto_commit и команда  BEGIN TRANSACTION явно не запускалась, то каждая SQL-команда изменения схемы данных или самих данных будет запускаться отдельной транзакцией без возможности отката (в чем собственно и состоит auto_commit). Такое поведение проще для понимания, но для большого количества запросов не вполне рационально (напр. у меня 30 млн. команд INSERT в таблицу из 8 числовых полей выполнялись 13 минут без auto_commit (в одной транзакции), а с auto_commit дождаться окончания процесса так и не удалось - методом экспликации можно предположить, что процесс длился бы чуть больше месяца).
    По умолчанию транзакция запускается в режиме DEFERRED (т.е. отложенный). Его отличие от возможных параметров IMMEDIATE (т.е. непостредственный) и EXCLUSIVE (это слово уже кажется переводить нет нужды) обсуждается ниже на примерах.
    Дано: база данных /tmp/test.db с таблицей следующей схемы:
    CREATE TABLE t1 (a integer primary key, b text);
    Создавать несколько таблиц для нашего тестирования бесполезно т.к. в SQLite 3 транзакция все равно блокирует всю базу (как это ни печально).
    Итак, поключаемся к базе двумя CLI клиентами (синий и красный) и начинаем тесты.
    Сперва DEFERRED:
    sqlite> begin transaction;


    sqlite> select * from t1;
    sqlite> insert into t1 (b) values ('red insert on deferred');
    sqlite> select * from t1;
    1|red insert on deferred

    Как видно из этого абзаца, хотя транзакция уже запущена, база не заблокирована ни на чтение, ни на запись.

    sqlite> insert into t1 (b) values ('blue insert on deffered');

    sqlite> select * from t1;
    1|red insert on deferred
    sqlite> insert into t1 (b) values ('red insert on deferred');
    Error: database is locked

    а вот после первого INSERT в синем клиенте, красный уже не может писать в базу (такое поведение и называется отложенным). Читать он может, но результатов работы незаконченной "синей" транзакции он не видит - т.к. read_uncommited по умолчанию выключен.

    sqlite> commit;

    sqlite> select * from t1;
    1|red insert on deferred
    2|blue insert on deffered
    sqlite> insert into t1 (b) values ('red insert on deferred');
    sqlite> select * from t1;
    1|red insert on deferred
    2|blue insert on deffered
    3|red insert on deferred

    после "синего" COMMIT, "красный" увидел результаты накаченной транзакции - и вновь может писать в базу. Добавим еще, что в нашем случае "красный" работал в режиме auto_commit, но при DEFERRED транзакциях ничего бы не изменилось, если б он запускал BEGIN TRANSACTION, потому что при отложенном поведении база блокируется не началом транзакции, а первым оператором изменения данных в ней (т.е. фактически BEGIN TRANSACTION не делает ничего). 
    Теперь посмотрим, как поведут себя те же клиенты при IMMEDIATE транзакциях.
    sqlite> begin immediate transaction;

    sqlite> select * from t1;
    sqlite> insert into t1 (b) values ('red insert on immediate');
    Error: database is locked
    sqlite> begin immediate transaction;
    Error: database is locked

    с началом транзакции база заблокирована на запись - вставить запись нельзя, начать транзакцию тоже нельзя.

    sqlite> insert into t1 (b) values ('blue insert on immediate');

    sqlite> select * from t1;

    "красному" ничего не видно - т.к. read_uncommited никто не включал

    sqlite> commit;

    sqlite> select * from t1;
    1|blue insert on immediate
    sqlite> insert into t1 (b) values ('red insert on immediate');
    sqlite> select * from t1;
    1|blue insert on immediate
    2|red insert on immediate

    после наката транзакции стали видны ее результаты - и снялась блокировка на запись.
    Теперь самый суровый вариант транзакции - EXCLUSIVE:
    sqlite> begin exclusive transaction;

    sqlite> select * from t1;
    Error: database is locked
    sqlite> insert into t1 (b) values ('red insert on exclusive');
    Error: database is locked
    sqlite> begin exclusive transaction;
    Error: database is locked

    "красному" нельзя ничего - база у нас теперь эксклюзивная.

    Осталось написать только про read_uncommited. Включается она по идее должна так:
    PRAGMA read_uncommited = 1;
    Но увидеть ее в деле мне не удалось ни в версии 3.3.6, ни в 3.7.3 (платформа - Linux). Документация безстрастно сообщает, что некоторые прагмы могут не поддерживаться в некоторых версиях - и установка неизвестной прагмы ошибки не вызывает. Вероятно, это тот самый вариант.
    Вообще знакомство с транзакциями в SQLite породило у меня ощущение, что ими можно пользоваться лишь для ускорения операций записи в базу данных (см. выше вопиющий пример с 30 млн. записей). Можно ли рассчитывать на них при реальном наличии конкурентного доступа к БД - лично для меня - большой вопрос.