OpenStack Cinder provides an API to attach/detach volume to Nova instances. This is public, but not documented API which is used only by Nova now.  In scope of “Attach/detach volumes without Nova” [1] blueprint we introduce new python-cinderclient extension to provide attach/detach API not only for Nova called python-brick-cinderclient-ext. Before Mitaka release everybody who want to use Cinder volumes not only with Nova instances have to create hardening scripts based on python-cinderclient and os-brick [3] projects to make it done.

Since Mitaka, Cinder opens attach/detach API for any users. It will allow to:


  • Attach volume to Ironic instance
  • Attach volume to any virtual/baremetal host which is not provisioned by Nova or Ironic


It means, Cinder becomes stand-alone project that could be used outside OpenStack cloud with one limitation: Keystone is still required.

For now, python-brick-cinderclient-ext has only ‘get-connector’ API. Attach/detach features are under development and any feedback are welcome to get implemented in the best way. I hope, it will be implemented and documented as well in scope of Mitaka release cycle.

I will show you how it works in current proof-of-concept code [4]. Anybody is welcome to review and test it:).

To demonstrate this feature I will use virtual Devstack environment with Ironic+Cinder. Here is my local.conf [5].

Current limitations are:


  • Ironic instance must have access to API and storage networks (it works on Devstack with a default configuration
  • Users inside instance must have root permissions and be able to install required software


Detailed manual how to setup Ironic using Devstack could be found here [6]. Since volumes attach/detach operations require python, open-iscsi, udev and other packages I will use Ubuntu-based image for Ironic instances. You can use Ubuntu cloud image [7] or build your own using ‘disk-image-builder’ tool [8]. I’ve built my Ubuntu image with disk-image-builder:

$  disk-image-create ubuntu vm dhcp-all-interfaces grub2 -o ubuntu-image
$  glance image-create --name ubuntu-image --visibility public \
--disk-format qcow2 \
--container-format bare < ubuntu-image.qcow2

After it we need to run Ironic instance:

#  query the image id of the default cirros image
$  image=$(nova image-list | egrep "ubuntu" | awk '{ print $2 }')
#  create keypair
$  ssh-keygen
$  nova keypair-add default --pub-key ~/.ssh/ # spawn instance $ prv_net_id=$(neutron net-list | egrep "$PRIVATE_NETWORK_NAME"'[^-]' | awk '{ print $2 }') $ nova boot --flavor baremetal --nic net-id=$prv_net_id --image $image --key-name default testing

Wait until instance is booted and ready [9]:

$  nova list
$ ironic node-list

Now you can connect to the instance using SSH:

$  ssh ubuntu@

By default, in Devstack both Nova and Ironic instances have access to OpenStack APIs.

To attach volume you need to install required packages inside you instance:

$  sudo apt-get install -y open-iscsi udev python-dev python-pip git

NOTE: if you can't acces Internet inside your instance, try the following command on the DevStack host:

$  sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERAD

Clone and install the latest python-cinderclient (the latests version from PyPi will also work but you'll need to pass --os-volume-api-version explicit):

$  git clone
$  cd python-cinderclient
$  sudo pip install .

Clone the python-brick-cinderclient-ext and apply the patch:

$  git clone
$  cd python-brick-cinderclient-ext
$  git fetch refs/changes/44/263744/8 && git checkout FETCH_HEAD
$  sudo pip install .

That’s all! Now, you can attach/detach volumes inside your instance. Because it is still PoC implementation you need few additional steps:

$  PATH=$PATH:/lib/udev
$  export PATH

The steps above is needed until python-brick-cinderclient-ext will use oslo.rootwrap or privsep libraries.

Verify, that python-brick-cinderclient-ext works well [10] (you need to setup your own credentiala and auth_url):

$  cat << EOF >> ~/openrc
 #!/usr/bin/env bash
export OS_AUTH_URL=""
export OS_NO_CACHE="1"
export OS_PASSWORD="password"
export OS_REGION_NAME="RegionOne"
export OS_TENANT_NAME="admin"
export OS_USERNAME="admin"
$  source ~/openrc
$  sudo -E cinder get-connector

You should get something this: [11].

Finally, create and attach volume to your Ironic instance:

$ cinder create 1
$ sudo -E PATH=$PATH cinder local-attach 0a946c67-2d5c-4413-b8ec-350240e967d2

You should get something like: [12]

Now you can verify that volume is attached via iSCSI protocol [13]:

$  sudo iscsiadm -m session
$  ls -al /dev/disk/by-path/

Detach is also easy:

$  sudo -E PATH=$PATH cinder local-detach 0a946c67-2d5c-4413-b8ec-350240e967d2

That’s all! You’ve got attached your Cinder volume to an Ironic instance without Nova! You can do the same steps to attach volumes inside Nova instance or your desktop. It will work too. I will show you a demo with Nova instance and cloud config scrips in the next post.



Other useful links:

Didn't google "how to run horizon integration tests" unswer in 10 seconds. Making note how to do it.

My development environment usually looks like: macbook + VM with Ubuntu Server or CentOS without GUI. I try to run all tests inside VMs. In case of Selenium tests, I need some preparation for it:

  1. $ sudo apt-get install firefox
    this command will install FireFox. Selenium has WebDriver for it out of the box
  2. $ sudo apt-get install xvfb
    install Virtual Frame Buffer for X Server (
  3. Run tests:
    • Simple way for OpenStack Horizon:
      ./ --integration --selenium-headless
    • Hard way for any project:
      • Start xvfb:
        $ sudo Xvfb :10 -ac
      • Start headless FireFox:
        DISPLAY=:10 firefox
      • run tests
Useful links:

It’s my first try to blog in English. Feel free to comment for any typo, grammar errors, etc.

There are nothing new, nothing innovative below. There are just a step-by-step guide to not forget and to not google each time when I need it.

Usually, on my dev environment, I’ve got KVM instances with disk images in QCOW format. So time to time I need to extend my virtual disks to get more free space.

  1. Shutdown VM:
    • `sudo shutdown -p now` inside VM
    • `sudo virsh shutdown <vm_name>` on my host
  2. Find QCOW file to change it:
    Be default, it’s located at `/var/lib/libvirt/images`
    `virsh dumpxml dsvm1 | grep file`
    Find something like:
    <disk type='file' device='disk'>
    <source file='/var/lib/libvirt/images/devstack.img'/>
  3. Create backup of your virtual drive (E.g. `cp /var/lib/libvirt/images/devstack.img /var/lib/libvirt/images/devstack.img.bak`)!!!!
  4. Change QCOW image size: `sudo qemu-img resize /var/lib/libvirt/images/devstack.img +10G`  - this command increases size with 10 GB more
    • If image has snapshots, you need to delete them first:
      sudo qemu-img snapshot -l /var/lib/libvirt/images/devstack.img
      sudo qemu-img snapshot -d <snapshot_id> /var/lib/libvirt/images/devstack.img
  5. Boot VM: `sudo virsh start <vm_name>`
  6. NOTE: I don’t care about disk data in this example. But I have backup (see #3) and can restore all needed data.
    Create new partition table with fdisk. fdisk can’t change partition size, we need to delete and create a new one:
    sudo fdisk /dev/sdb
    ‘d’ - delete partitions(s)
    ‘p’ - create new partition(s)
    ‘w’ - write changes
  7. Mount drive to your VM:
    sudo mount /dev/sdb1 /mnt/data
  8. Create filesystem:
    sudo resize2fs /dev/vda3
  9. In case, if you use something else like parted instead of fdisk, you could just extend filesystem size:
    sudo mkfs.ext3 /dev/sdb1


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


11:00. Пришел на работу. Нужно сделать кофе.

11:10. Хорошо, теперь можно пописать код.

11:11. Что за [IMPORTANT] письмо такое свалилось?

12:30. Разгреб почту, ответил начальству, поддержке и коллегам. Можно писать код.

12:45. Синк-ап.

13:00. Нужно написать недельный репорт чем занимался.

13:30. Обед - не сегодня, лучше попишу код.

13:35. Опять тесты на CI завалились.

15:30. Нашел проблему в другом компоненте и две у себя. Фикшу баги.

18.00. Чашечка кофе с печенькой будет не лишней.

18:15. Нужно занться планированием на следующую итерацию.

18:30. Как у кастомера ничего не работает???

20:00. Вот сейчас таки займусь этой фичей, которую все давно ждут.

20:30. Что значит что нам надо пофиксить все баги до понедельника?

21.00. Bug Fixed. Завтра утром попишу код и сделаю фичу. Там всего-то 2 часа делов и час на написание тестов...

Проблема пришла от туда, от куда не ждали. А именно от таких 5-ти строчек кода:

class CustomModel(Base):
    __tablename__ = 'custom_model'
    id = Column(Integer,)
    name = Unicode(100)
    datetime = Column(DateTime,

Когда-то давно я или неправильно понял доку, недочитал или прочитал не то, но был уверен в том, что этот код “компилируется” в примерно такой SQL (код приведен только в целях примера и может не работать:) ):

CREATE TABLE custom_model (
     id integer,
     name varchar(20),
    created_at datetime default getdate().now

Проблема проявилась в том, что order by по полю created_at не работал. Все дело в том, что вышеприведенный код генерируется в такой SQL:

     id integer,
     name varchar(20),

Т.е. значение по умолчанию выставляются не на уровно СУБД, а на уровне модели при вставке. Более того, код “” отрабатывает только раз при создании модели и все значения будут одинаковыми. Для исправления этого достаточно сделать что бы значение по умолчанию вычислялось каждый раз новое. Для этого в параметр default можно передать любой callable объект, например lambda-функцию (default=lambda: Если копнуть дальше в исходники, что значение default - это инстанс типа ColumnDefault, спрятанный за удобным синтаксисмом.

Что бы значения по умолчанию выставлялись на уровне СУБД в SQLAlchemy необходимо использовать параметр server_default (к слову, onupdate и другие триггеры на сервере ставятся с помощью добавления префикса “server_”, например server_onupdate). Если необходимо в качестве значения по умолчанию задать какое-то выражение (sql expression), то делается это с помощью следующей конструкции:


К слову о  Django - там все работает так же, только про server_default я ничего не нашел:(.

Ссылки по теме:



Выбор версии языка программирования, фреймворка - сложный вопрос, который всегда бурно обсуждался и будет обсуждаться. В enterprise мире часто, но не всегда, используют старые и проверенные инструменты. В то время как Python 2.7 все еще нет из коробки в RedHat/CentOS/др дистрибутивах, в некоторых уже используется Python 3.3, пусть и не в качестве системного. В мире opensource - наоборот, часто используют только самое-самое новое. Но это правило не относится к разным фреймворкам. Представьте, что завтра, например, Django будет поддерживать только Python 3.4, который еще не зарелизился. Никто им пользоваться не будет. Вот и приходится поддерживать несколько версия языка программирования. Похожая ситуация с paramiko, nose и другими популярными проектами/инструментами/библиотеками.

Сегодня, пытаясь настроить Travis CI для небольшокго плагина для nose (, столкнулся с проблемой немного удивился в отличии списка поддерживаемых версия питона nose, а в следствии и nose-timer, и Travis CI. Travis работает только с несколькими самыми используемыми версиями - 2.6, 2.7, 3.2 и 3.3 ( Для меня это показатель: раз такой популярный инструмент поддерживает эти версии, то следует задуматься какие версии python'а нужно поддерживать в своих библиотеках.


P.S. А тем временем стал доступен Python 3.4.0 beta 2.

Если коротко, то типы в Python делятся либо на встроенные и пользовательские, либо на mutable и immutable (сразу так и не подобрал подходящего перевода на русский язык). Ну а если немного подробнее, то как-то так:

Рассмотрим простой пример кода (здесь и далее примеры кода будут писаться и выполняться в ipython’е):

In [1]: a = 1000
In [2]: a
Out[2]: 1000
In [3]: type(a)
Out[3]: int

Здесь мы объявляем переменую создаем объект (object) a, со значением(value) 1000 типа(type) int. Объекты в python - это абстрация над данными. Поэтому любой объект содержит в себе идентификатор(identity), тип и значение. Идентификатор - это адрес объекта в памяти, который никогда не меняется. Но мы можем присвоить объекту новое значение, написав, следующий код:

In [4]: a = 'hello'
In [5]: a
Out[5]: 'hello'
In [6]: type(a)
Out[6]: str

Теперь мы присвоили объекту a новое значение типа string. За кулисами этого всего, интерпритатор сделал, примерно, такое: создал новый объект ‘hello’, и поменялл ссылку переменной a на новый объект в памяти. При этом старое значение 1000 все еще хранится в памяти и будет удалено сборщиком мусора, т.к. на него нет ни одной ссылки. Тоже самое происходит и присвоении переменной нового значений такого же типа:

In [7]: b = 1000
In [8]: id(b)
Out[8]: 40235856
In [9]: b = 1001
In [10]: id(b)
Out[10]: 39189680

Интересный эффект будет при использовании целых чисел в диапазоне от -5, до 256 - их id всегда будет одинаковый, т.к. для улучшения быстродействия интерпритатор при старте создает объекты с этими значениями.

Стандартные типы в CPython делятся на два типа: mutable и immutable. Mutable — это обекты, значения которых могут быть изменены(например, list), а immutable — это обекты, значения которых не может меняться (например, string). Но это не значит, что если у нас есть переменная immutable типа, то ее нельзя изменить. Например, с типом string все привыкли работать так:

In [11]: text = 'Hello'
In [12]: text
Out[12]: 'Hello'
In [13]: text = 'Hello, World'
In [14]: text
Out[14]: 'Hello, World'

Как мы видим, начение переменной text поменялось. На самом деле, при присвоении переменной text нового значения, в памяти создается новый объект, и переменная text начинает на него ссылаться. В этом легко убедится при помощи функции id - получить идентификатор до и после изменения переменной.

Mutable типы ведут себя так:
In [15]: l = [1, 2, 3]
In [16]: l
Out[16]: [1, 2, 3]
In [17]: id(l)
Out[17]: 139901299808088
In [18]: l.append(4)
In [19]: l
Out[19]: [1, 2, 3, 4]
In [20]: id(l)
Out[20]: 139901299808088

Основываясь на этом свойстве(и не только), ключами в словаре (dict) могут быть только immutable обекты:

In [21]: d = {'one': 1}
In [22]: type(d)
Out[22]: dict
In [23]: d[1]=1
In [24]: d
Out[24]: {1: 1, 'one': 1}
In [25]: d[l] = 'list'
TypeError                                 Traceback (most recent call last)
<ipython-input-25-4f49b18d7af7> in <module>() ----> 1 d[l] = 'list'
TypeError: unhashable type: 'list'

Immutable типы в Python — это числа(numbers), строки (strings) и кортежи (tuples).

Для облегчения привжу список некоторых всторенных типов (built-in types):


  • Numbers (числа) — int, long, float, complex(3+4j), Decimal, Fraction
  • Sequence Types (последовательности) — str, unicode, list, tuple, bytearray, buffer, xrange
  • Sets — set, frozenset
  • Maps (хеш-таблицы) — dict
  • Files, context managers и другие.


Ссылки по теме:




Безусловно, выбор языка программироания, текстового редактора, IDE, операционной системы и многого другого лежит на плечах каждого отдельно взятого разработчика. Даже если он работает в команде.

По сути, всем все-равно в какой IDE пишете код, если он работает так, как надо и написан вовремя. Унификация средст разработки внутри компании/команды лишь облегчает жизть менеджерам, ИТ, новым членам команды и упрощает коммуникацию между разработчиками. Ведь на много легче один раз написать инструкцию по установки всего нужного для запуска проекта ПО, например, для Ubuntu 13.10 x64 и запуск проекта в PyCharm, чем каждый раз сталкиваться с проблемой как запустить X в окружении Y.

Но каждый разработчик в праве решать сам чем он будет пользоваться. Вот только к выбору инструментов нужно подходить “с умом”. Главное - хорошо знать инструменты, которыми вы пользуетесь. Иначе будет ситуация, когда используется Photoshop только для изменения размера картинки. Досконально знать OS, IDE, текстовый редактор, которым пользуетесь каждый день не только приятно, но и полезно. Это может экономить массу времени. Всем известно, что использования горячих клавиш, в некоторых условиях, в разы быстрее, чем совершать нужные действия мышкой. Аналогичная ситуация с разнообразными инструментами автоматизации. Если мне нужно выполнить действиее более 2-3х раз - я пишу небольшой скрипт на bash или python. Иногда скрипты получаются не очень и маленькими, иногда в помощь приходят fabric/chef/puppet. Готовые настройки для используемых редактором и систем (профайлы bash, zsh) - упрощают жизь и экономят время, которое можно потратить на более полезные и интересные вещи.

Все вышесказанное всем известно и описано в множестве книг и постов. Захотелось повторить, т.к. я в очередной раз наступил на эти грабли: потратил почти час на написание простого скрипта на bash’е, аналог которого на python мне удалось написать за 10 минут. Да, я узнал кое-что новое о bash, но это не мой основной язык программирования, и скрипт писался исключительно для себя (и не важно, что любой может скачать его с github и пользоваться), но я потратил массу времени из-за того, что выбрал не тот инструмент. Нельзя путать процесс обучения и процесс разаботки. Между ними очень тонкая грань, которую легко перейти и потерять время напрасно.

Всегда недолюбливал Apache из-за формата его конфига. Конфиг Nginx’а мне вседа было порще читать и писать. Да и статику им раздавать хорошо и быстро, поэтому от Apache я, по возможности, отказываюсь. Но частая проблема с nginx в том, что приходится собирать необходимые модули из исходников самому, со всемы вытикаюющими плюсами и минусами. И, как полагается любому популярному и быстро развивающемуся проекту, документация по сборке этих самих модулей не всегда полная и актуальная.

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

Оффициальная документация расположена тут и тут. При этом сложилось впечатление, что на github’е она более актуальная и полная. По ним можно собрать все, при условии, что у вас уже стоят нужные пакеты. Мне понадобились:

$ sudo apt-get install autoconf automake libtool libcurl4-openssl-dev

Тажке, не забыть выполнить “./” перед “./configure”. Почему-то, в секции “Installation for NGINX” документации написаны только общие сведения. Мне, например, не хотелось и не нужно было собирать mod_security для apache, устанавливать пакет apache2-dev и т.д. Поэтому выполнил configure со следующими параметами:

$ ./configure --prefix=/usr --disable-apache2-module --enable-standalone-module

Далее сборка, установка и настройка у меня прошла без проблем.

Как всегда, в рубрике “мысли в слух” звучит только исключительно мнение автора и может не соответствовать действительности.

Безусловно, всем, ну ладно, почти всем, хочется писать только на самых последний версиях фреймворков, использовать самые новые технологии и навсегда забыть о так называемом legacy code. Но мало кто из разработчиков думает о том, когда и зачем это нужно, а когда - невозможно. Разберем пример выбора фреймворка Х на примере нескольких случаев.

Случай #1. Работает - не трожь!

Очень распространенный и нелюбимый мною случай. Проекту N лет, заказчика он устраивает, клиенты довольны. Но проект еще развивается, нужно не только фиксить баги, но и улучшать текущую, добавлять нувою функциональность. Но что бы добавить одну простую новую функцию, нужно написать “много” кода, который уже есть в новой версии фрреймворка X, который используется. А новая версия может быть несовместима со старой. А если и совместима - нет гарантии что все быстро и хорошо заработает. Часто бывает что проще и дешевле переписать с нуля. И вот тут должен выключаться режим разработчика и включаться режим владельца бизнеса. На основе естимейтов по переходу на новый фреймворк, можно посчитать что дешевле: дописать старое или написать, частично или полностью, новое. Наверняка, в каких-то правильных книжках по менеджменту (возможно, не только для ИТ) и/или економики уже есть готовые формулы для этого, но я не встречал такого. Самое сложное здесь то, что формула будет не такой простой, как может показаться изначально.


Функция А будет сделана(разработанна, протестированна, задеплоенная - вообщем, готова для использования) за 30 часов на старом фреймворке и за 20 на новом. Умножая кол-во часов на n денег легко посчитать как дешевле. Но тут нужно учитывать всевозможные риски с новым фреймворком - его баги, баги после миграции, стоимость изучения и т.д.

Пример выше сильно упрощен и я не сталкивался с таким в работе. Обычно, все выглядит так (цифры придуманы):

Функция А будет сделана(разработанна, протестированна, задеплоенная - вообщем, готова для использования) за 10 часов на старом фреймворке и за 50 на новом. Разница по деньгам и времени - в 5 раз. Тажело будет уговорить менеджера и/или заказчика. Но тут на решение должны влиять другие факторы, а именно:


  • каждая новая фича будет стоить дешевле из-за того, что разработчики уже выучат матчасть, набьют новые шишшки с новым фреймворком и т.д., в последствии чего - в идеальных условиях, время разработки будет стремится к времени на разработку этой же фичи на старом фремйворке - n часов.
  • во время миграции на новый фреймворк, пользователи продукта не будут видеть новых версий, что сильно влияет на time to market (TTM), может пказаться, что продукт больше не развивается.
  • на протяжении этого всего времени, нужно будет поддерживать старую/текущую версию продукта, что тоже стоит денег.
  • переписанный продукт на новый фреймворк X - часто является скорее новым продуктом, чем новой версией старого, со всемы вытикающими последствиями.



Случай #2. “Теперь мы используем фреймворк X версии Y” или “у нас новый проект на фреймворке X версии Y”.

Казалось бы все хорошо, фреймворк X версии Y - самая последняя версия самого крутого фреймворка, использующего самые новые технологии. Все хотят это испльзовать и работать с этим. Но пройдет сколько-то времени (месяц, два, год), а мы пишем все тот же проект, на все тех же технологиях, которые были актуальны и популярны полгода-год-два назад. И все сводится к вышеописанному сценарию #1.


Случай #3. Идеальный процесс разработки продукта.

Раз это описание идеального процесса, пусть это будет что-то типа continuous development. Все лучшее от разнообразных Agile методологий, Kaizen и т.д. и заказчик готов платить достаточно денег при соблюдении таких условий:


  • продукт будет выпущен в оговоренные сроки
  • дена дальшейшей разработки будет снижаться
  • цена поддержки будет низкой


Допустим, дата релиза запланированна на весну (сейчас декабрь). К тому времени должна выйти новая версия хорошо известного фреймворка X, а сейчас доступна совсем не production ready alpha. Т.к. авторы этого фреймворка всегда выпускают релизы вовремя(у нас же идеальная ситуация:) ), то можно начать писать на нем. Но т.к. у нас в руках очень сырая и нестабильная alpha версия, то нам нужно обезопасить себя от всяческих рисков и проблем, связанных с багами и недоработками фреймворка. Я вижу только один действительно рабочий способ - внедрение continuous integration процесса в полной его мере: покрытие всяческими тестами и постоянный их запуск. Таким образом, особенно при хорошем покрытии кода unit-тестами, мы можем отделить ошибки которые связаны с нашим кодом, от тех, что зависят от фреймворка и/или сторонних библиотек.

Таким образом мы берем на себя технологический риски, связанные с “сырым” фреймворком и возможные потери денег как на разработку продукта, так и на возможную недополученную прибыль от его использования и/или продажи. Но если все будет хорошо, и проект успешно выйдет в production, то мы получим следующие плюсы от этого:


  • малое значение TTM - продукт выходит на рынок как только пояляется новая технология/фреймворк
  • вы становитесь early adopters выбранного вами фреймворка
  • качество продукта будет достаточно высоким (см. о continuous integration)
  • счастливые разработчики, использующие самые последние технологии и фрейморки
  • разработка следующей версии продукта стартует быстрее (есть наборы тестов, CI) и так же может быть на еще более новой версии фреймворка


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


Ссылки по теме: