Python 2.5 & python-mysqldb : TypeError: not all arguments converted during string formatting

Баг в python-mysqldb Debian 5.0.6

Фаил : /usr/site/lib/python2.5/site-packages/MySQLdb/cursors.py

Пакет :

Package: python-mysqldb
Priority: optional
Section: python
Installed-Size: 360
Maintainer: Debian Python Modules Team Architecture: amd64
Version: 1.2.2-7

Пример :

sql = '''INSERT INTO call_fail_log (`callerid`,`date_stamp`,`cause`) VALUES(%s,FROM_UNIXTIME(%s),%s)'''
        cursor = self.db_init().cursor()
        cursor.executemany(sql,[(callerid,datestamp,cause)])
        self.db_init().close()

Третий плейсхолдер не работоспособен, причина – неправильная работа регулярного вырождения при парсинге.

Решение :

Патч.

--- cursors.py~ 2007-02-11 07:48:19.000000000 -0800
 +++ cursors.py 2008-10-09 14:31:27.000000000 -0700
 @@ -6,7 +6,14 @@
 """

 import re
 -insert_values =
 re.compile(r"
\svalues\s*(\(((?<!\\)'.*?\).*(?<!\\)?'|.)+?\))",
 re.IGNORECASE)
 +
 +restr = (r"
\svalues\s*"
 + r"
(\(((?<!\\)'[^\)]*?\)[^\)]*(?<!\\)?'"
 + r"
|[^\(\)]|"
 + r"
(?:\([^\)]*\))"
 + r"
)+\))")
 +
 +insert_values= re.compile(restr)
 from _mysql_exceptions import Warning, Error, InterfaceError, DataError,
 \
DatabaseError, OperationalError, IntegrityError, InternalError, \
NotSupportedError, ProgrammingError

Накладываем патч на /usr/site/lib/python2.5/site-packages/MySQLdb/cursors.py , радуемся.

Asterisk логирование не обслужанных звонков

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

Замутим логирование кодов закрытия канала связи..

Что есть : Asterisk 1.4 , Python 2.5 , MySQL 5.1

Создадим табличку для MySQL в которую и будем складывать высеры Астериска.

Будем сохранять:
callerid – собственно номер звонившего
date_stamp – timestamp обрыва канала (HANGUP,CANCEL, etc..)
cause – код причины
status и comment – зело не обязательно, статус записи (типа обслужено или нет) и комментарий

CREATE TABLE `asterisk`.`call_fail_log` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`callerid` VARCHAR(11) DEFAULT NULL,
`date_stamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`cause` VARCHAR(15) DEFAULT NULL,
`status` tinyint(1) DEFAULT '0',
`comment` text,
PRIMARY KEY (`id`),
KEY `call_index` (`callerid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8

Теперь сварганим махонький python agi скрипт который непосредственно сохранять данный в базу..

#!/usr/bin/env python

'''
Created on 05.08.2011

@author: valor
'''

import MySQLdb
import sys

class CallFailLog:

    DBHOST = "192.168.158.160"
    DBUSER = "agi"
    DBPASS = "asterisk"
    DBNAME = "asterisk"

    def __init__(self):
        pass

    def db_init(self):
        connect = MySQLdb.connect(db=self.DBNAME, host=self.DBHOST, user=self.DBUSER, passwd=self.DBPASS)
        return connect

    def main(self,callerid, datestamp, cause):
        sql = '''INSERT INTO call_fail_log (`callerid`,`date_stamp`,`cause`) VALUES(%s,FROM_UNIXTIME(%s),%s)'''
        cursor = self.db_init().cursor()
        cursor.executemany(sql,[(callerid,datestamp,cause)])
        self.db_init().close()

if __name__ == '__main__':
    app = CallFailLog()
    app.main(sys.argv[1], sys.argv[2], sys.argv[3])

Как видим все просто и легко..
Теперь лезем в extensions.conf и сделаем макрос обработки кодов + посмотрим откель его можно вызвать..

Собственно макрос:

[macro-logfailcall]
exten => s,1,GotoIF($["${ARG1}" = "16"]?goin:end)
exten => s,n(goin),DeadAGI(callfaillog.py,${CALLERID(num)},${EPOCH},${ARG1})
exten => s,n(end),NoOp(&gt;&gt;&gt; Unknown cause ${ARG1})
exten => s,n,Hangup()

Код завершения 16 NORMAL_CLEARING  – есть не что иное как положенная абонентом трубка.

Вызвать его можно в любом контексте, например так..

...
exten => h,1,Macro(logfailcall,${HANGUPCAUSE})
...

a: Вызывается, когда пользователь нажимает ‘*’ во время проигрывания приветствия системы голосовой почты.
h: Вызывается, по завершению вызова. 
i: Вызывается, при попытке вызова неизвестного екстеншена.
o: Расширение оператора, используется для обработки нажатия нуля в системе голосовой почты.
s: Стартовое расширение в контексте.
t: Вызывается, при наступлении состояния таймаута.
T: Вызывается, при наступлении состояния абсолютного таймаута, заданного функцией AbsolutTimeout?.
e: Перехват расширений i,t и T для обработки ошибки в едином месте. Для выяснения типа ошибки может быть использована функция EXCEPTION(?)
failed: Используется, если auto-dial out вызов завершился неудачно (который имеет определённый контекст, приоритет и расширение).
fax: Используется для определения факса на Zap каналах.
talk: Используется в конъюнкции с BackgroundDetect?.

Полезные ссылки : Коды причин закрытия канала

Удачи..

VsFTPD and Virtual users on Debian

На заметку..

Пакеты:

apt-get install libpam-pwdfile vsftpd

Предположим, что сайтики лежат в /srv/www/{sitename – вида www.mysite.ru}/htdocs , соответственно ограничиваем пользователя доступом в корень, т.е. не выше директории www.mysite.ru.

И так чистим дефолтный конфиг vsftpd

echo "" > /etc/vsftpad.conf

И записываем туды следующие..

listen=YES
anonymous_enable=NO
local_enable=YES
write_enable=YES
local_umask=022
# пользователя vsftpd нужно создать системным без шела, например useradd -M -r vsftpd
nopriv_user=vsftpd
virtual_use_local_privs=YES
guest_enable=YES
user_sub_token=$USER
# Токен $USER предполагает что к пути /srv/www/ будет добавлено имя пользователя, он и будет являться корнем для него.
local_root=/srv/www/$USER
chroot_local_user=YES
hide_ids=YES
guest_username=vsftpd
chown_uploads=YES
# От имени и группы www-data пашет Apache.
chown_username=www-data
guest_enable=YES
guest_username=www-data
chown_upload_mode=0775

use_localtime=YES
xferlog_enable=YES
xferlog_file=/var/log/vsftpd.log
xferlog_std_format=YES

dirmessage_enable=YES
ascii_upload_enable=YES
ascii_download_enable=YES
ftpd_banner=Welcome developer to WWW server!
ls_recurse_enable=YES
secure_chroot_dir=/var/run/vsftpd/empty

pasv_enable=YES
pasv_min_port=62000
pasv_max_port=64000

Далее надо настроит pam аутентификацию, для сего лезим в /etc/pam.d/vsftpd и комментим все попутно добавив туда две строчки

auth required pam_pwdfile.so pwdfile /etc/ftpd.passwd
account required pam_permit.so

Собственно создаем файлик /etc/ftpd.passwd командой

htpasswd -c /etc/ftpd.passwd

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

htpasswd /etc/ftpd.passwd www.mysite.ru

Ребутим vsftpd и радуемся..

Asterisk – запись исходящих звонков.

Задача: Записывать исходящие звонки от некоторого подмножества абонентов корпоративной телефонной сети.

Что есть в наличии: Asterisk 1.4* – вот с ним то и будем плясать..

Решение:  Список жертв будет храниться в бд, в моем случае это MySQL, для этого сварганим не хитрую табличку:

CREATE TABLE `asterisk`.`callout` (
`id` INT(3) NOT NULL AUTO_INCREMENT,
`callerid` INT(4) NOT NULL,
`status` TINYINT(1) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`)
)
ENGINE = MyISAM;

Поясню: Поле  callerid – будет хронить 4х значный номер внутреннего абонента. status – просто флаг который позволит нам например временно исключать номер из списка записывающихся..

Далее давайте напишим AGI скрипт, который будет пробивать переданный ему в качестве аргумента callerid и если таковой там имеется, то вновь созданный канал будет записан.

Пример скрипта:

#!/usr/bin/env python
'''
Created on 26.05.2011

@author: valor
'''

import MySQLdb
from asterisk.agi import AGI
import sys

class calloutrec:

DBHOST = "localhost"
DBUSER = "agi"
DBPASS = "asterisk"
DBNAME = "asterisk"

DEBUG = 3

agi = AGI()

def db_init(self):
connect = MySQLdb.connect(db=self.DBNAME, host=self.DBHOST, user=self.DBUSER, passwd=self.DBPASS)
return connect

def main(self,arg):

    self.agi.verbose(">>> start executing AGI script ", self.DEBUG)

    cursor = self.db_init().cursor()
    cursor.execute('''SELECT c.callerid FROM callout c WHERE c.status = 1 AND c.callerid =%s''',arg)
    res = cursor.fetchone()
    if res != None:
        self.agi.set_variable("ISREC", 1)
    else:
        self.agi.set_variable("ISREC", 0)

if __name__ == '__main__':
app = calloutrec()
app.main(sys.argv[1])

В основе скрипта лежит мини фреймворк pyst , один из немногих еще не здохших проектов – рекомендую, жизнь облегчит.

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

[macro-calloutrec]
exten => s,1,AGI(callaoutrec.py,${ARG1})
exten => s,n,NoOp(${ISREC})
exten => s,n,GotoIF($["${ISREC}" = "1"]?rec:norec)
exten => s,n(rec),MixMonitor(/raid/outmonitor/${EPOCH}-${CALLERID(num)}.wav)
exten => s,n(norec),NoOp(${CALLERID(num)} >>> No record call)

Вот такой вот нехитрый макрос принимает в качестве аргумента callerid абонента осуществляющего исходящий звонок, далее передает его как аргумент agi скрипту и на основе возвращенного результата решает писать его или нет.

Осталось применить макрос в контексте через который идут исходящие звонки. примерно так:

exten =&gt; _XXXXXX.,n,Macro(calloutrec,${CALLERID(num)})

Вот и все.. записанные файлики в формате wav будут складываться в директорию  /raid/outmonitor/ с именем фала вида utc-callerid.wav .

ЗЫ. Для сортировки полученных фалов можно использовать скрипт из предыдущей статьи.

Asterisk – укладываем фалики по полочкам.

Для тех кто использует Asterisk для построения колцентров..

Если вы ведете запись входящих звонков в очередях (asterisk: Queues), то рано или поздно встает вопрос об вменяемой организации нескольких десятков (может быть и сотен) тысяч файлов в нечто удобное для поиска и прочих манипуляций..

Я в свою очередь предлагаю хранить все файлы как дерево вида: YYYY/MM/DD/ , для сего написал небольшой скриптик на питоне..

#!/usr/bin/env python
'''
Created on 03.01.2011

@author: valor
'''

import time
import os
from shutil import move

class AstMonFLSorter:

    store_root = "/raid/monitor/"

    def __init__(self):
        pass

    def utc_get_date(self,utc):
        return time.strftime("%Y:%m:%d", time.localtime(utc))

    def main(self):
        count = 0
        for fname in os.listdir(self.store_root):
            utc = fname[:10]
            year = self.utc_get_date(float(utc))[:4]
            month = self.utc_get_date(float(utc))[5:7]
            day = self.utc_get_date(float(utc))[8:10]
            curday = self.utc_get_date(time.time())[8:10]

            if not os.path.isdir(self.store_root+year):
                print ">>> create "+year+" folder"
                os.mkdir(self.store_root+year)
            if not os.path.isdir(self.store_root+year+"/"+month):
                print ">> create "+month+" folder"
                os.mkdir(self.store_root+year+"/"+month)
            if not os.path.isdir(self.store_root+year+"/"+month+"/"+day):
                print "> create "+day+" folder"
                os.mkdir(self.store_root+year+"/"+month+"/"+day)
            else:
                if os.path.isfile(self.store_root+fname) and day != curday:
                    move(self.store_root+fname, self.store_root+year+"/"+month+"/"+day)
                    count +=1
                    print "=="+str(count)+"== moved file "+fname+" to > "+self.store_root+year+"/"+month+"/"+day


if __name__ == '__main__':
    astmon = AstMonFLSorter()
    astmon.main()

Вот как-то так.. Есть замечания или предложения, или может есть ошибки? Пишите все поправим..

Grub2 и framebuffer в Debian

Ахтунг!!! Не надо ничего менять в /boot/grub/grub.conf , это не наш метод.

Разрешение ЖК моего ноутбука 1280×800, не забываем его сменить на свое!

Для начала открываем  /etc/default/grub и ищем строку:

GRUB_GFXMODE=800x600

Правим ее немного, что бы получилось:

GRUB_GFXMODE=1280x800@32
# ниже добавляем строку
GRUB_GFXPAYLOAD=1280x800x32

Теперь лезем в /etc/grub.d/00_header ищем там строку:

if [ "x${GRUB_GFXMODE}" = "x" ] ; then GRUB_GFXMODE=800x600x32 ; fi
#меняем разрешение на свое и ниже добавляем еще одну
if [ "x${GRUB_GFXPAYLOAD}" = "x" ] ; then GRUB_GFXPAYLOAD=1280X800x32 ; fi

В самом конце файла ищем строку

set gfxmode=${GRUB_GFXMODE}

и ниже добавляем

set gfxpayload=${GRUB_GFXPAYLOAD}

Сохраняем и запускаем update-grub2.

Удачи.



Hiberante multi instance – работа с несколькими БД.

Мемориз так сказать,  не так часто нужно на практике, но все же..

JBoss AS 6.0.0

HibernateUtils.java

public class HiberanteUtils {

private static final Logger _log = Logger.getLogger(HiberanteUtils.class);

private static final SessionFactory nagiosSessionFactory, asteriskSessionFactory;

static {

try{

nagiosSessionFactory = new Configuration().configure("hibernate-nagios.cfg.xml").buildSessionFactory();

asteriskSessionFactory = new Configuration().configure("hibernate-asterisk.cfg.xml").buildSessionFactory();

}catch (Throwable ex) {

_log.error("Initial SessionFactory creation failed.", ex);

throw new ExceptionInInitializerError(ex);
}
}

public static SessionFactory getNagiosSessionFactory() {
return nagiosSessionFactory;
}

public static SessionFactory getAsteriskSessionFactory() {
return asteriskSessionFactory;
}

Соответственно hibernate-nagios.cfg.xmlhibernate-asterisk.cfg.xml должны лежать в class path..

Notes@ Jetty 7-8 Eclipse WTP Adapter

Дабы не забыть в будущем оставлю линк.

Debian NumLock trouble

Угораздило меня нарваться на иксовый баг чёрт его знает какой давности.. причем баг сей не очевиден и вроде даже не совсем баг (да.. вот такой вот каламбур).. Короче, суть в том, что если нечаянно нажать Shift+NumLock , то цифровая консоль переходит в режим управления курсором, а так как при каждом нажатии клавиш 8,6,2,4 курсор сдвигается на один пиксел, заметить сей трабл не просто, по симу первая мысль приходящая в голову это БАГ..

Для поличения сей пичали нужно жмакнуть  Shift+NumLock еще раз..

ЗЫ.. Как мне подсказали интернеты.. данная «фича», есть не что иное как не плохой  способ подосрать ближнему ибо неплохо функционирует при не активных иксах (т.е. в разлогиненном состоянии).

Gnome-terminal как фон рабочего стола..

Захотелось странного..  Хочу что-бы консоль была фоном рабочего стола.  Не отвлекаясь на преамбулы, возьмемся за решение поставленной задачи.

На просторах интернетов набрел на занятный тузлец Alltray (AllTray – Dock any program into the system tray.) , полистав man – понял, что для решения нашей задачи оно может сгодиться.

И так решение.

В качестве эмулятора терминала берем стандартный для Gnome – Gnome-terminal, дистрибутив Debian..

Для начала малость подрехтуем сам gnome-terminal:

  1. Создаем новый профиль.
  2. Отключаем отображения верхнего меню.
  3. Отключаем полосы прокрутки.
  4. Увеличиваем буфер строк (для удобства).
  5. Выкручиваем до упора прозрачность фона терминала.
  6. Подгоняем цвет консольных шрифтов под цвет фона рабочего стола.
  7. Остальное по вкусу..

Устанавливаем alltray

viktor@viktor-laptop:~$ apt-get install alltray

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

alltray --show --sticky --skip-taskbar --no-alltray --notray -x -g +800+0 --geometry 1024x600 gnome-terminal --window-with-profile=s

Лично я добавлял ее в Система->Параметры->Запускаемые приложения.

Да и пару слов об опциях alltray..

  • show – не сворачивать при запуске
  • sticky – отображать на всех рабочих столах
  • skip-taskbar – убрать приложение из панели задач
  • no-alltray – убрать заголовок alltray
  • notray – не показывать значёк в трее
  • x – убрать декорацию окон
  • +800+0 – позиция окна на рабочем столе (первое – горизонталь, второе – вертикаль)
  • geometry 1024×600 – разрешение рабочего стола (это если нужно растянуть терминал на весь рабочий стол, иначе просто указываем нужную геометрию окна терминал)
  • window-with-profile=s – это уже параметр gnome-terminal говорящий о том, что при запуске нужно заюзать профиль «s», который мы создали выше.

Ну вот и все.. Перезагружаем иксы и ликуем..

Все эти выше описанные манипуляции выполнялись на Asus EeePC 1005 с Debian Sqeeze и Gnome 2.28.1..

Пользователей KDE, Xfce, etc..  очень прошу оставить свои комментарии по поводу различий и нюансов в реализации данной фичи.

Скрины на память…

Терминал как фон рабочего стола

Терминал как фон рабочего стола