---
date: 2015-05-05
title: Identify and mounting Cinder Volumes in Openstack Heat
category: devops
---

I\'m back playing with Openstack again. The day job once again Openstack
based, and as of last week my lab is all Openstack too. While
[oVirt](http://ovirt.org) is awesome, I felt like a change.

Anyway, the meat of today\'s problem comes from the day job. I have some
instances deployed via heat that have multiple Cinder volumes attached
to them, these then need to be mounted in a certain way. The syntax for
attaching a cinder volume to an instance is:

    instance_vol_att:
        type: OS::Cinder::VolumeAttachment
        properties:
          instance_uuid:  { get_resource: instance }
          volume_id: { get_resource: instance_vol_data }
          mountpoint: /dev/vdb

See at the end there is `mountpoint`? Awesome, my device will always
appear as /dev/vdb!

No! Unfortunately, there is no link between Cinder/Nova and *udev*
within the instance. As a result, udev will simply assign it a device
name in the same way your workstation does to a USB key: it could be
anything.

So what is a poor Openstack admin to do?

Each volume has a UUID, which in the example above. Lets start with a
simple HOT template to create a single instance and volume:

    heat_template_version: 2014-10-16
    description: A simple server to run Jenkins

    parameters:
      imageid:
        type: string
        default: Centos-7-x64
        description: Image use to boot a server

    resources:
      jenkins:
        type: OS::Nova::Server
        properties:
          image: { get_param: ImageID }
          flavor: m1.tiny
          networks:
          - network: { get_param: NetID }
      jenkins_data:
        type: OS::Cinder::Volume
        properties:
          size: 50G
      jenkins_data_att:
        type: OS::Cinder::VolumeAttachment
        properties:
          instance_uuid: { get_resource: jenkins }
          volume_id: { get_resource: jenkins_data}

That will create everything we need. The rest we need to pass though
from Nova to the instance somehow. While Nova does not talk to udev, it
does pass the `volume_id` though, albeit with a caveat. the ID is
truncated to **20** characters and is available as
`/dev/disk/by-id/virtio-volid20chars`. We can now access this using the
userdata property and `cloud-init`.

I actually create a small bash script then run it later, so now my
*Server* resource will look like:

    jenkins:
      type: OS::Nova::Server
        properties:
          image: { get_param: ImageID }
          flavor: m1.tiny
          networks:
            - network: { get_param: NetID }
          user_data_format: RAW
          user_data:
            str_replace:
              template: |
                #cloud-config
                write_files:
                  - content: |
                      #!/bin/bash
                      voldata_id="%voldata_id%"
                      voldata_dev="/dev/disk/by-id/virtio-$(echo ${voldata_id} | cut -c -20)"
                      mkfs.ext4 ${voldata_dev}               
                      mkdir -pv /var/lib/jenkins
                      echo "${voldata_dev} /var/lib/jenkins ext4 defaults 1 2" >> /etc/fstab
                      mount /var/lib/jenkins
                    path: /tmp/format-disks
                    permissions: '0700'
                runcmd:
                  - /tmp/format-disks
              params:
                "%voldata_id%": { get_resource: jenkins_data }
    jenkins_data:
      type: OS::Cinder::Volume
      properties:
        size: 50
    jenkins_data_att:
      type: OS::Cinder::VolumeAttachment
      properties:
        instance_uuid: { get_resource: jenkins }
        volume_id: { get_resource: jenkins_data}

What is happenning here? I create 3 resources:

-   a server
-   a volume
-   a volume attachment

Within the server there is a *cloud-init* script passed in via
*user*data\_. This cloud-init script is created using a template which
has a single parameter. This parameter is `%voldata_id%` - I put `%`
symbols around all my variables in this context, it makes false matches
pretty much impossible. The `get_resource` command collects the ID of
the Cinder volume I created.

Now we move into the *cloud-init* script created which does 2 things:

-   creates a bash script, including the variable for the ID
-   launches that scripts

The Bash script calculates what the device will be (`$voldata_dev`),
formats it and mounts it at the mountpoint it creates. It also adds this
into `/etc/fstab` for the future.

This can easily be used for multiple volumes. All one does is add an
extra parameter to collect the extra resources, then extend the Bash
script to do them too.
