Записки программиста, обо всем и ни о чем. Но, наверное, больше профессионального.

2012-03-30

Масштабируемость

Интересно бывает наблюдать за изменением отношения к предмету с ходом времени, вернее, с накоплением опыта.
Вот, к примеру, день назад мне казался очень интересным вопрос — почему с увеличением количества обработчиков (процессов) общая производительность падает, хотя должна бы возрастать. Нет, не абстрактно а вполне конкретно — на задаче генерации тайлов кеша для некоей карты в ArcGIS Server. А сегодня (после проведения серии бесчеловечных опытов :) мне этот вопрос уже не кажется столь интересным, ибо ответ на него почти очевиден.
Или другой вопрос — почему генератор кеша (ManageMapServerCacheTiles) отваливается по таймауту (A request to obtain a free ServerContext has failed because the Wait Timeout Interval has elapsed) и что с этим делать?

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

Поставлена задача — посчитать тайлы для кешированного картографического сервиса, на основе ArcGIS Server. Документация рассказывает, что чем больше процессов (в их терминологии — потоков) натравить на расчет, тем быстрее оно посчитается. И в это можно поверить, ибо теоретически задача прекрасно параллелится, вплоть до того, что можно один тайл считать отдельным потоком.
Есть железка с 8-ю ядрами и достаточным количеством памяти. В теории, можно зарядить до 16-ти потоков.

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

Так вот, оптимальное количество потоков волшебным образом совпало с количеством используемых в карте источников данных. В качестве каковых используются FileGeodatabasegdb. Их пять. Возможно, это просто совпадение, чтобы утверждать что-либо надо сперва провести массу практических экспериментов. Я же для себя сделал вывод — для каждого конкретного картпроекта надо опытным путем подбирать оптимальное количество потоков. Это не сложно, берем таймер и прогоняем тестовый счет минут на 5-15 для разных вариантов конфига.

Я выше говорил про то, что вопрос потерял свою интересность из-за очевидности ответа. Очевидность заключается в том, что, как известно, формат источника данных — gdb не предусматривает совместной/параллельной работы. Хотя в теории, при построении тайлового кеша данные используются только на чтение, что не должно вызывать блокировок. Так что, возможно, это неправильная очевидность. Пусть меня поправит тот, кто знает лучше.

А теперь про таймаут, из-за которого отваливалась процедура счета.
При достаточно большом количестве потоков (я проверял 12 и 16), инструмент выдавал такое сообщение о ошибке
Executing: ManageMapServerCacheTiles wrngis1 Sborka4cache Layers 'СЛОИ для КЭШа'
591657527,591555;295828763,795777;147914381,897889;73957190,948944;36978595,474472
"Recreate Empty Tiles" "-22954246,8225262 -3753860,36441453 22461579,686571 24540920,4368881"
12 NONE # IGNORE_COMPLETION_STATUS_FIELD # #
Start Time: Wed Mar 28 18:33:00 2012
Finding missing tiles
ERROR 000564: Completed with error. One or more server contexts failed.
Failed to execute (ManageMapServerCacheTiles
при этом в журнале ArcGIS Server наблюдались такие записи
<Msg time='2012-03-28T20:10:59' type='WARNING' code='2008' target='Sborka4cache.MapServer' thread='1700'
elapsed='60,00000'>A request to obtain a free ServerContext has failed because the Wait Timeout Interval has elapsed.</Msg>
И, что любопытно, в конфиге сервиса указаны таймауты
 <WaitTimeout>60</WaitTimeout>
 <IdleTimeout>1800</IdleTimeout>
 <UsageTimeout>3000000</UsageTimeout>
Параметр
<WaitTimeout>60</WaitTimeout>
задается равным 60 секунд по умолчанию, при создании сервиса. И этого времени, очевидно, не хватает.

После того, как я увеличил этот параметр до 600 секунд (<WaitTimeout>600</WaitTimeout>), процедура перестала отваливаться с сообщением об ошибке и считала все корректно. Но, как выяснилось, медленно. На 12-ти потоках я в журнале ArcGIS Server видел такие числа
<Msg time='2012-03-28T21:11:29' type='INFO3' code='4006' target='Sborka4cache.MapServer' machine='wrngis1'
thread='1540' elapsed='122,12500'>Server Context created.</Msg>
то есть, время ожидание более 2-х минут. Что я исправил, уменьшив количество потоков до 5-ти.

В общем, если следить за этим показателем в логах, то можно поймать момент, когда конфиг перестает быть оптимальным. Ну и в целом, полезно иногда проанализировать журнал на предмет поиска чрезмерных elapsed-ов.

Фейловое обновление

Иногда мне попадаются на глаза высказывания типа «тестовые сборки вполне стабильны, я ими пользуюсь х лет без проблем». В таких случаях мой внутренний скептик обычно ухмыляется и говорит - «смотря что считать проблемами». А тут как раз показательный случай произошел.

Железка у меня относительно новая, на ей Debian Squeeze с ядром из бэкпортов (иначе графика не работала).

И вот, на днях, накатил я обновления на рабочую станцию и после ребута она перестала быть рабочей (каламбурчик :) При загрузке ядро (kernel) падает в панику и все умирает. Только кнопка reset может оживить. Определить по тексту на экране причину проблемы не представляется возможным. Другого ядра у меня нет (на то свои, дурацкие причины), recovery mode (он же single режим) тоже не работает. Что делать, ааааа!!!!

Вот на этом месте я и оценил в полной мере стабильность тестовых сборок, вырывая с досады остатки волос на жопе. Потом успокоился и придумал, как мне порешать эту проблему.

А выбрался я из ситуевины довольно просто. Раздобыл внешний привод CD и диск дистрибутивный со Squeeze. Загрузился с него и пошел по пути — advanced installrescue. Прошел по предлагаемым шагам до пункта выбора дискового раздела на роль рутового. Тут случилась заминка, пришлось вспоминать раскладку компа по дискам. Потом согласился с предложением запустить шелл, после чего попал в любезную консоль.
Дальше все просто. Подмонтировать остальные разделы (mount -a), запустить приличный шелл (bash, su -l), установить предыдущую версию ядра. И перезапустить машинку.

В итоге, работаю сейчас на ядре
linux-image-3.2.0-0.bpo.1-amd64
а ядро
linux-image-3.2.0-0.bpo.2-amd64
пока подождет.

Мораль? Да сколько угодно. Не пользуйтесь нестабильными/тестовыми сборками без особой на то нужды. Держите под рукой (в загрузчике) альтернативное стабильное ядро. Имейте наготове план и причиндалы восстановления. Мойте руки перед едой. Продолжать?

P.S.
Конечно, джедаи сами компиляют свои ядра, я в курсе. Я и сам их себе компилял, пока не надоело. Нынче меня вполне устраивают стоковые.

2012-03-29

VCS

Вот так почитаешь новости и призадумаешься — а какого, собственно, маракуЯ, мы до сих пор пользуемся Subversion, в то время как вся прогрессивная общественность активно юзает Git? Может пора уже распробовать новые вкусняшки?

Уже и инструкции подробные есть, как свой сервер сделать, чтобы репозитории Git-овские хостить:

If you're comfortable with Unix and ssh, and you have a relatively sane setup, the following steps should work:
  • create a user called git. Login to this user.
  • copy your ssh pubkey from your workstation. Rename it to YourName.pub.
  • now run these commands:
    git clone git://github.com/sitaramc/gitolite
    gitolite/src/gl-system-install
    gl-setup -q ~/YourName.pub
You're done. If it didn't work, well that's what the install doc is for, especially the if you run into trouble section.
WARNING: do NOT add repos or users directly on the server! You MUST manage the server by cloning the special 'gitolite-admin' repo on your workstation (git clone git@server:gitolite-admin), making changes, and pushing them. Here's how to add users and repos.



Или даже по-русски:

gitolite: свой git репозитарий
... gitolite поддерживает разграничение прав на уровне веток, тегов, файлов, директорий, т.е. можно указать кому можно пушить (push) правки в ветку, а кому нет. Еще одним аргументом в сторону gitolite были недавние ротации на kernel.org, после которых было принято решение перейти на gitolite.
Другие возможности gitolite описаны тут.
...
Для начала нам нужно создать системного пользователя, который будет администратором всех репозиториев...


И ребенок справится.

2012-03-28

SciTE + ExtMan

Я уже упоминал, что среди всех текстовых редакторов фаворитом у меня значится SciTE. И вот сегодня я открыл для себя знатное подспорье в автоматизации этого прекрасного редактора. Подспорье называется Scite Ext Man.

Это не очень длинный (460 строк) скрипт на Lua, который подключается в SciTE как стартовый, после чего становится очень легко цеплять свои функции за события редактора и создавать пункты меню с клавиатурными шорткатами.

Как это сделать. Сначала скачать пакет. Потом зацепить его за SciTE.

Делай раз — Options — Open User Options File.
ext.lua.directory=$(SciteUserHome)/lua/scite_lua
ext.lua.startup.script=$(SciteUserHome)/lua/extman.lua

В моем случае, скрипт автозагрузки я положил по путю
C:\Documents and Settings\v\lua\extman.lua
а все остальные луа-файлы я поместил в папку
C:\Documents and Settings\v\lua\scite_lua
Из этой папки будут автоматически загружаться все файлы с расширением lua.

Делай два — записал файл
c:\Documents and Settings\v\lua\scite_lua\v_cmd.lua
с содержимым
function replaceWinSlashes2Unix()
  local sel = editor:GetSelText()
  editor:ReplaceSel(string.gsub(sel, "\\", "/"))
end

function removeSpaces()
  local sel = editor:GetSelText()
  editor:ReplaceSel(string.gsub(sel, "%s", ""))
end

function ToggleMonospace()
  scite.MenuCommand(IDM_MONOFONT)
  return false
end

scite_Command 'Sel Replace Slashes|replaceWinSlashes2Unix|Shift+Ctrl+M'
scite_Command 'Sel Remove Spases|removeSpaces|Shift+Ctrl+Alt+M'

scite_OnOpen(ToggleMonospace)
что дало мне, после перезапуска редактора, две команды в меню Tools (одна переворачивает слеши, другая убирает пробелы, обе для выделенного текста) и одну интересную фишку.
Ради этой фишки я и занимался сегодня изысканиями, приведшими меня к SciteExtMan.
При открытии файла по умолчанию он теперь отображается моноширинным шрифтом.

Удивительно, но факт — в настройках самогО редактора сделать это невозможно, не потеряв возможности переключать моно/проп по Ctrl-F11. А по умолчанию, что тоже удивительно для программерского редактора, выставляется всегда отображение пропорциональным шрифтом.

Как видно из кода, цеплять обработчики событий и команды меню — проще пареной свеклы. И только потому, что в автозагрузку прописан SciteExtMan.

2012-03-27

Корреляция

Нам сообщают:

Компания Cast Software... представила результаты исследования качества программного обеспечения, проведённого на основе анализа 745 бизнес-приложений на различных языках программирования (Java EE, Cobol, .Net, C, C++), состоящих в сумме из 365 млн строк кода...
... наибольшее число проблем свойственно проектам на платформе Java-EE, а наименьшее - на языке Cobol. Расчетная стоимость исправления проблем для Java составила $5.42 на строку кода, а для Cobol - $1.26. При оценке безопасности кода, наихудшие показатели наблюдаются у платформы .NET, лучшие у программ на языке Cobol. По мнению исследователей наименьшее число проблем в программах на языке Cobol объясняется спецификой использования данного языка и богатым 30-летним опытом разработки. Проблемы в коде на языке Java связываются с тем, что много людей начинают создавать программы на данном языке не имея надлежащей теоретической подготовки по компьютерным наукам.


Выводы почему-то не удивляют. Было бы странно ожидать большей безопасности кода от программ на .NET, при такой интеграции рантайма с системой. Было бы странно ожидать большого количества ошибок от тех двух с половиной профи, что еще пишут на Коболе, тем более, что они уже и не пишут, а поправляют ранее написанное. И было бы странно ожидать хорошего кода от «индусов» выдающих вал-по-плану на Ява, языке, вакансии по которому до сих пор выглядят наиболее привлекательно.
Вот что странно, так это то, что .NET показал худшие показатели по безопасности, чем С/С++.


Там же есть сцыль на инфографику, сделанную по результатам опроса 500 разрабов — какие инструменты они предпочитают. Вообще, полезно поглядеть, для ориентации.

2012-03-24

ZMI, Вкладка Properties

На предыдущем шаге я сделал вкладку 'View' для Zope Product. Теперь рассмотрим, что надо сделать, чтобы в ZMI появилась вкладка 'Properties' для объектов типа VCUFile.

Вообще говоря, изменений чуть, ибо вся логика наследуется из OFS.PropertyManager/PropertyManager:
from OFS.PropertyManager import PropertyManager

from AccessControl import ClassSecurityInfo
from Globals import InitializeClass

from vcufile import IVCUFile
from zope.interface import implements

from Products.PageTemplates.PageTemplateFile import PageTemplateFile # ZPT files
from Globals import DTMLFile # dtml files

addForm = DTMLFile('www/vcuFileAdd', globals())

def addFunction(self, id, filename, REQUEST=None):
    """addForm processor function. Constructor.
    Create a new VCUFile and add it to container.
    """
    p = VCUFileProduct(id, filename)
    self.Destination()._setObject(id, p)
    if REQUEST is not None:
        return self.manage_main(self, REQUEST, update_menu=0)
    return id
#def addFunction(dispatcher, id, filename):

class VCUFileProduct(Implicit, Persistent, RoleManager, Item, PropertyManager):
    """VCUFile product class, implements IVCUFile
    """
    implements(IVCUFile)

    #Item requerements
    meta_type = 'VCUFile'
    id = ''
    title = ''

    # ZMI views
    manage_options = (
        {'label':'Edit',   'action': 'editForm'}, # # editForm invoke editFunction
        {'label':'View',   'action': ''}, # If your class has a default view method (index_html) you should also include a View view whose action is an empty string.
    ) + PropertyManager.manage_options + RoleManager.manage_options + Item.manage_options # properties, security, undo, ownership
В модуль с классом продукта внесены изменения (выделены жирным) — добавлена одна строка (импорт) и изменены две (список наследования класса и добавка к 'manage_options').

Эти изменения реализуют вкладку 'Options' в ZMI для объектов VCUFile. Что и требовалось.

На этом можно и закончить обзор вопроса «как же мне создать Zope Product». Вот так и создать:

Про установку Zope:

А мне теперь надо наполнять эту заготовку логикой моего мегааплоада.

2012-03-23

ZMI, Вкладка просмотра (View)

Продолжаем вытачивать полуфабрикат продукта для Zope. На предыдущем шаге сделана форма (вкладка ZMI) редактирования 'Edit'. Теперь сделаем вкладку просмотра 'View'.

Вкладка 'View' делается аналогично вкладке редактирования и даже проще, потому, что нет нужды делать парную функцию — обработчик формы. Форма просмотра будет только «на чтение».

Для разнообразия сделаю ее на основе шаблона ZPT.
from Products.PageTemplates.PageTemplateFile import PageTemplateFile # ZPT files
from Globals import DTMLFile # dtml files

addForm = DTMLFile('www/vcuFileAdd', globals())

def addFunction(self, id, filename, REQUEST=None):
    """addForm processor function. Constructor.
    Create a new VCUFile and add it to container.
    """
    p = VCUFileProduct(id, filename)
    self.Destination()._setObject(id, p)
    if REQUEST is not None:
        return self.manage_main(self, REQUEST, update_menu=0)
    return id
#def addFunction(dispatcher, id, filename):

class VCUFileProduct(Implicit, Persistent, RoleManager, Item):
    """VCUFile product class, implements IVCUFile
    """
    implements(IVCUFile)

    #Item requerements
    meta_type = 'VCUFile'
    id = ''
    title = ''

    # ZMI views
    manage_options = (
        {'label':'Edit',   'action': 'editForm'}, # # editForm invoke editFunction
        {'label':'View',   'action': ''}, # If your class has a default view method (index_html) you should also include a View view whose action is an empty string
    ) + RoleManager.manage_options + Item.manage_options # security, undo, ownership

    security = ClassSecurityInfo()

    security.declareProtected('View', 'index_html')
    index_html = PageTemplateFile('www/vcuFileView', globals()) # ZPT file

    security.declareProtected('View management screens', 'editForm')
    editForm = DTMLFile('www/vcuFileEdit', globals()) # DTML file
В модуль с классом продукта добавлены четыре строки (выделены жирным). Один импорт, один элемент кортежа 'manage_options' и метод 'index_html' с декларацией защиты.

Сама страничка просмотра сделана добавлением файла 'vcufile\www\vcuFileView.zpt' такого содержимого
<html>
  <head>
    <title tal:content="here/title_or_id">The title</title>
    <meta http-equiv="content-type" content="text/html;charset=utf-8">
  </head>
  <body>
    <h2><span tal:replace="here/title_or_id">content title or id</span>
        <span tal:condition="template/title"
              tal:replace="template/title">optional template title</span>
 </h2>
    This is VCU File <em tal:content="here/id">file id</em>.
  </body>
</html>
- самого примитивного (как просто делать заготовки :)

Осталось совсем чуть-чуть. Вкладку 'Properties' — и тема «заготовки Продукта Zope» будет закрыта.

2012-03-22

ZMI, Вкладка редактирования

Продолжаем вытачивать полуфабрикат продукта для Zope. На предыдущем шаге я закончил реализацией формы и функции создания нового объекта. Продолжим реализацией формы редактирования (вкладка 'Edit' в ZMI).

A product’s management views are defined in the manage_options class attribute.

Чтобы сделать вкладку редактирования, я внес в класс продукта следующие строки (выделены жирным):
class VCUFileProduct(Implicit, Persistent, RoleManager, Item):
    """VCUFile product class, implements IVCUFile
    """
    implements(IVCUFile)

    #Item requerements
    meta_type = 'VCUFile'
    id = ''
    title = ''

    # ZMI views
    manage_options = (
        {'label':'Edit',   'action': 'editForm'}, # editForm invoke editFunction
    ) + RoleManager.manage_options + Item.manage_options # security, undo, ownership

    security = ClassSecurityInfo()

    security.declareProtected('View management screens', 'editForm')
    editForm = DTMLFile('www/vcuFileEdit', globals())

    security.declareProtected('Change Images and Files', 'editFunction')
    def editFunction(self, filename, REQUEST=None):
        """Changes attributes."""
        self.title = filename
        if REQUEST is not None:
            return self.editForm(REQUEST, management_view='Edit',
                manage_tabs_message='VCU File attribs changed.')

    def __init__(self, id, filename):
        self.fid = ''
        self.id = id
        self.title = filename

    security.declarePublic('getFID')
    def getFID(self):
        """File ID"""
        return self.fid
Добавлены два метода (editForm, editFunction) и один атрибут (manage_options). Пока код ничего полезного не делает, это только заготовка, которая, тем не менее, выполняет все требования со стороны Zope. Остается только наполнить форму и обработчик реальными данными.

Сама форма редактирования выглядит так:


и нарисована она в новом файле 'vcufile\www\vcuFileEdit.dtml'.

Посмотрим на его содержимое
<dtml-var manage_page_header>
<dtml-var manage_tabs>
<p class="form-help">
    You can update the data for this file object using the form below.
</p>
<form action="editFunction">
<table cellspacing="0" cellpadding="2" border="0">
  <tr>
    <td align="left" valign="top">
        <div class="form-optional">Filename</div>
    </td>
    <td align="left" valign="top">
        <input type="text" name="filename" size="40"
            value="&dtml-title;"/>
    </td>
  </tr>
  <tr>
    <td align="left" valign="top"> </td>
    <td align="left" valign="top">
        <div class="form-element">
            <input class="form-element" type="submit" name="submit" value=" Save Changes " />
        </div>
    </td>
  </tr>
</table>
<p class="form-help">
    Lorem ipsum dolor sit amet, ...
</p>
</form>
<dtml-var manage_page_footer>
Пока это очень похоже на форму добавления. Из отличий особо можно отметить строку '<dtml-var manage_tabs>' очевидного назначения. А если не очевидно, попробуйте ее убрать и посмотреть, что получится.

На этом пока все. В следующий раз будем делать вкладку 'View' и, может быть, 'Properties'.

2012-03-21

Практикум dm-crypt (LUKS, cryptsetup), дополнение


Небольшое дополнение к заметке про dm-crypt (LUKS, cryptsetup).

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

Кстати, про резервную копию хидера. Имея на руках копию хидера, можно вынуть из него ключ методом брутфорса. Поэтому очень важно выбирать правильные ключи и защищать резервные копии хидера.

Чтобы избавиться от наличия хидера, можно применить метод 'plain' для cryptsetup. Тогда, фактически, LUKS (и его полезняшки) использоваться не будет.

Подробнее читайте тут.

Сословная Россия

Наши сети притащили

В 2002 году появился закон «О системе государственной службы РФ», — рассказывает он. — Потом закон «О государственной гражданской службе». По закону — и вопреки Конституции — создавались категории людей с выделенным статусом. У меня что-то копошилось в башке: я не понимал, зачем это. ...
А потом у меня в голове сошлось: вот эти законы о системе госслужбы — это создание новой социальной структуры
...
Сословия — это группы, создаваемые государством для решения своих задач. Вот есть внешняя угроза — значит, должны быть люди, которые ее нейтрализуют, военные. Есть внутренняя угроза — значит, внутренние войска и милиция. Есть космическая угроза — должны быть космические войска. Есть природная угроза — есть служба Роспотребнадзора. Сословия — это не профессии, там могут быть люди разных профессий. Сословия есть в любой социальной системе. Это доклассовая штука. Классы возникают на рынке естественным путем, а сословия создаются государством.
Если у власти классовая структура, появляется механизм согласования интересов между классами. Называется это демократия. Появляется парламент как ее оформление. У демократии очень прикладная функция: согласование интересов богатых и бедных. А в сословной системе механизм согласования интересов — собор. Съезды КПСС — это были соборы: представители всех сословий собирались раз в четыре-пять лет и согласовывали свои интересы.
...
Если есть рынок, возникают классы. Отношения между классами нужно регулировать. Появляются законы, регулирующие эти отношения. Появляется судебная система. А в сословной системе это все — лишнее. Там нет рынка, а есть система распределения. Наверху находится какой-то человек, называется он президентом, генсеком или монархом — неважно. Он верховный арбитр. Ведь все люди, которым распределяют ресурсы, считают себя обиженными. В нашей стране есть два типа жалоб: много взяли и мало дали. И все жалобы обращены наверх, к верховному арбитру. Пишут ему и ждут, что он там решит.
...
В 90-е возникло социальное расслоение. Учителя, врачи, военные — это были советские сословия, лишенные потока советских ресурсов. И они попали в самый низ иерархии распределения. Начали формироваться классы богатых и бедных. Сословные различия между бедными исчезали. Начались движения протеста — забастовки, голодовки. Нужно было наводить порядок. А порядок в чем заключается? В том, чтобы накормить, обеспечить обделенных положенными им ресурсами. Для этого нужно было рынок ужать — ресурсы изъять с рынка, чтобы их можно было потом распределять в пользу сирых и убогих. Мы последнее десятилетие жили в этом процессе
...


Какая любопытная теория. Значит, в России заправляет делами сословный механизм. Если учесть, что более половины бюджета нынче составляет «труба», и доля растет, кстати, то эта теория очень хорошо ложится на наблюдаемые процессы. Особенно мне понравилось про «верховного арбитра». В этом моменте я согласен с автором на 100%.


Кстати, про демократию, как «механизм согласования интересов между классами». Хочу посчитать, на память.
Население страны — 143 миллиона человек. Из них трудоспособны по возрасту 88 миллионов + пенсионеры 32 миллиона = 120 миллионов с правом голоса (на самом деле меньше, но пусть). Из них на выборы президента пришли 50%, если откинуть карусели и прочие накрутки = 60 миллионов. Из них, допустим, 51% проголосовал за Путина = 30 миллионов. Выходит, среди населения в 143 М.человек, Путина нынче поддерживают 30 М.человек, почти каждый пятый (а на самом деле и того меньше, ибо числа мои сильно округлены в большую сторону). Или я опять наврал в арифметике?

Вот и вся демократия — навязывание воли агрессивного меньшинства пассивному большинству.

2012-03-20

Практикум dm-crypt (LUKS, cryptsetup)

LUKS is the standard for Linux hard disk encryption. ... While LUKS is a standard on-disk format, there is also a reference implementation. LUKS for dm-crypt is implemented in an enhanced version of cryptsetup.

Недавно я отчитался (в виде наблюдений за производительностью) по использованию eCryptfs. Теперь запишу отчет о dm-crypt.

eCryptfs годится, когда закрыть от посторонних глаз надо отдельное дерево каталогов или папочку-другую. Если же оперировать дисковыми разделами, то удобнее, надежнее и производительнее использовать блочную прослойку. В смысле, криптовать не на уровне файлов а на уровне блоков ввода/вывода. Отдельно надо заметить возможность аппаратного ускорения операций алгоритмов AES современными камнями от Intel (AES-NI). dm-crypt умеет аппаратно ускоряться, eCryptfs — нет (во всяком случае, я не встретил инструкции по зацеплению eCryptfs за крипто-API ядра). Могу сразу сказать, я разницу между ускоренным AES и традиционным заметил. Это экономия около 10% загрузки CPU и прибавка в 10 — 15 мегабайт/сек. трансфера с диска на диск. Хотя, надо сделать скидку на разницу в подходах — файловый IO и блочный IO. А с другой стороны, где-то я видел число более 500 мегабайт/сек. на ускоренном AES. Более чем достаточно, ящетаю.

Итак, практикум.

Сначала надо сделать копию данных с раздела, назначенного под криптование
rsync -av /extst1/virt /extht1/virt
Пока пишется, читаем теорию и готовимся.

Смотрим, есть ли в ядре поддержка AES
cat /boot/config-3.2.0-0.bpo.2-amd64 | grep AES
Как дела с модулями
lsmod |grep aes
   aesni_intel            50498  216
   cryptd                 14463  1 aesni_intel
   aes_x86_64             16796  1 aesni_intel
   aes_generic            37122  2 aesni_intel,aes_x86_64
Видно, что AES-ni есть и даже загружен, это какбэ намекает на то, что акселерация будет.

Ставим Cryptsetup
aptitude install cryptsetup

Посмотрев в /etc/fstab видно, что нужный мне раздел монтируется по uuid
UUID=216858de-b781-4efa-9666-4373804a17cf /extst1
А вывод команды mount говорит о двойном подключении, сперва
/dev/sdb1 on /extst1 type ext4 (rw)
а потом
/extst1/virt on /extst1/virt type ecryptfs...
А тут и копирование закончено. Если не закончено — ждем.

Раз два монтирования, надо два раза размонтировать
umount /extst1/virt
umount /dev/sdb1

Создаем криптованный раздел
cryptsetup --verbose --verify-passphrase luksFormat /dev/sdb1
если параметры по умолчанию (алгоритмы, длина ключа и пр.) не устраивают, читаем man cryptsetup и задаем параметры ручками. Умолчальные же параметры можно глянуть в выводе команды
cryptsetup --help

Теперь криптованный раздел можно подключить к мапперу
cryptsetup luksOpen /dev/sdb1 extst1crypt
или, если уж основываться на uuid, сперва выясним его
blkid
   /dev/sdb1: UUID="96eed01e-6783-4e64-917a-2aa1f0a76f1c" TYPE="crypto_LUKS"
и подключаем так
cryptsetup luksOpen /dev/disk/by-uuid/96eed01e-6783-4e64-917a-2aa1f0a76f1c extst1crypt

Проверим статус
cryptsetup status extst1crypt
   /dev/mapper/extst1crypt is active:
     cipher:  aes-cbc-essiv:sha256
     keysize: 256 bits
     device:  /dev/sdb1
     offset:  2056 sectors
     size:    976760824 sectors
     mode:    read/write

Создадим файловую систему
mke2fs -t ext4 /dev/mapper/extst1crypt
И смонтируем в дерево файлсистем
mount /dev/mapper/extst1crypt /extst1

Осталось вернуть взад файло из резервной копии
# chown -R v:users /extst1
# chmod -R 766 /extst1
$ rsync -av /extht1/virt/ /extst1/virt/
   sent 206013701910 bytes  received 945 bytes  115123611.54 bytes/sec
   total size is 205988553697  speedup is 1.00

И на тот случай, если понадобится вручную отключить раздел, заклинать так
umount /dev/mapper/extst1crypt
cryptsetup luksClose /dev/mapper/extst1crypt

Собственно, все. Тут нарисовано простейшее использование dm-crypt в обертке cryptsetup. Использование ключей в виде файлов, скрытых разделов и прочие продвинутые вещи — остались за кадром. Кому надо, тот сам нагуглит, инфы полно.

По материалам

2012-03-17

О жадности


Для тех, кто не врубается в буржуйскую речь, попробую кратенько очертить, о чем идет речь:

Они (копирасты) говорят: потери в $58 миллиардов в год на воровстве контента.
Давайте посчитаем.
Это эквивалентно потери рынка зерновых (кукурузы?), всех фруктов, пшеницы, хлопка, табака, риса и сорго (приведены по убыванию доли в экономике).
В то время как зафиксированные доходы музыкальной индустрии с 2000 года падали на $1 миллиард в год. Что дает реальных убытков с 2000 года в $12 миллиардов.
В то время как общие доходы TV + Satellite + Cable и прочая мелочь, выросли с 2000 года на $40 миллиардов.
Доходы выросли. Но они говорят, рост мог бы быть больше (на $58 лярдов в год?). Но на рынке нет нормативов.
Они говорят, что Америка теряет 373 000 рабочих мест на воровстве контента.
В то время как в 1998 г. в индустрии кино и видео было занято всего 275 000 рабочих мест.
Они говорят, что один украденный трек оценивается в $150 тысяч и это без учета инфляции.
Что означает, в современный iPod помещается до $8 миллиардов или 75 тысяч рабочих мест.
Смешно? В зале смеются.

Короче - копирастическая математика - бред жадной сволочи.

2012-03-16

Вкладки ZMI

Продолжим изготовление Zope2 Product (aka Add-on) под кодовым названием VCUFile. Начало.

Как минимум, нам нужно две приличных (в отличие от того, что в заготовке) вкладки:
* форма добавления;
* форма редактирования.
Начнем с переделки формы добавления, ибо редактировать нечего, пока ничего не добавлено.
Чтобы сделать стандартную форму добавления объекта, мне надо заменить функцию
def addForm(self):
    ...
на эдакий код
from Globals import DTMLFile
addForm = DTMLFile('www/vcuFileAdd', globals())
И сваять упомянутый DTML файл. Причем файл «vcuFileAdd» на самом деле называться будет «www\vcuFileAdd.dtml». А содержание его будет таким, для начала:
<dtml-var manage_page_header>
<dtml-var "manage_form_title(
 this(), _,
 form_title='Add VCU File',
 help_product='Products/vcufile',
 help_topic='VCUHelp'
)">
<p class="form-help">
 This form allows you to upload a really big file to the server repository.
</p>
<form action="addFunction">
<table cellspacing="0" cellpadding="2" border="0">
  <tr>
    <td align="left" valign="top">
  <div class="form-label">Id</div>
    </td>
    <td align="left" valign="top">
  <input type="text" name="id" size="40" />
    </td>
  </tr>
  <tr>
    <td align="left" valign="top">
  <div class="form-optional">Filename</div>
    </td>
    <td align="left" valign="top">
  <input type="text" name="filename" size="40" />
    </td>
  </tr>
  <tr>
    <td align="left" valign="top">
    </td>
    <td align="left" valign="top">
  <div class="form-element">
   <input class="form-element" type="submit" name="submit" value=" Add " />
  </div>
    </td>
  </tr>
</table>
<p class="form-help">
 Lorem ipsum dolor ...
</p>
</form>
<dtml-var manage_page_footer>
Что дает желаемое — стандартный вид формы


Поскольку с некоторых пор решено похерить систему хелпа Zope, все части кода, относящиеся к системе справки тупо не работают. Поэтому я забил место под будущую справку лорем-ипсумом.

Пока так. Заготовку формы редактирования (о, чуть не забыл, еще нужна вкладка просмотра — view) нарисую в следующий раз.
Stay tuned.

kwords: web http chunked resumable upload, Zope, python, silverlight, javascript 

2012-03-15

Ошибки при использовании ExtFile в Zope 2.13.13

При попытке использовать продукт ExtFile/ExtImage 2.0.2 в свежей сборке Zope 2 (2.13), лезут ошибки. Связано это, с одной стороны, с прекращением поддержки ExtFile в 2008 году, а с другой стороны, с деятельностью авторов Zope2, которые занимаются неслабым рефакторингом кода. Я продукт подлечил и заставил ExtFile работать как положено. Прилагаю таблетку.

Ошибка
ImportError: No module named ZRDB.TM
устранилась добавлением к Zope пакета Products.ZSQLMethods
через билдаут
eggs =
  Zope2
  Products.ZSQLMethods
Борьба с билдаутом расписана тут.

Ошибка
ImportError: No module named WriteLockInterface
  File "c:\d\Zope\custom\zope\inst\Products\ExtFile\ExtImage.py", line 61, in <module>
    from webdav.WriteLockInterface import WriteLockInterface
zope.configuration.xmlconfig.ZopeXMLConfigurationError: File "c:\d\Zope\custom\zope\inst\etc\site.zcml", line 16.2-16.23
    ZopeXMLConfigurationError: File "c:\d\Zope\custom\zope\inst\Products\ExtFile\configure.zcml", line 23.2-27.6
    ImportError: No module named WriteLockInterface
устраняется (http://community.webfaction.com/questions/1401/extfileextimage-problems-in-zope) заменой пары строк в двух файлах
было
       from webdav.WriteLockInterface import WriteLockInterface
стало
       from webdav.interfaces import IWriteLock as WriteLockInterface


Ошибка
ImportError: Couldn't import zope.app.container.interfaces, No module named container.interfaces
 File "c:\d\zope\custom\zope\inst\eggs\zope.configuration-3.7.4-py2.7.egg\zope\configuration\fields.py", line 229, in fromUnicode
    raise InvalidToken("%s in %s" % (v, u))
zope.configuration.xmlconfig.ZopeXMLConfigurationError: File "c:\d\Zope\custom\zope\inst\etc\site.zcml", line 16.2-16.23
    ZopeXMLConfigurationError: File "c:\d\Zope\custom\zope\inst\Products\ExtFile\configure.zcml", line 5.2-9.6
    ConfigurationError: ('Invalid value for', 'for', "ImportError: Couldn't import zope.app.container.interfaces, No module named container.interfaces in .interfaces.IExtFile          zope.app.container.interfaces.IObjectMovedEvent")
  Module zope.interface.adapter, line 585, in subscribers
  Module Products.ExtFile.ExtFile, line 1065, in afterAdd
ImportError: No module named container.interfaces
устраняется (http://bluebream.zope.org/doc/1.0/new/whatsnew-1.0.html) заменой шести строк в трех файлах
C:\d\Zope\custom\zope\inst\Products\ExtFile\configure.zcml
zope.app.container.interfaces zope.container.interfaces
и в C:\d\Zope\custom\zope\inst\Products\ExtFile\Ext(File|Image).py
from zope.app.container.interfaces... from zope.container.interfaces...


Если сложить все вместе, то выходит такой diff:
--- configure.zcml Mon Oct 29 20:03:45 2007
+++ configure.zcml Mon Oct 29 20:03:45 2007
@@ -3,11 +3,11 @@
     i18n_domain="extfile">
 
   <subscriber
     handler=".ExtFile.afterAdd"
     for=".interfaces.IExtFile
-         zope.app.container.interfaces.IObjectMovedEvent"
+         zope.container.interfaces.IObjectMovedEvent"
     />
 
   <subscriber
     handler=".ExtFile.afterClone"
     for=".interfaces.IExtFile
@@ -21,11 +21,11 @@
     />
 
   <subscriber
     handler=".ExtImage.afterAdd"
     for=".interfaces.IExtImage
-         zope.app.container.interfaces.IObjectMovedEvent"
+         zope.container.interfaces.IObjectMovedEvent"
     />
 
   <subscriber
     handler=".ExtImage.afterClone"
     for=".interfaces.IExtImage
--- ExtFile.py Mon Oct 29 20:10:23 2007
+++ ExtFile.py Mon Oct 29 20:10:23 2007
@@ -65,11 +65,11 @@
 from Products.ExtFile import TM
 from zExceptions import Redirect
 from ZPublisher.Iterators import IStreamIterator
 from DocumentTemplate.html_quote import html_quote
 
-from webdav.WriteLockInterface import WriteLockInterface
+from webdav.interfaces import IWriteLock as WriteLockInterface
 from zope import interface
 from interfaces import IExtFile
 from zope import event
 from interfaces import ExtFileUpdatedEvent
 
@@ -1059,12 +1059,12 @@
         external files doesn't exist yet, otherwise it was renamed to .undo
         by beforeDelete before and must be restored by _undo().
 
         Subscriber for (IExtFile, IObjectMovedEvent)
     """
-    from zope.app.container.interfaces import IObjectAddedEvent
-    from zope.app.container.interfaces import IObjectRemovedEvent
+    from zope.container.interfaces import IObjectAddedEvent
+    from zope.container.interfaces import IObjectRemovedEvent
 
     # If this is a Removed event we are done
     if IObjectRemovedEvent.providedBy(event):
         return
 
--- ExtImage.py Mon Oct 29 20:10:28 2007
+++ ExtImage.py Mon Oct 29 20:10:28 2007
@@ -56,11 +56,11 @@
 from OFS.Image import getImageInfo
 from OFS.Image import Pdata
 from Acquisition import aq_base
 from DocumentTemplate.html_quote import html_quote
 
-from webdav.WriteLockInterface import WriteLockInterface
+from webdav.interfaces import IWriteLock as WriteLockInterface
 from zope import interface
 from interfaces import IExtImage
 from zope import event
 from interfaces import ExtImageUpdatedEvent
 
@@ -673,12 +673,12 @@
         external files doesn't exist yet, otherwise it was renamed to .undo
         by beforeDelete before and must be restored by _undo().
 
         Subscriber for (IExtImage, IObjectMovedEvent)
     """
-    from zope.app.container.interfaces import IObjectAddedEvent
-    from zope.app.container.interfaces import IObjectRemovedEvent
+    from zope.container.interfaces import IObjectAddedEvent
+    from zope.container.interfaces import IObjectRemovedEvent
 
     # If this is a Removed event we are done
     if IObjectRemovedEvent.providedBy(event):
         return
 

Все вышенаписанное справедливо для Zope Version (2.13.13, python 2.7.2, win32); ExtFile/ExtImage 2.0.2.

Установить Zope еще правильнее, через кастомный билдаут

Продолжение. Начало тут.
При установке продукта ExtFile я получил проблему

zope2 extfile product ImportError: No module named ZRDB.TM
гугление показало, что искомый модуль был вынесен в отдельный пакет, не устанавливаемый по умолчанию.


Factored out the Products.ZSQLMethods into its own distribution. The distribution also includes the Shared.DC.ZRDB code

Чтобы установить необходимое, надо сделать кастомный билдаут. Этим и займемся.

По инструкции (http://docs.zope.org/zope2/releases/2.13/INSTALL-buildout.html#creating-a-buildout-based-zope-instance), мы сделаем билдаут с нужным нам набором пакетов и включающий в себя инстанс (в предыдущем случае инстанс создавался спецкомандой после билдаутной установки).
Такой способ наверно удобен, если уже есть Data.fs и конфиг, надо только софт накатить или обновить. Хотя, если честно, я не очень понимаю, почему просто не поправить buildout.cfg в предыдущем варианте. Ну да ладно.

Понеслась.
C:\d\Zope>call setenv.cmd
mkdir custom\zope\inst
pushd custom\zope\inst
C:\d\Zope\custom\zope\inst>mkdir etc
mkdir log
mkdir var
python -murllib http://svn.zope.org/*checkout*/zc.buildout/trunk/bootstrap/bootstrap.py>bootstrap.py

загрузчик есть, папки etc, var, log есть. Самое время в них поместить имеющееся содержимое, если в наличии. И создать конфиг билдаута:
vi buildout.cfg
[buildout]
parts = instance
extends = http://download.zope.org/Zope2/index/2.13.13/versions.cfg

[instance]
recipe = zc.recipe.egg
eggs =
  Zope2
  Products.ZSQLMethods
interpreter = zopepy
scripts = runzope zopectl
initialization =
  import sys
  sys.argv[1:1] = ['-C', r'${buildout:directory}\etc\zope.conf']

билдаутим
python bootstrap.py
bin\buildout

Если конфиг еще не сделан, теперь необходимо либо сделать новый конфиг из заготовки
copy c:\d\Zope\custom\zope\inst\eggs\zope2-2.13.13-py2.7.egg\Zope2\utilities\skel\etc\zope.conf.in etc\zope.conf
и отредактировать его, либо поправить уже имеющийся
vi etc\zope.conf
# меняем строки
%define INSTANCE c:\d\Zope\custom\zope\inst
debug-mode on
default-zpublisher-encoding utf-8

и можно запускать:
start bin\runzope.exe

Что еще?
У меня не захотел работать bin\zopectl.exe
который понадобился для создания учетки админа (это надо сделать, если базы Data.fs не было). При попытке создать учетку, ругался словами
>zopectl adduser admin 1234
ImportError: No module named Zope2

Пришлось сделать обертку bin\zopectl.bat
@set PYTHON=c:\d\Zope\custom\zope\inst\bin\zopepy
@set INSTANCE_HOME=c:\d\Zope\custom\zope\inst
@set CONFIG_FILE=%INSTANCE_HOME%\etc\zope.conf
@set ZDCTL=c:\d\Zope\custom\zope\inst\bin\zopectl.exe
"%ZDCTL%" %1 %2 %3 %4 %5 %6 %7
и запускать уже так
C:\d\Zope\custom\zope\inst>bin\zopectl.bat
zopectl> help
zopectl> adduser admin 1234
Created user: admin
zopectl> fg
zopectl> quit

По аналогии, можно сделать и запускатель bin\runzope.bat
@REM ~ C:\d\Zope\custom\zope\inst\bin\runzope.bat
@set INSTANCE_HOME=c:\d\Zope\custom\zope\inst
@set CONFIG_FILE=%INSTANCE_HOME%\etc\zope.conf
@set ZOPE_RUN=c:\d\Zope\custom\zope\inst\bin\runzope
"%ZOPE_RUN%" %1 %2 %3 %4 %5 %6 %7
В предыдущем варианте билдаута такие обертки создаются автоматически. А в этом нет. Лень разбираться — почему.

Вывод: билдаут это мощно но запутанно. Так или иначе, свою проблему (ImportError: No module named ZRDB.TM) с добавлением продукта ExtFile я решил. Правда, полезли другие ошибки, но это уже отдельная история.

На досуге можно еще попробовать еще один рецепт билдаута

2012-03-14

Установить Zope

Пока возюкался с предыдущими версиями мегааплоада, прозевал момент выхода свежих версий Zope. Буду наверстывать.

Zope versions prior to Zope 2.12 are no longer maintained and will not see any further bugfixes or updates.
Нынче все так поменялось (на сайте), видимо у них взяли курс на мировое господство. Все кладовые перетряхнули, навели порядок, многое переделали. В частности, теперь я не смог найти простого инсталлятора под MS Windows. Будем ставить «из пакетов».

Чтобы поставить свежего Zope, пришлось сперва наладить среду (под Linux все проще, но на работе я сижу под MS Windows).

Открыл консоль, сделал папочку
mkdir C:\d\code\web.upload\vcu\zope
pushd C:\d\code\web.upload\vcu\zope

И понеслась
* установить Python http://www.python.org/download/ 2.7.2
* установить cURL http://curl.haxx.se/download.html (http://curl.haxx.se/gknw.net/win32/curl-7.24.0-devel-mingw32.zip) бинарники слить в папку, доступную по %PATH%

@REM ~ # -*- mode: bat; coding: cp1251 -*-
@REM ~ # (c) Valik mailto:vasnake@gmail.com

@echo off
chcp 1251 > nul
set wd=%~dp0
pushd "%wd%"
set NLS_LANG=AMERICAN_CIS.UTF8
set PYTHONPATH=
set path=c:\d\Python27;c:\d\Python27\Scripts;%path%
set proxy=http://proxy.algis.com:3128
set http_proxy=%proxy%
set ftp_proxy=%proxy%
set all_proxy=%proxy%

@rem все, что выше, пишем в отдельный setenv.cmd и вызываем через call setenv.cmd в дальнейшем

curl -k https://raw.github.com/pypa/pip/master/contrib/get-pip.py | python

* установить virtualenv (http://pypi.python.org/pypi/virtualenv)
pip install virtualenv
* создать вирт.среду
C:\d\code\web.upload\vcu\zope>virtualenv --no-site-packages zope213

и только потом установить Zope
cd zope213
scripts\easy_install -i http://download.zope.org/Zope2/index/2.13.13 Zope2
Scripts\mkzopeinstance.exe
start inst\bin\runzope.bat

После того как все установлено, запускать Zope можно так:
C:\d\code\web.upload\vcu\zope>call setenv.cmd
cd zope213
start inst\bin\runzope.bat
INST – это я так задал instance home когда спрашивали.

Чтож, проверено, работает. Теперь надо

установить Zope правильно, через buildout


* Прибить предыдущий экскремент папку zope213 со всем содержимым
* утянуть дистр
C:\d\code\web.upload\vcu\zope>wget http://pypi.python.org/packages/source/Z/Zope2/Zope2-2.13.13.zip
и распаковать по месту.
* Установить как положено
cd Zope2-2.13.13
python bootstrap.py
bin\buildout
Установлено.

Теперь создать инстанс.
bin\mkzopeinstance
и можно запускать
start inst\bin\runzope.bat

Впоследствии можно запускать так
pushd C:\d\code\web.upload\vcu\zope
call setenv.cmd
pushd Zope2-2.13.13
start cmd.exe /c "inst\bin\runzope.bat"

Зашел браузером в ZMI (http://localhost:8080/manage), поглядел всякое. Интересно, продуктов нет (в контролпанели ZMI). Добавил свой продукт, он тоже в панели управления (http://localhost:8080/Control_Panel/Products/manage_main) не появился. Хотя в списке добавления есть. При этом, стандартные продукты теперь оформлены как яйца (Zope2-2.13.13\eggs\).
Вопрос для прояснения — зачем нынче нужна папка Control_Panel/Products? Для совместимости?
А вот и ответ (Zope2-2.13.13\inst\etc\zope.conf)
# Directive: enable-product-installation
#
# Description:
#     If this directive is turned on, Zope performs 'product installation'
#     (the registration of Python modules in various Products directories)
#     at startup. Only turn this on if your code relies on the Products
#     section in the Control_Panel to be populated.
#
# Default: off
#
# Example:
#
#    enable-product-installation on
Механизм регистрации теперь отключен по умолчанию. Но можно включить. Правда, не стало понятнее, чем он мешал в старом виде.
Changed the default for enable-product-installation to off. This matches the default behavior of buildout installs via plone.recipe.zope2instance. Disabling the persistent product installation also disabled the ZMI help system.
Заодно и справку отрубает.

2012-03-13

Zope Product (Add-on)

Как нам реорганизовать РабКрин построить Продукт для Zope.

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

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

Как иначе? А так. Поскольку платформой я выбрал Zope (ну нравится оно мне), логично сделать класс объектов, наподобие стандартного типа «File». Внешне новый Файл будет отличаться от стандартного только тем, что загружаться на сервер он будет кусочками а не сразу. Ну и храниться будет не в ZODB а в файловой системе. Тема близкая к (http://old.zope.org/Members/shh/ExtFile/). А чтобы сделать такой класс, надо реализовать Продукт (Product) или, иначе, Аддон (Add-on).

Поскольку это первый в моей практике Продукт, попробую задокументировать процесс. Надеюсь, будет полезно не только мне, тем более, что с качественной документацией для Zope не все хорошо, мягко говоря.

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

Исходные материалы

Что в активе, кроме желания сделать Продукт.
Zope Version (Zope 2.11.4-final, python 2.4.4, win32)


http://sites.google.com/site/vasnake/ Zope.book.23.Extending.Zope.odt - перевод главы №23 зопобука. Глава разьясняет создание продуктов (Product) из зетклассов (ZClass) в Zope. Как выяснилось, эта информация устарела (ZClass are deprecated).

http://docs.zope.org/zope2/zdgbook/Products.html базовый источник информации о построении продуктов Zope.

http://docs.zope.org/zope2/zope2book/MaintainingZope.html#installing-new-products — несколько слов о процессе установки продукта в Zope.

http://wiki.zope.org/zope2/DiskBasedProduct — с миру по нитке, будет информация о продуктах.

c:\Zope\2.11.4\Zope\lib\python\Products\ZReST\
http://old.zope.org/Members/shh/ExtFile/ - примеры кода для изучения.

Заготовка

Для начала, следует знать, что разработка аддонов через ZClass-ы уже неактуальна. Поэтому главу 23 Зопобука можно не читать. Разрабатывать аддон надо построением питонского пакета определенного формата. Первым делом сделаю каркас, содержащий необходимый минимум.

Кстати, при разработке полезно запускать Zope в режиме дебага (файл c:\Zope\Instance\2.11.4\etc\zope.conf, переключатель debug-mode). Для запуска я сделал файл
debug.cmd с содержимым
@REM ~ # -*- mode: bat; coding: cp1251 -*-
@REM ~ # (c) Valik mailto:vasnake@gmail.com
@echo off
chcp 1251 > nul
set wd=%~dp0
pushd "%wd%"
set PYTHONPATH=
@rem предварительно надо остановить сервис, через ControlPanel ZMI к примеру
pushd c:\Zope\Instance\2.11.4\bin
start cmd.exe /c "c:\Zope\2.11.4\Python\python.exe zopeservice.py debug"
pause

Логи посмотреть можно тут
c:\Zope\Instance\2.11.4\log\Z2.log
c:\Zope\Instance\2.11.4\log\event.log

Чуть отвлекся, вернемся обратно к баранам.
Создание продукта заключается, по сути, в создании в нужном месте папки с нужным именем и наполнении этой папки нужными файлами. Согласно руководства http://docs.zope.org/zope2/zdgbook/Products.html
Забегая вперед: это описание не совсем точное, местами я консультировался с разработчиками других продуктов (ExtFile/ExtImage, ZReST и другие из c:\Zope\2.11.4\Zope\lib\python\Products\), путем изучения их кода.

Папка продукта должна располагаться тут
c:\Zope\2.11.4\Zope\lib\python\Products\
или лучше тут
c:\Zope\Instance\2.11.4\Products\

Я назову тебя стеклянным продукт «VCUFile», значит папка продукта будет
c:\Zope\2.11.4\Zope\lib\python\Products\VCUFile
или, что лучше
c:\Zope\Instance\2.11.4\Products\VCUFile

Какие файлы и папки следует включить в состав продукта:
vcufile.py – интерфейсы (соглашение такое, хотя можно и без них)
vcufileimpl.py – тестовая реализация интерфейсов, в продукте не нужен
vcufileproduct.py – реализация продукта
__init__.py – инициализация продукта
www – Contains your icon & ZPT files
help – Contains your help files
tests – Contains your unit tests
README.txt 
VERSION.txt
LICENSE.txt
INSTALL.txt
TODO.txt
CHANGES.txt and HISTORY.txt
DEPENDENCIES.txt
Конечно, можно весь код затолкать в __init__.py и снабдить его одним readme.txt, но лучше не надо. В процессе эволюции джентльмены выработали некоторые соглашения. Согласно этим соглашениям, содержимое продукта следует оформить как указано.

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

vcufile\vcufile.py
#!/usr/bin/env python
# -*- mode: python; coding: utf-8 -*-
# (c) Valik mailto:vasnake@gmail.com

# VCUFile public interfaces
# Фактически, этот модуль относится к документации
# и если бы его не было, работоспособности продукта это бы не помешало.

from zope.interface import Interface

class IVCUFile(Interface):
    """A giant file for chunked upload.
    File will be stored in filesystem.
    """

    def getFID():
        """File ID
        Пример метода типа get.
        Ну надо же было придумать хоть что нибудь.
        """
интерфейс следует описать из соображений документированности и установления «контракта». Людям так удобнее.

vcufile\vcufileproduct.py
#!/usr/bin/env python
# -*- mode: python; coding: utf-8 -*-
# (c) Valik mailto:vasnake@gmail.com

__doc__ = """VCUFile product module.
    The VCUFile Addon works like the Zope File product, but stores
    the uploaded file externally and can upload giant files because of chunked upload."""

__version__ = '3.0.0.alpha'

from Acquisition import Implicit
from Globals import Persistent
from AccessControl.Role import RoleManager
from OFS.SimpleItem import Item

from AccessControl import ClassSecurityInfo
from Globals import InitializeClass

from vcufile import IVCUFile
from zope.interface import implements

def addForm(self):
    """Returns an HTML form for VCUFile 'add' action.
    Constructor.
    Эта функция вызывается, когда пользователь добавляет обьект
    через список добавления в ZMI.
    Сабмит формы вызывает  addFunction (см.ниже).
    """
    return """<html>
    <head><title>Add VCUFile</title></head>
    <body><form action="addFunction">
    id <input type="type" name="id"><br>
    filename <input type="type" name="filename"><br>
    <input type="submit" value=" Add ">
    </form></body>
    </html>"""
#def addForm():

def addFunction(self, id, filename, REQUEST=None):
    """addForm processor function. Constructor.
    Create a new VCUFile and add it to container.
    Обработчик формы addForm, создает обьект и пишет его в контейнер.
    """
    p = VCUFileProduct(id, filename)
    self.Destination()._setObject(id, p)
    if REQUEST is not None:
        return self.manage_main(self, REQUEST, update_menu=0)
    return id
#def addFunction(dispatcher, id, filename):


class VCUFileProduct(Implicit, Persistent, RoleManager, Item):
    """VCUFile product class, implements IVCUFile
    Класс реализации продукта, самая сложная штука в проекте.
    """

    # Реализуем интерфейс
    implements(IVCUFile)

    #Item requerements (класс Item содержит эти атрибуты)
    meta_type = 'VCUFile'
    id = ''
    title = ''

    # используется для декларации ограничений безопасности
    security = ClassSecurityInfo()

    def __init__(self, id, filename):
        self.fid = ''
        self.id = id
        self.title = filename

    security.declarePublic('getFID')
    def getFID(self):
        """File ID
        Реализуем метод интерфейса
        """
        return self.fid
#class VCUFileProduct(Implicit, Persistent, RoleManager, Item)

InitializeClass(VCUFileProduct)
продукт и его фабрика. Минималистичнее сделать трудно, наоборот, сюда надо много добавить. К примеру, код реализации вкладок для управлятора ZMI; раскладку по привилегиям доступа.

vcufile\__init__.py
#!/usr/bin/env python
# -*- mode: python; coding: utf-8 -*-
# (c) Valik mailto:vasnake@gmail.com

__doc__ = """VCUFile initialization module. """
__version__ = '3.0.0.alpha'

# Самая простая часть (если не считать интерфейса), регистрация продукта

from vcufileproduct import VCUFileProduct, addForm, addFunction

def initialize(registrar):
    '''Addon register method.
    Call by Zope in startup time.
    '''
    registrar.registerClass(
        VCUFileProduct,
        constructors=(addForm, addFunction),
        icon = 'www/vcufile16.png'
    )
    # help notes, by example from Products\ExternalMethod\
    # чтобы это сработало, у меня сделан файл vcufile\help\VCUFile.stx
    registrar.registerHelp()
    registrar.registerHelpTitle('Zope Help')
регистрация продукта в ZODB, при запуске Zope.

vcufile\help\VCUFile.stx
VCU File: File object for upload giant files with chunks, in resumable mode.

  VCUFile is a regular file that can be upload to Zope server filesystem.
  Upload process is resumable and can be completed in any time
  from any computer.
минимальная справка по продукту, просто текстовый файл (первая строка добавлена только для тут, в оригинальном файле ее нет).

Содержимое файлов *.TXT я приводить не буду, оно достаточно тривиальное.

Ну вот, заготовка есть. Теперь то, что получилось можно добавить в Zope. Для этого достаточно разместить файлы в папке
c:\Zope\Instance\2.11.4\Products\VCUFile
и перезапустить Zope.

Если продукт зарегился при запуске Zope, найти его можно тут

А раз есть продукт, можно добавить объект в любую папку,

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

На этом сегодня всё.

Позже я покажу как эту заготовку превратить в нормальный, полновесный, рабочий продукт. В частности, надо сделать нормальную форму добавления; нужна вкладка редактирования; с безопасностью разобраться. И многое другое.
Stay tuned.

kwords: web http chunked resumable upload, Zope, python, silverlight, javascript

Архив блога

Ярлыки

linux (241) python (191) citation (186) web-develop (170) gov.ru (159) video (124) бытовуха (115) sysadm (100) GIS (97) Zope(Plone) (88) бурчалки (84) Book (83) programming (82) грабли (77) Fun (76) development (73) windsurfing (72) Microsoft (64) hiload (62) internet provider (57) opensource (57) security (57) опыт (55) movie (52) Wisdom (51) ML (47) driving (45) hardware (45) language (45) money (42) JS (41) curse (40) bigdata (39) DBMS (38) ArcGIS (34) history (31) PDA (30) howto (30) holyday (29) Google (27) Oracle (27) tourism (27) virtbox (27) health (26) vacation (24) AI (23) Autodesk (23) SQL (23) humor (23) Java (22) knowledge (22) translate (20) CSS (19) cheatsheet (19) hack (19) Apache (16) Klaipeda (15) Manager (15) web-browser (15) Никонов (15) functional programming (14) happiness (14) music (14) todo (14) PHP (13) course (13) scala (13) weapon (13) HTTP. Apache (12) SSH (12) frameworks (12) hero (12) im (12) settings (12) HTML (11) SciTE (11) USA (11) crypto (11) game (11) map (11) HTTPD (9) ODF (9) Photo (9) купи/продай (9) benchmark (8) documentation (8) 3D (7) CS (7) DNS (7) NoSQL (7) cloud (7) django (7) gun (7) matroska (7) telephony (7) Microsoft Office (6) VCS (6) bluetooth (6) pidgin (6) proxy (6) Donald Knuth (5) ETL (5) NVIDIA (5) Palanga (5) REST (5) bash (5) flash (5) keyboard (5) price (5) samba (5) CGI (4) LISP (4) RoR (4) cache (4) car (4) display (4) holywar (4) nginx (4) pistol (4) spark (4) xml (4) Лебедев (4) IDE (3) IE8 (3) J2EE (3) NTFS (3) RDP (3) holiday (3) mount (3) Гоблин (3) кухня (3) урюк (3) AMQP (2) ERP (2) IE7 (2) NAS (2) Naudoc (2) PDF (2) address (2) air (2) british (2) coffee (2) fitness (2) font (2) ftp (2) fuckup (2) messaging (2) notify (2) sharepoint (2) ssl/tls (2) stardict (2) tests (2) tunnel (2) udev (2) APT (1) Baltic (1) CRUD (1) Canyonlands (1) Cyprus (1) DVDShrink (1) Jabber (1) K9Copy (1) Matlab (1) Portugal (1) VBA (1) WD My Book (1) autoit (1) bike (1) cannabis (1) chat (1) concurrent (1) dbf (1) ext4 (1) idioten (1) join (1) krusader (1) license (1) life (1) migration (1) mindmap (1) navitel (1) pneumatic weapon (1) quiz (1) regexp (1) robot (1) science (1) seaside (1) serialization (1) shore (1) spatial (1) tie (1) vim (1) Науру (1) крысы (1) налоги (1) пианино (1)