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:
parent
6e95e1ab3e
commit
5d8a457c4e
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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."""
|
||||||
|
|
Loading…
Reference in New Issue