From bb923d5eedc922fbfa5cbe755b46a334c5ed9664 Mon Sep 17 00:00:00 2001 From: john-griffith Date: Sat, 16 Feb 2013 09:58:46 -0700 Subject: [PATCH] Add get_cluster_stats to SolidFire driver With the filter scheduler addition we added get_stats to the drivers. This provides the required data for the filtering mechanisms and is called by the scheduler. The change adds the required call in the driver and also adds an update routine to it's init to populate this info on startup. This also adds the fake api response so that the tests can perform the init routine and get the data, while giving the added bonus of testing the new functions at the same time. Change-Id: Ia75e078f8e73883131cb1e68f6e6713fce1ab518 --- cinder/tests/test_drivers_compatibility.py | 8 +++ cinder/tests/test_solidfire.py | 37 ++++++++++++-- cinder/volume/drivers/solidfire.py | 59 +++++++++++++++++++++- 3 files changed, 99 insertions(+), 5 deletions(-) diff --git a/cinder/tests/test_drivers_compatibility.py b/cinder/tests/test_drivers_compatibility.py index 07cdf7ff5..f69eb65ab 100644 --- a/cinder/tests/test_drivers_compatibility.py +++ b/cinder/tests/test_drivers_compatibility.py @@ -16,6 +16,7 @@ from cinder import context from cinder import flags from cinder.openstack.common import importutils from cinder import test +from cinder.volume.drivers.solidfire import SolidFire FLAGS = flags.FLAGS @@ -40,6 +41,9 @@ ZADARA_MODULE = "cinder.volume.drivers.zadara.ZadaraVPSAISCSIDriver" class VolumeDriverCompatibility(test.TestCase): """Test backwards compatibility for volume drivers.""" + def fake_update_cluster_status(self): + return + def setUp(self): super(VolumeDriverCompatibility, self).setUp() self.manager = importutils.import_object(FLAGS.volume_manager) @@ -49,6 +53,10 @@ class VolumeDriverCompatibility(test.TestCase): super(VolumeDriverCompatibility, self).tearDown() def _load_driver(self, driver): + if 'SolidFire' in driver: + # SolidFire driver does update_cluster stat on init + self.stubs.Set(SolidFire, '_update_cluster_status', + self.fake_update_cluster_status) self.manager.__init__(volume_driver=driver) def _driver_module_name(self): diff --git a/cinder/tests/test_solidfire.py b/cinder/tests/test_solidfire.py index b1ded96e3..6a6c0f19e 100644 --- a/cinder/tests/test_solidfire.py +++ b/cinder/tests/test_solidfire.py @@ -26,8 +26,21 @@ LOG = logging.getLogger(__name__) class SolidFireVolumeTestCase(test.TestCase): def setUp(self): super(SolidFireVolumeTestCase, self).setUp() + self.stubs.Set(SolidFire, '_issue_api_request', + self.fake_issue_api_request) def fake_issue_api_request(obj, method, params): + if method is 'GetClusterCapacity': + LOG.info('Called Fake GetClusterCapacity...') + data = {} + data = {'result': + {'clusterCapacity': {'maxProvisionedSpace': 99999999, + 'usedSpace': 999, + 'compressionPercent': 100, + 'deDuplicationPercent': 100, + 'thinProvisioningPercent': 100}}} + return data + if method is 'GetClusterInfo': LOG.info('Called Fake GetClusterInfo...') results = {'result': {'clusterInfo': @@ -76,7 +89,7 @@ class SolidFireVolumeTestCase(test.TestCase): 'enable512e': True, 'access': "readWrite", 'status': "active", - 'attributes': None, + 'attributes':None, 'qos': None, 'iqn': test_name}]}} return result @@ -98,6 +111,9 @@ class SolidFireVolumeTestCase(test.TestCase): def fake_volume_get(obj, key, default=None): return {'qos': 'fast'} + def fake_update_cluster_status(self): + return + def test_create_with_qos_type(self): self.stubs.Set(SolidFire, '_issue_api_request', self.fake_issue_api_request) @@ -110,6 +126,7 @@ class SolidFireVolumeTestCase(test.TestCase): 'volume_type_id': 'fast'} sfv = SolidFire() model_update = sfv.create_volume(testvol) + self.assertNotEqual(model_update, None) def test_create_volume(self): self.stubs.Set(SolidFire, '_issue_api_request', @@ -121,6 +138,7 @@ class SolidFireVolumeTestCase(test.TestCase): 'volume_type_id': None} sfv = SolidFire() model_update = sfv.create_volume(testvol) + self.assertNotEqual(model_update, None) def test_create_volume_with_qos(self): preset_qos = {} @@ -137,8 +155,13 @@ class SolidFireVolumeTestCase(test.TestCase): sfv = SolidFire() model_update = sfv.create_volume(testvol) + self.assertNotEqual(model_update, None) def test_create_volume_fails(self): + # NOTE(JDG) This test just fakes update_cluster_status + # this is inentional for this test + self.stubs.Set(SolidFire, '_update_cluster_status', + self.fake_update_cluster_status) self.stubs.Set(SolidFire, '_issue_api_request', self.fake_issue_api_request_fails) testvol = {'project_id': 'testprjid', @@ -188,7 +211,7 @@ class SolidFireVolumeTestCase(test.TestCase): 'size': 1, 'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66'} sfv = SolidFire() - model_update = sfv.delete_volume(testvol) + sfv.delete_volume(testvol) def test_delete_volume_fails_no_volume(self): self.stubs.Set(SolidFire, '_issue_api_request', @@ -199,12 +222,16 @@ class SolidFireVolumeTestCase(test.TestCase): 'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66'} sfv = SolidFire() try: - model_update = sfv.delete_volume(testvol) + sfv.delete_volume(testvol) self.fail("Should have thrown Error") except Exception: pass def test_delete_volume_fails_account_lookup(self): + # NOTE(JDG) This test just fakes update_cluster_status + # this is inentional for this test + self.stubs.Set(SolidFire, '_update_cluster_status', + self.fake_update_cluster_status) self.stubs.Set(SolidFire, '_issue_api_request', self.fake_issue_api_request_fails) testvol = {'project_id': 'testprjid', @@ -223,6 +250,10 @@ class SolidFireVolumeTestCase(test.TestCase): sfv._get_cluster_info() def test_get_cluster_info_fail(self): + # NOTE(JDG) This test just fakes update_cluster_status + # this is inentional for this test + self.stubs.Set(SolidFire, '_update_cluster_status', + self.fake_update_cluster_status) self.stubs.Set(SolidFire, '_issue_api_request', self.fake_issue_api_request_fails) sfv = SolidFire() diff --git a/cinder/volume/drivers/solidfire.py b/cinder/volume/drivers/solidfire.py index 800ac2413..fa4dce238 100644 --- a/cinder/volume/drivers/solidfire.py +++ b/cinder/volume/drivers/solidfire.py @@ -33,7 +33,7 @@ from cinder.openstack.common import log as logging from cinder.volume.drivers.san.san import SanISCSIDriver from cinder.volume import volume_types -VERSION = 1.1 +VERSION = 1.2 LOG = logging.getLogger(__name__) sf_opts = [ @@ -73,11 +73,13 @@ class SolidFire(SanISCSIDriver): 'off': None} sf_qos_keys = ['minIOPS', 'maxIOPS', 'burstIOPS'] + cluster_stats = {} GB = math.pow(10, 9) def __init__(self, *args, **kwargs): super(SolidFire, self).__init__(*args, **kwargs) + self._update_cluster_status() def _issue_api_request(self, method_name, params): """All API requests to SolidFire device go through this method. @@ -282,6 +284,9 @@ class SolidFire(SanISCSIDriver): 'is_clone': 'True', 'src_uuid': 'src_uuid'} + if qos: + attributes['qos'] = qos + params = {'volumeID': int(sf_vol['volumeID']), 'name': 'UUID-%s' % v_ref['id'], 'attributes': attributes, @@ -328,7 +333,7 @@ class SolidFire(SanISCSIDriver): qos[i.key] = int(i.value) return qos - def _set_qos_by_volume_type(self, ctxt, type_id): + def _set_qos_by_volume_type(self, type_id, ctxt): qos = {} volume_type = volume_types.get_volume_type(ctxt, type_id) specs = volume_type.get('extra_specs') @@ -396,6 +401,8 @@ class SolidFire(SanISCSIDriver): attributes = {'uuid': volume['id'], 'is_clone': 'False'} + if qos: + attributes['qos'] = qos params = {'name': 'UUID-%s' % volume['id'], 'accountID': None, @@ -482,3 +489,51 @@ class SolidFire(SanISCSIDriver): volume) return model + + def get_volume_stats(self, refresh=False): + """Get volume status. + + If 'refresh' is True, run update first. + The name is a bit misleading as + the majority of the data here is cluster + data + """ + if refresh: + self._update_cluster_status() + + return self.cluster_stats + + def _update_cluster_status(self): + """Retrieve status info for the Cluster.""" + + LOG.debug(_("Updating cluster status info")) + + params = {} + + # NOTE(jdg): The SF api provides an UNBELIEVABLE amount + # of stats data, this is just one of the calls + results = self._issue_api_request('GetClusterCapacity', params) + if 'result' not in results: + LOG.error(_('Failed to get updated stats')) + + results = results['result']['clusterCapacity'] + free_capacity =\ + results['maxProvisionedSpace'] - results['usedSpace'] + + data = {} + data["volume_backend_name"] = self.__class__.__name__ + data["vendor_name"] = 'SolidFire Inc' + data["driver_version"] = '1.2' + data["storage_protocol"] = 'iSCSI' + + data['total_capacity_gb'] = results['maxProvisionedSpace'] + data['free_capacity_gb'] = free_capacity + data['reserved_percentage'] = FLAGS.reserved_percentage + data['QoS_support'] = True + data['compression_percent'] =\ + results['compressionPercent'] + data['deduplicaton_percent'] =\ + results['deDuplicationPercent'] + data['thin_provision_percent'] =\ + results['thinProvisioningPercent'] + self.cluster_stats = data -- 2.45.2