Volume Attach/Detach workflow

There are six API calls associated with attach/detach of volumes in Cinder (3 calls for each operation). This can lead to some confusion for developers trying to work on Cinder. The convention is actually quite simple, although it may be difficult to decipher from the code.

Attach/Detach Operations are multi-part commands

There are three things that happen in the workflow for an attach or detach call.

1. Update the status of the volume in the DB (ie attaching/detaching) - For Attach, this is the cinder.volume.api.reserve method - For Detach, the analogous call is cinder.volume.api.begin_detaching

2. Handle the connection operations that need to be done on the Volume - For Attach, this is the cinder.volume.api.initialize_connection method - For Detach, the analogous calls is cinder.volume.api.terminate_connection

3. Finalize the status of the volume and release the resource - For attach, this is the cinder.volume.api.attach method - For detach, the analogous call is cinder.volume.api.detach

Attach workflow

reserve_volume(self, context, volume)

Probably the most simple call in to Cinder. This method simply checks that the specified volume is in an “available” state and can be attached. Any other state results in an Error response notifying Nova that the volume is NOT available. The only valid state for this call to succeed is “available”.

NOTE: multi-attach will add “in-use” to the above acceptable states.

If the volume is in fact available, we immediately issue an update to the Cinder database and mark the status of the volume to “attaching” thereby reserving the volume so that it won’t be used by another API call anywhere else.

initialize_connection(self, context, volume, connector)

This is the only attach related API call that should be doing any significant work. This method is responsible for building and returning all of the info needed by the caller (Nova) to actually attach the specified volume to the remote node. This method returns vital information to the caller that includes things like CHAP credential, iqn and lun information. An example response is shown here:

::
{‘driver_volume_type’: ‘iscsi’, ‘data’: {‘auth_password’: ‘YZ2Hceyh7VySh5HY’,
‘target_discovered’: False, ‘encrypted’: False, ‘qos_specs’: None, ‘target_iqn’: ‘iqn.2010-10.org.openstack:volume-8b1ec3fe-8c5 ‘target_portal’: ‘11.0.0.8:3260′, ‘volume_id’: ‘8b1ec3fe-8c57-45ca-a1cf-a481bfc8fce2′, ‘target_lun’: 1, ‘access_mode’: ‘rw’, ‘auth_username’: ‘nE9PY8juynmmZ95F7Xb7′, ‘auth_method’: ‘CHAP’}}``

In the process of building this data structure, the Cinder Volume Manager makes a number of calls to the backend driver, and builds a volume_attachment entry in the database to store the connection information passed in via the connector object.

driver.validate_connector

Simply verifies that the initiator data is included in the passed in connector (there are some drivers that utilize pieces of this connector data, but in the case of the reference, it just verifies it’s there).

driver.create_export

This is the target specific, persistent data associated with a volume. This method is responsible for building an actual iSCSI target, and providing the “location” and “auth” information which will be used to form the response data in the parent request. We call this infor the model_update and it’s used to update vital target information associated with the volume in the Cinder database.

driver.initialize_connection

Now that we’ve actually built a target and persisted the important bits of information associated with it, we’re ready to actually assign the target to a volume and form the needed info to pass back out to our caller. This is where we finally put everything together and form the example data structure response shown earlier.

This method is sort of deceptive, it does a whole lot of formatting of the data we’ve put together in the create_export call, but it doesn’t really offer any new info. It’s completely dependent on the information that was gathered in the create_export call and put into the database. At this point, all we’re doing is taking all the various entries from the database and putting it together into the desired format/structure.

The key method call for updating and obtaining all of this info was done by the create_export call. This formatted data is then passed back up to the API and returned as the response back out to Nova.

At this point, we return attach info to the caller that provides everything needed to make the remote iSCSI connection.

attach(self, context, volume, instance_uuid, host_name, mount_point, mode)

This is the last call that should be pretty simple. The intent is that this is simply used to finalize the attach process. In other words, we simply update the status on the Volume in the database, and provide a mechanism to notify the driver that the attachment has completed successfully.

There’s some additional information that has been added to this finalize call over time like instance_uuid, host_name etc. Some of these are only provided during the actual attach call and may be desired for some drivers for one reason or another.

Detach workflow

begin_detaching(self, context, volume)

Analogous to the Attach workflows reserve_volume method. Performs a simple conditional update of Volume status to detaching.

terminate_connection(self, context, volume, connector, force=False)

Analogous to the Attach workflows initialize_connection method.

Used to send calls down to drivers/target-drivers to do any sort of cleanup they might require.

For most this is a noop, as connections and iscsi session management is the responsibility of the initiator. HOWEVER, there are a number of special cases here, particularly for target-drivers like LIO that use access-groups, in those cases they remove the initiator from the access list during this call which effectively closes sessions from the target side.

detach(self, context, volume, attachment_id)

The final update to the DB and yet another opportunity to pass something down to the volume-driver. Initially a simple call-back that now has quite a bit of cruft built up in the volume-manager.

For drivers like LVM this again is a noop and just updates the db entry to mark things as complete and set the volume to available again.