Add create & attach times to SolidFire attributes.

This change simply adds create_time and attach_time to
the SolidFire devices attributes.

Times are taken from the volume-ref object, attach_time
is only set/present after an attach, and is also removed
from the attributes list on a detach.

This also required that we actually set attach_time on the
volume in the db.  We have the column, but we weren't actually
setting it, so this change required that be fixed.

In the future we should also look at changing the attach_time
from a string to a proper date-time object.

Change-Id: Ib9577ac160596a6878d1729f6022885b6cfa90e2
This commit is contained in:
John Griffith 2013-07-19 17:05:55 -06:00
parent 6e95e1ab3e
commit 5d8a457c4e
6 changed files with 110 additions and 24 deletions

View File

@ -1078,6 +1078,7 @@ def volume_detached(context, volume_id):
volume_ref['attach_status'] = 'detached' volume_ref['attach_status'] = 'detached'
volume_ref['instance_uuid'] = None volume_ref['instance_uuid'] = None
volume_ref['attached_host'] = None volume_ref['attached_host'] = None
volume_ref['attach_time'] = None
volume_ref.save(session=session) volume_ref.save(session=session)

View File

@ -19,6 +19,7 @@ import mox
from cinder import exception from cinder import exception
from cinder.openstack.common import log as logging from cinder.openstack.common import log as logging
from cinder.openstack.common import timeutils
from cinder import test from cinder import test
from cinder.volume import configuration as conf from cinder.volume import configuration as conf
from cinder.volume.drivers.solidfire import SolidFireDriver from cinder.volume.drivers.solidfire import SolidFireDriver
@ -143,7 +144,8 @@ class SolidFireVolumeTestCase(test.TestCase):
'name': 'testvol', 'name': 'testvol',
'size': 1, 'size': 1,
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66', 'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
'volume_type_id': 'fast'} 'volume_type_id': 'fast',
'created_at': timeutils.utcnow()}
sfv = SolidFireDriver(configuration=self.configuration) sfv = SolidFireDriver(configuration=self.configuration)
model_update = sfv.create_volume(testvol) model_update = sfv.create_volume(testvol)
@ -156,7 +158,9 @@ class SolidFireVolumeTestCase(test.TestCase):
'name': 'testvol', 'name': 'testvol',
'size': 1, 'size': 1,
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66', 'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
'volume_type_id': None} 'volume_type_id': None,
'created_at': timeutils.utcnow()}
sfv = SolidFireDriver(configuration=self.configuration) sfv = SolidFireDriver(configuration=self.configuration)
model_update = sfv.create_volume(testvol) model_update = sfv.create_volume(testvol)
self.assertNotEqual(model_update, None) self.assertNotEqual(model_update, None)
@ -169,7 +173,9 @@ class SolidFireVolumeTestCase(test.TestCase):
'name': 'testvol', 'name': 'testvol',
'size': 1, 'size': 1,
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66', 'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
'volume_type_id': None} 'volume_type_id': None,
'created_at': timeutils.utcnow()}
self.configuration.sf_emulate_512 = False self.configuration.sf_emulate_512 = False
sfv = SolidFireDriver(configuration=self.configuration) sfv = SolidFireDriver(configuration=self.configuration)
model_update = sfv.create_volume(testvol) model_update = sfv.create_volume(testvol)
@ -189,7 +195,8 @@ class SolidFireVolumeTestCase(test.TestCase):
'74-4cb7-bd55-14aed659a0cc.4060 0', '74-4cb7-bd55-14aed659a0cc.4060 0',
'provider_auth': 'CHAP stack-1-a60e2611875f40199931f2' 'provider_auth': 'CHAP stack-1-a60e2611875f40199931f2'
'c76370d66b 2FE0CQ8J196R', 'c76370d66b 2FE0CQ8J196R',
'provider_geometry': '4096 4096' 'provider_geometry': '4096 4096',
'created_at': timeutils.utcnow(),
} }
sfv = SolidFireDriver(configuration=self.configuration) sfv = SolidFireDriver(configuration=self.configuration)
@ -208,7 +215,8 @@ class SolidFireVolumeTestCase(test.TestCase):
'size': 1, 'size': 1,
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66', 'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
'metadata': [preset_qos], 'metadata': [preset_qos],
'volume_type_id': None} 'volume_type_id': None,
'created_at': timeutils.utcnow()}
sfv = SolidFireDriver(configuration=self.configuration) sfv = SolidFireDriver(configuration=self.configuration)
model_update = sfv.create_volume(testvol) model_update = sfv.create_volume(testvol)
@ -224,7 +232,8 @@ class SolidFireVolumeTestCase(test.TestCase):
testvol = {'project_id': 'testprjid', testvol = {'project_id': 'testprjid',
'name': 'testvol', 'name': 'testvol',
'size': 1, 'size': 1,
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66'} 'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
'created_at': timeutils.utcnow()}
sfv = SolidFireDriver(configuration=self.configuration) sfv = SolidFireDriver(configuration=self.configuration)
try: try:
sfv.create_volume(testvol) sfv.create_volume(testvol)
@ -266,7 +275,9 @@ class SolidFireVolumeTestCase(test.TestCase):
testvol = {'project_id': 'testprjid', testvol = {'project_id': 'testprjid',
'name': 'test_volume', 'name': 'test_volume',
'size': 1, 'size': 1,
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66'} 'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
'created_at': timeutils.utcnow()}
sfv = SolidFireDriver(configuration=self.configuration) sfv = SolidFireDriver(configuration=self.configuration)
sfv.delete_volume(testvol) sfv.delete_volume(testvol)
@ -276,7 +287,9 @@ class SolidFireVolumeTestCase(test.TestCase):
testvol = {'project_id': 'testprjid', testvol = {'project_id': 'testprjid',
'name': 'no-name', 'name': 'no-name',
'size': 1, 'size': 1,
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66'} 'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
'created_at': timeutils.utcnow()}
sfv = SolidFireDriver(configuration=self.configuration) sfv = SolidFireDriver(configuration=self.configuration)
try: try:
sfv.delete_volume(testvol) sfv.delete_volume(testvol)
@ -294,7 +307,9 @@ class SolidFireVolumeTestCase(test.TestCase):
testvol = {'project_id': 'testprjid', testvol = {'project_id': 'testprjid',
'name': 'no-name', 'name': 'no-name',
'size': 1, 'size': 1,
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66'} 'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
'created_at': timeutils.utcnow()}
sfv = SolidFireDriver(configuration=self.configuration) sfv = SolidFireDriver(configuration=self.configuration)
self.assertRaises(exception.SfAccountNotFound, self.assertRaises(exception.SfAccountNotFound,
sfv.delete_volume, sfv.delete_volume,
@ -323,7 +338,9 @@ class SolidFireVolumeTestCase(test.TestCase):
testvol = {'project_id': 'testprjid', testvol = {'project_id': 'testprjid',
'name': 'test_volume', 'name': 'test_volume',
'size': 1, 'size': 1,
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66'} 'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
'created_at': timeutils.utcnow()}
sfv = SolidFireDriver(configuration=self.configuration) sfv = SolidFireDriver(configuration=self.configuration)
sfv.extend_volume(testvol, 2) sfv.extend_volume(testvol, 2)
@ -349,7 +366,9 @@ class SolidFireVolumeTestCase(test.TestCase):
testvol = {'project_id': 'testprjid', testvol = {'project_id': 'testprjid',
'name': 'no-name', 'name': 'no-name',
'size': 1, 'size': 1,
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66'} 'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
'created_at': timeutils.utcnow()}
sfv = SolidFireDriver(configuration=self.configuration) sfv = SolidFireDriver(configuration=self.configuration)
self.assertRaises(exception.SfAccountNotFound, self.assertRaises(exception.SfAccountNotFound,
sfv.extend_volume, sfv.extend_volume,

View File

@ -167,12 +167,12 @@ class VolumeDriver(object):
"""Disallow connection from connector""" """Disallow connection from connector"""
raise NotImplementedError() raise NotImplementedError()
def attach_volume(self, context, volume_id, instance_uuid, host_name, def attach_volume(self, context, volume, instance_uuid, host_name,
mountpoint): mountpoint):
"""Callback for volume attached to instance or host.""" """Callback for volume attached to instance or host."""
pass pass
def detach_volume(self, context, volume_id): def detach_volume(self, context, volume):
"""Callback for volume detached.""" """Callback for volume detached."""
pass pass

View File

@ -210,7 +210,7 @@ class ScalityDriver(driver.VolumeDriver):
"""Disallow connection from connector.""" """Disallow connection from connector."""
pass pass
def detach_volume(self, context, volume_id): def detach_volume(self, context, volume):
"""Callback for volume detached.""" """Callback for volume detached."""
pass pass

View File

@ -30,6 +30,7 @@ from oslo.config import cfg
from cinder import context from cinder import context
from cinder import exception from cinder import exception
from cinder.openstack.common import log as logging from cinder.openstack.common import log as logging
from cinder.openstack.common import timeutils
from cinder.volume.drivers.san.san import SanISCSIDriver from cinder.volume.drivers.san.san import SanISCSIDriver
from cinder.volume import volume_types from cinder.volume import volume_types
@ -364,9 +365,11 @@ class SolidFireDriver(SanISCSIDriver):
# to set any that were provided # to set any that were provided
params = {'volumeID': sf_volume_id} params = {'volumeID': sf_volume_id}
create_time = timeutils.strtime(v_ref['created_at'])
attributes = {'uuid': v_ref['id'], attributes = {'uuid': v_ref['id'],
'is_clone': 'True', 'is_clone': 'True',
'src_uuid': src_uuid} 'src_uuid': src_uuid,
'created_at': create_time}
if qos: if qos:
params['qos'] = qos params['qos'] = qos
for k, v in qos.items(): for k, v in qos.items():
@ -484,8 +487,10 @@ class SolidFireDriver(SanISCSIDriver):
if type_id is not None: if type_id is not None:
qos = self._set_qos_by_volume_type(ctxt, type_id) qos = self._set_qos_by_volume_type(ctxt, type_id)
create_time = timeutils.strtime(volume['created_at'])
attributes = {'uuid': volume['id'], attributes = {'uuid': volume['id'],
'is_clone': 'False'} 'is_clone': 'False',
'created_at': create_time}
if qos: if qos:
for k, v in qos.items(): for k, v in qos.items():
attributes[k] = str(v) attributes[k] = str(v)
@ -662,3 +667,55 @@ class SolidFireDriver(SanISCSIDriver):
data['thin_provision_percent'] =\ data['thin_provision_percent'] =\
results['thinProvisioningPercent'] results['thinProvisioningPercent']
self.cluster_stats = data self.cluster_stats = data
def attach_volume(self, context, volume,
instance_uuid, host_name,
mountpoint):
LOG.debug(_("Entering SolidFire attach_volume..."))
sfaccount = self._get_sfaccount(volume['project_id'])
params = {'accountID': sfaccount['accountID']}
sf_vol = self._get_sf_volume(volume['id'], params)
if sf_vol is None:
LOG.error(_("Volume ID %s was not found on "
"the SolidFire Cluster!"), volume['id'])
raise exception.VolumeNotFound(volume_id=volume['id'])
attributes = sf_vol['attributes']
attributes['attach_time'] = volume.get('attach_time', None)
attributes['attached_to'] = instance_uuid
params = {
'volumeID': sf_vol['volumeID'],
'attributes': attributes
}
data = self._issue_api_request('ModifyVolume', params)
if 'result' not in data:
raise exception.SolidFireAPIDataException(data=data)
def detach_volume(self, context, volume):
LOG.debug(_("Entering SolidFire attach_volume..."))
sfaccount = self._get_sfaccount(volume['project_id'])
params = {'accountID': sfaccount['accountID']}
sf_vol = self._get_sf_volume(volume['id'], params)
if sf_vol is None:
LOG.error(_("Volume ID %s was not found on "
"the SolidFire Cluster!"), volume['id'])
raise exception.VolumeNotFound(volume_id=volume['id'])
attributes = sf_vol['attributes']
attributes['attach_time'] = None
attributes['attached_to'] = None
params = {
'volumeID': sf_vol['volumeID'],
'attributes': attributes
}
data = self._issue_api_request('ModifyVolume', params)
if 'result' not in data:
raise exception.SolidFireAPIDataException(data=data)

View File

@ -609,10 +609,16 @@ class VolumeManager(manager.SchedulerDependentManager):
elif volume['status'] != "available": elif volume['status'] != "available":
msg = _("status must be available") msg = _("status must be available")
raise exception.InvalidVolume(reason=msg) raise exception.InvalidVolume(reason=msg)
# TODO(jdg): attach_time column is currently varchar
# we should update this to a date-time object
# also consider adding detach_time?
now = timeutils.strtime()
self.db.volume_update(context, volume_id, self.db.volume_update(context, volume_id,
{"instance_uuid": instance_uuid, {"instance_uuid": instance_uuid,
"attached_host": host_name, "attached_host": host_name,
"status": "attaching"}) "status": "attaching",
"attach_time": now})
if instance_uuid and not uuidutils.is_uuid_like(instance_uuid): if instance_uuid and not uuidutils.is_uuid_like(instance_uuid):
self.db.volume_update(context, self.db.volume_update(context,
@ -623,9 +629,10 @@ class VolumeManager(manager.SchedulerDependentManager):
host_name_sanitized = utils.sanitize_hostname( host_name_sanitized = utils.sanitize_hostname(
host_name) if host_name else None host_name) if host_name else None
volume = self.db.volume_get(context, volume_id)
try: try:
self.driver.attach_volume(context, self.driver.attach_volume(context,
volume_id, volume,
instance_uuid, instance_uuid,
host_name_sanitized, host_name_sanitized,
mountpoint) mountpoint)
@ -646,8 +653,10 @@ class VolumeManager(manager.SchedulerDependentManager):
"""Updates db to show volume is detached""" """Updates db to show volume is detached"""
# TODO(vish): refactor this into a more general "unreserve" # TODO(vish): refactor this into a more general "unreserve"
# TODO(sleepsonthefloor): Is this 'elevated' appropriate? # TODO(sleepsonthefloor): Is this 'elevated' appropriate?
volume = self.db.volume_get(context, volume_id)
try: try:
self.driver.detach_volume(context, volume_id) self.driver.detach_volume(context, volume)
except Exception: except Exception:
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
self.db.volume_update(context, self.db.volume_update(context,
@ -657,10 +666,10 @@ class VolumeManager(manager.SchedulerDependentManager):
self.db.volume_detached(context.elevated(), volume_id) self.db.volume_detached(context.elevated(), volume_id)
# Check for https://bugs.launchpad.net/cinder/+bug/1065702 # Check for https://bugs.launchpad.net/cinder/+bug/1065702
volume_ref = self.db.volume_get(context, volume_id) volume = self.db.volume_get(context, volume_id)
if (volume_ref['provider_location'] and if (volume['provider_location'] and
volume_ref['name'] not in volume_ref['provider_location']): volume['name'] not in volume['provider_location']):
self.driver.ensure_export(context, volume_ref) self.driver.ensure_export(context, volume)
def _copy_image_to_volume(self, context, volume, image_service, image_id): def _copy_image_to_volume(self, context, volume, image_service, image_id):
"""Downloads Glance image to the specified volume.""" """Downloads Glance image to the specified volume."""