среда, 30 октября 2013 г.

На работе решил поставить сервер видеоконферений. перерыл кучу инфы в инете и остановился на BigBlueButton. Почитал документацию и выяснил что на основном сайте разработчика есть уже готовы образ виртуальной машины, который содержит в себе полностью сконфигурированный сервер видеоконференций.
Вот здесь скачал этот образ и подсунул своей виртуальной машины под kvm-manager- ом. Назначил машине сетевой мост на основную сетевуху, зашел через терминал на саму машину и назначил IP адрес. В ДНС-е прописал имя новой машине. Затем на самой машине сделал bbb-conf setip=имя-сервера-видеоконференций (которое только что назначил).
По умолчанию на 80 порту этой машины открываются  совершенно "лысые" странички jsp  с примерами как пользоваться API сервера и с его возможностями. Порылся в инете на тему более приличного интерфейса, но нашел лишь плагины для разных фреймворков типа Drupal, Joomla, даже под WordPress нашел. Ну что ж не плохо в общем то, но т.к. у меня все вертится вокруг Django решил поискать плагин к нему. Нашел проект под названием django-bigbluebutton. Скачал, установил, посмотрел - как то очень скупо... одна страничка для создания и запус ка видеоконференции, другая для отслеживания состояния и остановки, хотя конферения сама остановится, когда все участники выйдут из нее. Нет возможности планирования конференций и предварительной настроки (доступ, загрука файлов и т.п.). Такой набор возможностей очень даже нужен. Вот тогда я решил апгредить это Django приложение.
Началось с  того, что пришлось поковырятся в xml который выдает в ответ на запрос сервер BBB. Оттуда наковырялась возможность указывать будет ли делаться запись конференции или нет, будет это публичной конференция или нет.
Дальше я решил добавить разрешение на доступ из внешних (если сервер ставиться во внутри корпоротивной сети) сетей или нет. И самое главное нужно было сделать возможность планировать предстоящие конференции, чтобы потом создатель ее мог просто нажать кнопку пуск а не заполнять кучу полей с паролями и галочками.
Получилось вот такое django-bbb-conf в итоге.


Тут имеется возможность для любого пользователя Django  портала (сайта и т.п. куда прикручивается данный интерфейс) завести свою видеоконференцию с множеством опций.

Далее отслеживать  состояние и менять настройки.
После окончания конференции она не исчезает из списка, пока пользователь не удалит ее специально. Это может быть удобно для проведения тематических встреч или лекций.
Все публичные видеоконференции отображаются также на отдельной страничке. Это сделано для того, чтобы можно было вывесить ее на общедоступный сайт.
Администратор Django портала имеет возможность видеть список всех созданных конференций и их состояние.
Установка проста:
1. скопируйте папку bbb туда где у вас приложения Django;
2. добавьте в urls.py  "url(r'^bbb/', include('bbb.urls'))," в urlpatterns ;
3. настройте settings.py в папке bbb . Самое главное там указать ключ шифрования SALT (его можно найти в настройках BBB сервера) и url самого сервера видеоконференций. Остальное по желанию.
4. сделайте manage.py syncdb - чтобы добавились новые таблицы.

Так что можете пользоваться.

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

Всем удачи!

вторник, 29 октября 2013 г.

Заполняем шаблоны brython скриптом

Тут на днях один товарищ попросил решить такую задачку:
Имеется сайт состоящий из статических страниц. Среди них есть страницы где нужно выводить ленту новостей. И есть страница просто с новостями.
Статические страницы не удобно часто менять вручную. Ну и самое главное как быть с новостями? на каждой странице писать одну и туже ленту?
Это не удобно, то ошибешься где-то , то вообще забудешь что-то поменять, а если дизайн сменится вообще труба.
Как быть? Хотелось бы иметь один файл, где будут лежать данные о новостях, например в текстовом виде и с какими либо разделителями по полям. А еще хотелось бы иметь готовые html шаблоны для вывода на страницах. Тогда можно делать разный дизайн ленты новостей для разных страниц.
Но чем заполнять шаблоны данными из текстового файла?
Берем javascript интерпретатор для python-а  - Brython и пишем свой скрипт.

И получаем вот такой проектик - Brython-template-engine.

Что мы тут получаем?

В корневой директории сайта размещаем шаблоны html. Например для боковой ленты новостей в файле template_news1.html написано следующее:

<li>{{col0}}<head><b>{{col1}}</b></head>
<p>{{col2}}</p>
</li>

Этого достаточно, так как этот кусок будет повторяться столько раз сколько новостей или сколько раз указано выводить этот блок. Здесь указаны теги для html5, но это не важно т.к. можно написать любые теги.

А вот еще один шаблон в файле template_news2.html:

<tr>
    <td height="25"><strong>{{col0}}</strong><br>
    <img src="images/redline.jpg" alt=""></img></td>
  </tr>
  <tr>
    <td height="100" valign="top"><p align="justify">{{col2}}</p>
    <p>&nbsp;</p></td>
</tr>

Ну в общем как вам угодно создавайте шаблоны и сколько угодно.

Заметили что в шаблонах есть некоторые странные теги? например {{col0}} ?
Это как раз то место, куда будут вставляться данные из текстового поля  - это специальные теги разметки шаблонизатора. Я не стал сильно мудрствовать и взял стандартный способ применяемый в Django шаблонах.
Но на самом деле Вы можете придумать свои теги.

Теперь давайте рассмотрим как устроен файл с данными:
Он тоже лежит в корневой директории и называется news.txt
Но Вы можете назвать его как угодно, главное чтобы он имел обычный текстовый формат.
Вот его содержимое:

{{col0}};{{col1}};{{col2}}
1.;Head of first new;Text of first new. And more...
2.;Head of second new;Text of second new. And more...
3.;Head of third new;Text of third new. And more...
4.;Head of fourth new;Text of fourth new. And more...

Как видите в начале файле, в первой строке идет описание тех самых специальных тегов разметки. То есть тут Вы можете придумать какие-то свои теги. хоть например  %vasya% или &petya&. Главное чтобы каждый тег соответствовал своей колонке данных которые идут ниже.
Количество полей (колонок) может быть каким угодно. Там можно например добавить дата или ссылку на картинку.
Первая строка никогда не будет выводиться в шаблонах  - используйте ее для того чтобы задать специальные теги или символы.
Возникает вопрос -  а что является разделителями строк и колонок?
В данный момент я сделал стандартные разделители, то есть для строк '\\n' - перенос строки. Для разделения колонок указал знак ';' . Учтите что в разных системах перенос строки кодируется по разному, поэтому разделитель строк может не подходить.
Для того чтобы использовать другие разделители их нужно указать в коде скрипта. Скрипт лежит в директории /src/brython/src называется просто script.py. Так вот в нем в начале есть функция render:

def render(file,temp,target, count):
    global  file_data, template, target_id , count_view, rowscharsplit, colscharsplit
    file_data = file
    rowscharsplit = '\\n'
    colscharsplit = ';'
    template = temp
    target_id = target
    count_view = count
    open_template()

Вот здесь и можно указать специальные разделители.
rowscharsplit - разделитель строк, colscharsplit- разделитель полей.

Итак, как это все использовать:

1. скачиваете проект к себе корень проекта отсюда или делаете:
hg clone https://yaricp@bitbucket.org/yaricp/brython-template-engine, если есть установленный Mercurial ;
2. в случае архива распаковываете его;
3. подготавливаете шаблоны по цвету и вкусу;
4. заполняете файл с данными ;
5. в страницах в которые нужно вставить результат нужно в заголовке прописать :

<script type="text/javascript" src="src/brython/brython.js"></script>
    <script type="text/python" src="src/brython/src/script.py"></script>

в секцию <body> нужно вставить :

 onload="brython()"

Вот так например:

<body link="#000000" alink="#CC0000" vlink="#000000" leftmargin="0" topmargin="0" marginwidth="0" marginheight="0"  onload="brython()">

а в конце страницы, перед </body> вставить:

<script type="text/python">
        render(file = 'news.txt',
        temp = 'template_news1.html',
        target = "news-content",
        count = 3
        )
</script>

Здесь указывается file - какой файл с данными использовать,temp -  в какой шаблон вставлять, target -  куда выводить результат на этой странице, count - сколько строк выводить в данном месте.

Ну и ясен пень на этой странице должен присутствовать элемент  с id = "news-content". Например <div id= "news-content"> или <table id="news-content">
Не важно какой элемент. И даже не важно имеет ли он внутри себя уже какие-то элементы или нет. Все что в нем имеется останется на месте, но еще добавится то, что указно в шаблонах и вставятся данные.

Вот что получается:



Если есть два места куда нужно вставлять данные, то делайте так:

<script type="text/python">
        render(file = 'news.txt',
        temp = 'template_news2.html',
        target = "newstable",
        count = None)
</script>
<script type="text/python">
        render(file = 'news.txt',
        temp = 'template_news1.html',
        target = "news-content",
        count = 3
        )
</script>

Здесь используются два шаблона и вставляются в разные места на странице.
Эти примеры   лежат в архиве в файлах index.html и news.html

Получится вот так:



Можете вставлять большее количество шаблонов и из разных файлов данных.

Немного анализа:

чем хорош данный метод?
Он позволяет снизить нагрузку на канал. т.к. закачиваются чистые данные без тегов разметки html, а теги подставляются на стороне клиента. Конечно теги разметки закачиваются, но это всего лишь один маленький шаблон. Если данных огромное количество  - это становится актуально. Но постраничной загрузки пока нет и я не знаю как это сделать таким способом.

Чем плох данный метод?
 Очевидно тем, что не все браузеры правильно отрабатывают даже обычный javascript код, а тут еще и brython :). А некоторые (люди) вообще не любят использовать скрипты на страницах и они отключают поддержку javascript в браузере, но возможно, уже не стоит на таких ориентироваться.

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

Короче пользуйтесь! Исправляйте, пишите свои надстройки, в общем будьте счастливы! :)





четверг, 17 октября 2013 г.

Настройка nginx тестового и боевого сервера для php разработчиков

Задача:
Есть несколько проектов написанных на php (хотя это не принципиально, но у нас именно так.), есть несколько разработчиков, работающих с этими проектами совместно и есть nginx. Нужно обеспечить разработчиков тестовыми серверами и сделать "боевой" (продакшн ) сервер. Все это происходит на FreeBSD 9.0

Решение:

Было решено завести на сервере для разработчиков учетные записи с домашними директориями (как обычно во FreeBSD) и у каждого делаем директорию projects. То есть:

/home/user1/projects/

/home/user2/projects/

/home/user3/projects/

Далее делаем общую директорию для главных репозиториев проектов. Ну например в /usr/local/dev_www (development www).
Заводим там проекты:

/usr/local/dev_www/project1/

/usr/local/dev_www/project2/

/usr/local/dev_www/project3/

Создаем в проектах репозитории и разработчики user1, user2,.. сначала клонят проекты к себе в projects/,  а потом пушат, резолвятся как им вздумается, но важно чтобы не правили ничего в /usr/local/dev_www. Лучше им туда вообще доступ закрыть на правку, оставить только push allow в .hg/hgrc. Главное чтобы конфликты у себя в домашних директориях разрешали.

Затем делаем конфиг для тестового сервера nginx:

server {
        listen       80;
        server_name  dev-servername;
        access_log  /var/log/nginx/dev-servername.access.log;
        error_log  /var/log/nginx/dev-servername.error.log;
        location / {
            root   /usr/local/dev_www;
            index  index.html index.htm index.php;
            }
# Девелоперский код php
        location ~ /(user1|user2|user3).*(\.php$|/$) {
            set $user $1;
            allow   IP_user1;
            allow   IP_user2;
            allow   IP_user3;
            #...........
            index   index.php;
            rewrite ^/user1/(.*$) /$user/projects/$1 break;
            rewrite ^/user2/(.*$) /$user/projects/$1 break;
            rewrite ^/user3/(.*$) /$user/projects/$1 break;
            #..........
            deny    all;
            fastcgi_pass    unix:/var/run/php5-fpm.sock;
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME  /usr/home$fastcgi_script_name;
            fastcgi_param  PHP_VALUE  "include_path=/usr/home/$user/projects/include";
            include        fastcgi_params;
            }
# Девелоперская статика
        location ~ /(user1|user2|user3).*\.(htm|html|jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|doc|xls|exe|pdf|ppt|txt|tar|wav|bmp|rtf|js|sw
f)$ {
            set $user $1;
            root    /usr/home;
            allow   IP_user1;
            allow   IP_user2;
            allow   IP_user3;
            #...........
            index  index.html index.htm index.php;
            rewrite ^/user1/(.*$) /$user/projects/$1 break;
            rewrite ^/user2/(.*$) /$user/projects/$1 break;
            rewrite ^/user3/(.*$) /$user/projects/$1 break;
            #...........
            deny        all;
            }
#Раздел для корневых репозиториев     
        location ~ \.php$ {
            fastcgi_pass    unix:/var/run/php5-fpm.sock;
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME  /usr/local/dev_www$fastcgi_script_name;
            fastcgi_param  PHP_VALUE  "include_path=/usr/local/dev_www/include";
            include        fastcgi_params;
            }
#Статика корневых репозиториев
        location ~* ^.+\.(htm|html|jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|doc|xls|exe|pdf|ppt|txt|tar|wav|bmp|rtf|js|swf)$ {
            root /usr/local/dev_www;
            error_page   404  =  @back;
        }
# Файлы заливаемые пользователями.
        location /media {
            root /var/media/dev_www;
            error_page   404  =  @back;
        }
    }



Этот конфиг инклудим в основной nginx.conf и перезапускаем /usr/local/etc/rc.d/nginx restart

Получаем вот что:

Девелопер user1 заходит на http://dev-servername/user1/project1 и видит свой обрабатываемый  проект, с ошибками и косяками. И так любой из девелоперов с любым проектом.

Далее когда все слито в /usr/local/dev_www мы может зайти на http://dev-servername/project1 и наблюдать как живет разрабатываемый проект в целом.

Если разработчики постоянно пулят друг друга и разрешают конфликты оперативно, то может и не нужно отслеживать  http://dev-servername/project1.

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

В чем суть всего этого спросите вы?

А главное, что отлаживается проект на том же сервере, где и будет эксплуатироваться. Здесь используется один nginx на всех. И если вы ставите какой-то модуль для него или для php, то голова не будет болеть поставить то же самое на "боевом сервере".

Готовый код складываем в /usr/local/www/project1, /usr/local/www/project2 и т.д. То есть в стандартное www пространство. По мере готовности (по этапно) делаются hg push из /usr/local/dev_www/project1, в /usr/local/www/project1, . При этом в .hg/hgrc прописываем :

[hooks]
changegroup = hg update

Чтобы код репозитория /project1 автоматически апдейтился до последней версии. Тоже самое можно сделать и проектах в /usr/local/dev_www

Далее делаем простой и понятный конфиг боевого сервера.

server {
        server_name  nameserver;
        listen 80;
        client_max_body_size 100m;
        access_log  /var/log/nginx/nameserver.access.log;
        error_log  /var/log/nginx/nameserver.error.log;
        keepalive_timeout 3;

        location / {
            root   /usr/local/www;
            index  index.html index.htm index.php;
            }
        location ~ \.php$ {
            root           html;
            fastcgi_pass    unix:/var/run/php5-fpm.sock;
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME  /usr/local/www$fastcgi_script_name;
            fastcgi_param  PHP_VALUE  "include_path=/usr/local/www/include";
            include        fastcgi_params;
            }
   location ~* ^.+\.(htm|html|jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|doc|xls|exe|pdf|ppt|txt|tar|wav|bmp|rtf|js|swf)$ {
            root /usr/local/www/nginx;
            }
      location /media {
            root  /var/media/www;
            }
    }

Ну и еще на закуску берем и прикручиваем девелоперские проекты к системе управления проектами Redmine. Создаем там проекты, создаем тех самых пользователей - разработчиков (если аутентификация сделана и на сервер и в redmine через LDAP каталог, то вообще ничего делать не нужно) и указываем для проектов хранилища из /usr/local/dev_www/

Красота  - вся движуха по разработке видна в изменениях проекта, все ветки и коммиты видны в хранилище. Ссылка на проект указывает на http://dev-servername/project1 - где сразу видно его функционирование.

И последнее украшение - перекидывание сообщений из системы логирования например Sentry прямо в Redmine в новые задачи с меткой error.

Правда это последнее украшение пока не удалось корректно реализовать, Что-то Sentry  не то делает, но это отдельная тема, расскажу потом.

В общем как то так!

Это схема запущена и работает, пока все довольны.

Что хотелось бы улучшить?

Хотелось бы иметь отдельные логи для отдельных разработчиков и пересылку сообщений об ошибках каждому разработчику свои ошибки. Ну с боевого сервера конечно все получают и потом решают кто и что будет править (Это реализовано через raven-php и Sentry - сервер).

Хотелось бы в конфигах nginx в регулярки  rewrite ^/user1/(.*$) подствлять переменную $user которая берется из url. Пока не знаю как это сделать и поэтому приходится писать столько строк, сколько разработчиков. Немножко не универсально получается...

Вот так! Критикуйте! хвалите или что там еще...




среда, 16 октября 2013 г.

Некоторые особенности использования hg для /etc/ и /usr/local/etc

Как то на работе  решили попробовать отслеживать изменения настроек серверов. А то админов аж три штуки и серверов куча и каждый админ для своих целей (сервисов) переписывает настройки и других служб. А потом никто не помнит что и зачем делал.
Поэтому взяли и сделали hg init в каталогах типа /etc, /usr/local/etc/ , подключили все это хозяйство к hgserver-у и стали через веб интерфейс смотреть что, когда и зачем менялось. Было принято правило в комментах коммита писать причину сего изменения и имя админа, т.к. зачастую настройки делаются всеми в руте. В общем-то удобно. но оказалось есть свои подводные камни.
Вот они:
 Есть файлы, которые не нужно отслеживать, т.к. они меняются самой системой, например passwd или все, что связано с генерацией ключей и сертификатов. Да мало чего она генерит или исправляет и почему-то разработчики решили, что файлы эти должны лежать именно в /etc/
Нужно их добавить в .hgignore.

Добавление файлов и каталогов в .hgignore в каталогах /etc и /usr/local/etc !

Вроде бы все просто — берешь и добавляешь этот файл или папку в файл .hgignore… Но! Дело в том, что если этот файл или папка уже отслеживалась системой контроля версий (mercurial), то теперь для игнорирования ею этого файла нужно «отписать» этот файл или папку. Для этого в hg  есть команда remove. И вот тут есть подводный камень — т.к. система hg изначально придумана для слежение файлов проектов разрабатываемого ПО, то там не было необходимости переставать следить за файлом и при этом оставлять сам файл в каталоге. Иногда это даже может мешать. И поэтому hg remove УДАЛЯЕТ файл и перестает за ним следить.
В каталогах /etc, /usr/local/etc и т.п. это НЕДОПУСТИМО, поэтому решение такое:
1. Если файл уже отслеживается hg и был изменен (проверить можно командой hg stat, если перед файлом стоит M — то отслеживается), то нужно добавить его в .hgignore, затем копируем в временное место (любое) этот файл или копируем с переименованием файла, после этого делаем hg remove filename (файл при этом удаляется), а затем возвращаем файл обратно (переименовываем обратно).
2. Если файл не отслеживается (hg stat показывает ? перед файлом), то просто добавьте его в .hgignore  и все!
Проверить удачность можно опять же командой hg stat. При удачной операции файл не будет отображаться, это означает что он игнорируется.
Можете коммитить это состояние.

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

Чтобы избежать всей мучительной и возможно неоднократной операции лучше всего перед тем как сделать  hg init тчательно проанализировать все что находится в /etc/ и составить .hgignore заранее. Тогда в коммитах не появится ни одной версии игнорируемых файлов.

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

Python на Frontend-е

Мне как питонисту немного тяжко писать сложные Javascript-ы для улучшения функционала пользовательских страниц веб-проектов.
Поэтому я искал какой-то способ упростить написания фронтенда на Javascript.
Сначала мне попался Nodejs  и его coffeescript который по синтаксису похож на питон, но все же Nodejs это серверная платформа (бекенд) и не хочется распыляться поддержку еще одной платформы для одного веб-проекта (обновление модулей и т.д.).
Потом я встретил проект PyJamas, который является транслятором кода Python в код  Javascript. Здорово! почти то, что нужно! но нужно встраивать автоматизацию генерации кода на javascript и следить за ссылками на готовые файлы. Но похоже скорость ответа страницы будет зависеть от того как быстро переведется весь код питона для фронтенда в код Javascript.

Но самое простое решение это использовать проект Brython . Фактически это транслятор кода питон в javascript написанный на самом javascript-е и выполняется на стороне клиента. Отрицательной стороной является на мой взгляд  - нагрузка клиента (браузера), т.к. появляется дополнительные процессы для трансляции кода.
Вот пример как можно просто встроить питон в веб страничку:

на сайта проекта предлагают встраивать так:

<head> 
<script src="/brython.js"></script>
</head>
<body onload="brython()">
<script type="text/python">
def echo():
    alert(doc["zone"].value)
</script>
В своем Django проекте я использовал локальное подключение:

<script type="text/javascript" src="{{ STATIC_URL }}brython/brython.js"></script>
    <script type="text/python" src="{{ STATIC_URL }}brython/src/test.py"></script>
    <script type="text/javascript">
        function start_py(){
                brython();
                process();
        }
    </script>

Здесь  brython/src/test.py - файл с исходниками на питоне для фронтенда,
start_py() - функция привязанные к какому либо событию на страничке.
Во всех функциях, где будет использоваться питон обязательно сначала нужно использовать функцию brython();
Далее можно вызывать функции питона или прямо писать на питоне, только код нужно обернуть  в  <script type="text/python">.

Вот так!
Мне кажется это весьма удобно.