What is LIO target? Linux-OI Target is a Linux SCSI target introduced in a kernel v.2.6.38 and supports different fabrics modules like FibreChannel, iSCSI, iSER, etc. It works in a kernel space, so it’s faster than tgtd which is used in Cinder by default. Why do we still use tgtd instead of more faster LIO in Cinder by default? It’s only because we have to support rolling upgrades and we don’t know how to migrate from TGTd to LIO in a such way and pass Grenade successfully.

We’ve got non-voting gate-tempest-dsvm-full-lio-ubuntu-xenial job for a while. Due to some of my performance tests results it’s really faster than tgtd. So, how can you use it?

It’s pretty easy with LVM + Devstack. Everything you need is to add 'CINDER_ISCSI_HELPER=lioadm' to your localrc/local.conf.

If you have already configured Cinder+LVM it’s easy too to switch to the new target driver. I mean that you don’t have any in-use volume now but you have Cinder with LVM configured and running. Just follow these steps:

1) first of all, you have to install ‘rtslib-fb’ package using pip:

# pip install rtslib-fb

or using OS package manager:

# apt-get install python-rtslib-fb 

2) stop tgt:

# sudo service tgt stop

3) change /etc/cinder/cinder.conf to use LIO driver:

Set ‘iscsi_helper = lioadm' instead of ‘iscsi_helper = tgtadm


4) restart cinder and enjoy it!


I don’t like to post reviews. But this app is amazing! I use it more than 3 years and just want to share with you a list of my favorite features:


  • Split Panes - I started use iTerm2 because of it. Just press Cmd+d to split current panel. Cmd+Alt+Arrow to move between the panels
  • Search - Cmd+f to search throm commands and their output it work for the all session history
  • Inline images - seriously,  you can view images in your terminal. You need just to download ‘imgcat’ script and enjoy it
  • Download file from remote host - no need to use scp more, just ‘it2dl filename’ and it will download a file to ~/Downloads directory
  • Open list of most popular directories - Cmd+Alt+/ and select what you need. It’s real fast and useful
  • Restore the last session - no comments are needed here

P.S. Don’t forget to enable ‘Show Tip of the Day’ feature to enjoy more.

Useful links:



  • https://iterm2.com/documentation-utilities.html
  • https://www.iterm2.com/features.html

  • 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/id_rsa.pub # 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 https://github.com/openstack/python-cinderclient.git
    $  cd python-cinderclient
    $  sudo pip install .

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

    $  git clone https://github.com/openstack/python-brick-cinderclient-ext.git
    $  cd python-brick-cinderclient-ext
    $  git fetch https://review.openstack.org/openstack/python-brick-cinderclient-ext 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_IDENTITY_API_VERSION="2.0"
    export OS_NO_CACHE="1"
    export OS_PASSWORD="password"
    export OS_REGION_NAME="RegionOne"
    export OS_TENANT_NAME="admin"
    export OS_USERNAME="admin"
    export OS_VOLUME_API_VERSION="2"
    $  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/ip-

    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.


    [1] https://github.com/openstack/cinder-specs/blob/master/specs/mitaka/use-cinder-without-nova.rst
    [2] https://github.com/openstack/python-brick-cinderclient-ext
    [3] https://github.com/openstack/os-brick
    [4] https://review.openstack.org/263744
    [5] https://gist.github.com/e0ne/2579921aba839322decc
    [6] http://docs.openstack.org/developer/ironic/dev/dev-quickstart.html#deploying-ironic-with-devstack
    [7] https://cloud-images.ubuntu.com/
    [8] http://docs.openstack.org/developer/ironic/deploy/install-guide.html#image-requirements
    [9] http://paste.openstack.org/show/483734/
    [11] http://paste.openstack.org/show/483742/
    [12] http://paste.openstack.org/show/483743/

    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 (https://en.wikipedia.org/wiki/Xvfb)
    3. Run tests:
      • Simple way for OpenStack Horizon:
        ./run_tests.sh --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, default=datetime.now())

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

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

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

    CREATE TABLE test (
         id integer,
         name varchar(20),

    Т.е. значение по умолчанию выставляются не на уровно СУБД, а на уровне модели при вставке. Более того, код “default=datetime.now()” отрабатывает только раз при создании модели и все значения будут одинаковыми. Для исправления этого достаточно сделать что бы значение по умолчанию вычислялось каждый раз новое. Для этого в параметр default можно передать любой callable объект, например lambda-функцию (default=lambda: datetime.now()). Если копнуть дальше в исходники, что значение 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 (https://github.com/mahmoudimus/nose-timer), столкнулся с проблемой немного удивился в отличии списка поддерживаемых версия питона nose, а в следствии и nose-timer, и Travis CI. Travis работает только с несколькими самыми используемыми версиями - 2.6, 2.7, 3.2 и 3.3 (http://about.travis-ci.org/docs/user/languages/python/). Для меня это показатель: раз такой популярный инструмент поддерживает эти версии, то следует задуматься какие версии 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 и пользоваться), но я потратил массу времени из-за того, что выбрал не тот инструмент. Нельзя путать процесс обучения и процесс разаботки. Между ними очень тонкая грань, которую легко перейти и потерять время напрасно.