From: Joseph Vokt Date: Fri, 10 Jul 2015 20:10:40 +0000 (-0700) Subject: 3PAR: Adding performance metrics to volume status X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=a3f232e9a2dc03f0f7411447433e82e5f694fe64;p=openstack-build%2Fcinder-build.git 3PAR: Adding performance metrics to volume status If one storage backend has high load while another has lower load, when a user/admin creates a volume, the current evaluator/scheduler may place the volume on the backend with higher load. In many cases, the evaluator/scheduler should place that volume on the backend with the lowest load for load balancing purposes. In general, for load balancing virtual volume placement, an evaluator/scheduler needs access to performance metrics (e.g. IOPS throughput, bandwidth, latency, IO size) to measure load, but Cinder drivers don't generally report those metrics. Although these performance metrics are available from the 3PAR CLI, they are currently inaccesible in the evaluator/scheduler. The proposed changes add these metrics to the volume status report and thus make them accesible by the evaluator/scheduler. In cinder/volume/drivers/san/hp/hp_3par_common.py, uses the 3PAR client method HP3parClient.getCPGStatData to get performance metrics, and adds new fields in the output of HP3PARCommon.get_volume_stats corresponding to the performance metrics: IOPS throughput, bandwidth, latency, io_size, queue_length, and avg_busy_perc. These metrics are taken by averaging samples taken once per day for the last week. In cinder/tests/unit/test_hp3par.py, adds unit tests in TestHP3PARFCDriver.test_get_volume_stats and in TestHP3PARISCSIDriver.test_get_volume_stats. Closes-bug: #1482741 Change-Id: If9e7b5ffd100489af6537f7dab970d89a6d70851 --- diff --git a/cinder/tests/unit/test_hp3par.py b/cinder/tests/unit/test_hp3par.py index de7e333f6..18434a367 100644 --- a/cinder/tests/unit/test_hp3par.py +++ b/cinder/tests/unit/test_hp3par.py @@ -58,6 +58,19 @@ CHAP_PASS_KEY = "HPQ-cinder-CHAP-secret" FLASH_CACHE_ENABLED = 1 FLASH_CACHE_DISABLED = 2 +# Input/output (total read/write) operations per second. +THROUGHPUT = 'throughput' +# Data processed (total read/write) per unit time: kilobytes per second. +BANDWIDTH = 'bandwidth' +# Response time (total read/write): microseconds. +LATENCY = 'latency' +# IO size (total read/write): kilobytes. +IO_SIZE = 'io_size' +# Queue length for processing IO requests +QUEUE_LENGTH = 'queue_length' +# Average busy percentage +AVG_BUSY_PERC = 'avg_busy_perc' + class HP3PARBaseDriver(object): @@ -3407,7 +3420,8 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase): self.standard_logout) self.assertNotIn('initiator_target_map', conn_info['data']) - def test_get_volume_stats(self): + @mock.patch('hp3parclient.version', "3.2.2") + def test_get_volume_stats1(self): # setup_mock_client drive with the configuration # and return the mock HTTP 3PAR client config = self.setup_configuration() @@ -3425,6 +3439,16 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase): "rawFreeMiB": 1024.0 * 6, "usableFreeMiB": 1024.0 * 3 } + stat_capabilities = { + THROUGHPUT: 0, + BANDWIDTH: 0, + LATENCY: 0, + IO_SIZE: 0, + QUEUE_LENGTH: 0, + AVG_BUSY_PERC: 0 + } + + mock_client.getCPGStatData.return_value = stat_capabilities with mock.patch.object(hpcommon.HP3PARCommon, '_create_client') as mock_create_client: @@ -3444,12 +3468,26 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase): stats['pools'][0]['goodness_function']) self.assertEqual(FILTER_FUNCTION, stats['pools'][0]['filter_function']) + self.assertEqual(stat_capabilities[THROUGHPUT], + stats['pools'][0][THROUGHPUT]) + self.assertEqual(stat_capabilities[BANDWIDTH], + stats['pools'][0][BANDWIDTH]) + self.assertEqual(stat_capabilities[LATENCY], + stats['pools'][0][LATENCY]) + self.assertEqual(stat_capabilities[IO_SIZE], + stats['pools'][0][IO_SIZE]) + self.assertEqual(stat_capabilities[QUEUE_LENGTH], + stats['pools'][0][QUEUE_LENGTH]) + self.assertEqual(stat_capabilities[AVG_BUSY_PERC], + stats['pools'][0][AVG_BUSY_PERC]) expected = [ mock.call.getStorageSystemInfo(), mock.call.getCPG(HP3PAR_CPG), + mock.call.getCPGStatData(HP3PAR_CPG, 'daily', '7d'), mock.call.getCPGAvailableSpace(HP3PAR_CPG), mock.call.getCPG(HP3PAR_CPG2), + mock.call.getCPGStatData(HP3PAR_CPG2, 'daily', '7d'), mock.call.getCPGAvailableSpace(HP3PAR_CPG2)] mock_client.assert_has_calls( @@ -3468,6 +3506,18 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase): stats['pools'][0]['goodness_function']) self.assertEqual(FILTER_FUNCTION, stats['pools'][0]['filter_function']) + self.assertEqual(stat_capabilities[THROUGHPUT], + stats['pools'][0][THROUGHPUT]) + self.assertEqual(stat_capabilities[BANDWIDTH], + stats['pools'][0][BANDWIDTH]) + self.assertEqual(stat_capabilities[LATENCY], + stats['pools'][0][LATENCY]) + self.assertEqual(stat_capabilities[IO_SIZE], + stats['pools'][0][IO_SIZE]) + self.assertEqual(stat_capabilities[QUEUE_LENGTH], + stats['pools'][0][QUEUE_LENGTH]) + self.assertEqual(stat_capabilities[AVG_BUSY_PERC], + stats['pools'][0][AVG_BUSY_PERC]) cpg2 = self.cpgs[0].copy() cpg2.update({'SDGrowth': {'limitMiB': 8192}}) @@ -3494,9 +3544,136 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase): stats['pools'][0]['goodness_function']) self.assertEqual(FILTER_FUNCTION, stats['pools'][0]['filter_function']) + self.assertEqual(stat_capabilities[THROUGHPUT], + stats['pools'][0][THROUGHPUT]) + self.assertEqual(stat_capabilities[BANDWIDTH], + stats['pools'][0][BANDWIDTH]) + self.assertEqual(stat_capabilities[LATENCY], + stats['pools'][0][LATENCY]) + self.assertEqual(stat_capabilities[IO_SIZE], + stats['pools'][0][IO_SIZE]) + self.assertEqual(stat_capabilities[QUEUE_LENGTH], + stats['pools'][0][QUEUE_LENGTH]) + self.assertEqual(stat_capabilities[AVG_BUSY_PERC], + stats['pools'][0][AVG_BUSY_PERC]) common.client.deleteCPG(HP3PAR_CPG) common.client.createCPG(HP3PAR_CPG, {}) + @mock.patch('hp3parclient.version', "3.2.2") + def test_get_volume_stats2(self): + # Testing when the API_VERSION is incompatible with getCPGStatData + srstatld_api_version = 30201200 + pre_srstatld_api_version = srstatld_api_version - 1 + wsapi = {'build': pre_srstatld_api_version} + config = self.setup_configuration() + config.filter_function = FILTER_FUNCTION + config.goodness_function = GOODNESS_FUNCTION + mock_client = self.setup_driver(config=config, wsapi_version=wsapi) + mock_client.getCPG.return_value = self.cpgs[0] + mock_client.getStorageSystemInfo.return_value = { + 'serialNumber': '1234' + } + + # cpg has no limit + mock_client.getCPGAvailableSpace.return_value = { + "capacityEfficiency": {u'compaction': 594.4}, + "rawFreeMiB": 1024.0 * 6, + "usableFreeMiB": 1024.0 * 3 + } + + with mock.patch.object(hpcommon.HP3PARCommon, + '_create_client') as mock_create_client: + mock_create_client.return_value = mock_client + self.driver._login() + + stats = self.driver.get_volume_stats(True) + self.assertEqual('FC', stats['storage_protocol']) + self.assertEqual(0, stats['total_capacity_gb']) + self.assertEqual(0, stats['free_capacity_gb']) + self.assertEqual(24.0, stats['pools'][0]['total_capacity_gb']) + self.assertEqual(3.0, stats['pools'][0]['free_capacity_gb']) + self.assertEqual(87.5, stats['pools'][0]['capacity_utilization']) + self.assertEqual(3, stats['pools'][0]['total_volumes']) + self.assertEqual(GOODNESS_FUNCTION, + stats['pools'][0]['goodness_function']) + self.assertEqual(FILTER_FUNCTION, + stats['pools'][0]['filter_function']) + self.assertEqual(None, stats['pools'][0][THROUGHPUT]) + self.assertEqual(None, stats['pools'][0][BANDWIDTH]) + self.assertEqual(None, stats['pools'][0][LATENCY]) + self.assertEqual(None, stats['pools'][0][IO_SIZE]) + self.assertEqual(None, stats['pools'][0][QUEUE_LENGTH]) + self.assertEqual(None, stats['pools'][0][AVG_BUSY_PERC]) + + expected = [ + mock.call.getStorageSystemInfo(), + mock.call.getCPG(HP3PAR_CPG), + mock.call.getCPGAvailableSpace(HP3PAR_CPG), + mock.call.getCPG(HP3PAR_CPG2), + mock.call.getCPGAvailableSpace(HP3PAR_CPG2)] + + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) + + @mock.patch('hp3parclient.version', "3.2.1") + def test_get_volume_stats3(self): + # Testing when the client version is incompatible with getCPGStatData + # setup_mock_client drive with the configuration + # and return the mock HTTP 3PAR client + config = self.setup_configuration() + config.filter_function = FILTER_FUNCTION + config.goodness_function = GOODNESS_FUNCTION + mock_client = self.setup_driver(config=config) + mock_client.getCPG.return_value = self.cpgs[0] + mock_client.getStorageSystemInfo.return_value = { + 'serialNumber': '1234' + } + + # cpg has no limit + mock_client.getCPGAvailableSpace.return_value = { + "capacityEfficiency": {u'compaction': 594.4}, + "rawFreeMiB": 1024.0 * 6, + "usableFreeMiB": 1024.0 * 3 + } + + with mock.patch.object(hpcommon.HP3PARCommon, + '_create_client') as mock_create_client: + mock_create_client.return_value = mock_client + self.driver._login() + + stats = self.driver.get_volume_stats(True) + self.assertEqual('FC', stats['storage_protocol']) + self.assertEqual(0, stats['total_capacity_gb']) + self.assertEqual(0, stats['free_capacity_gb']) + self.assertEqual(24.0, stats['pools'][0]['total_capacity_gb']) + self.assertEqual(3.0, stats['pools'][0]['free_capacity_gb']) + self.assertEqual(87.5, stats['pools'][0]['capacity_utilization']) + self.assertEqual(3, stats['pools'][0]['total_volumes']) + self.assertEqual(GOODNESS_FUNCTION, + stats['pools'][0]['goodness_function']) + self.assertEqual(FILTER_FUNCTION, + stats['pools'][0]['filter_function']) + self.assertEqual(None, stats['pools'][0][THROUGHPUT]) + self.assertEqual(None, stats['pools'][0][BANDWIDTH]) + self.assertEqual(None, stats['pools'][0][LATENCY]) + self.assertEqual(None, stats['pools'][0][IO_SIZE]) + self.assertEqual(None, stats['pools'][0][QUEUE_LENGTH]) + self.assertEqual(None, stats['pools'][0][AVG_BUSY_PERC]) + + expected = [ + mock.call.getStorageSystemInfo(), + mock.call.getCPG(HP3PAR_CPG), + mock.call.getCPGAvailableSpace(HP3PAR_CPG), + mock.call.getCPG(HP3PAR_CPG2), + mock.call.getCPGAvailableSpace(HP3PAR_CPG2)] + + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) + def test_create_host(self): # setup_mock_client drive with default configuration # and return the mock HTTP 3PAR client @@ -3980,6 +4157,7 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase): expected_properties['data']['encrypted'] = True self.assertDictMatch(result, self.properties) + @mock.patch('hp3parclient.version', "3.2.2") def test_get_volume_stats(self): # setup_mock_client drive with the configuration # and return the mock HTTP 3PAR client @@ -3997,6 +4175,15 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase): "rawFreeMiB": 1024.0 * 6, "usableFreeMiB": 1024.0 * 3 } + stat_capabilities = { + THROUGHPUT: 0, + BANDWIDTH: 0, + LATENCY: 0, + IO_SIZE: 0, + QUEUE_LENGTH: 0, + AVG_BUSY_PERC: 0 + } + mock_client.getCPGStatData.return_value = stat_capabilities with mock.patch.object(hpcommon.HP3PARCommon, '_create_client') as mock_create_client: @@ -4015,12 +4202,26 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase): stats['pools'][0]['goodness_function']) self.assertEqual(FILTER_FUNCTION, stats['pools'][0]['filter_function']) + self.assertEqual(stat_capabilities[THROUGHPUT], + stats['pools'][0][THROUGHPUT]) + self.assertEqual(stat_capabilities[BANDWIDTH], + stats['pools'][0][BANDWIDTH]) + self.assertEqual(stat_capabilities[LATENCY], + stats['pools'][0][LATENCY]) + self.assertEqual(stat_capabilities[IO_SIZE], + stats['pools'][0][IO_SIZE]) + self.assertEqual(stat_capabilities[QUEUE_LENGTH], + stats['pools'][0][QUEUE_LENGTH]) + self.assertEqual(stat_capabilities[AVG_BUSY_PERC], + stats['pools'][0][AVG_BUSY_PERC]) expected = [ mock.call.getStorageSystemInfo(), mock.call.getCPG(HP3PAR_CPG), + mock.call.getCPGStatData(HP3PAR_CPG, 'daily', '7d'), mock.call.getCPGAvailableSpace(HP3PAR_CPG), mock.call.getCPG(HP3PAR_CPG2), + mock.call.getCPGStatData(HP3PAR_CPG2, 'daily', '7d'), mock.call.getCPGAvailableSpace(HP3PAR_CPG2)] mock_client.assert_has_calls( @@ -4053,6 +4254,133 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase): stats['pools'][0]['goodness_function']) self.assertEqual(FILTER_FUNCTION, stats['pools'][0]['filter_function']) + self.assertEqual(stat_capabilities[THROUGHPUT], + stats['pools'][0][THROUGHPUT]) + self.assertEqual(stat_capabilities[BANDWIDTH], + stats['pools'][0][BANDWIDTH]) + self.assertEqual(stat_capabilities[LATENCY], + stats['pools'][0][LATENCY]) + self.assertEqual(stat_capabilities[IO_SIZE], + stats['pools'][0][IO_SIZE]) + self.assertEqual(stat_capabilities[QUEUE_LENGTH], + stats['pools'][0][QUEUE_LENGTH]) + self.assertEqual(stat_capabilities[AVG_BUSY_PERC], + stats['pools'][0][AVG_BUSY_PERC]) + + @mock.patch('hp3parclient.version', "3.2.2") + def test_get_volume_stats2(self): + # Testing when the API_VERSION is incompatible with getCPGStatData + srstatld_api_version = 30201200 + pre_srstatld_api_version = srstatld_api_version - 1 + wsapi = {'build': pre_srstatld_api_version} + config = self.setup_configuration() + config.filter_function = FILTER_FUNCTION + config.goodness_function = GOODNESS_FUNCTION + mock_client = self.setup_driver(config=config, wsapi_version=wsapi) + mock_client.getCPG.return_value = self.cpgs[0] + mock_client.getStorageSystemInfo.return_value = { + 'serialNumber': '1234' + } + + # cpg has no limit + mock_client.getCPGAvailableSpace.return_value = { + "capacityEfficiency": {u'compaction': 594.4}, + "rawFreeMiB": 1024.0 * 6, + "usableFreeMiB": 1024.0 * 3 + } + + with mock.patch.object(hpcommon.HP3PARCommon, + '_create_client') as mock_create_client: + mock_create_client.return_value = mock_client + self.driver._login() + + stats = self.driver.get_volume_stats(True) + self.assertEqual('iSCSI', stats['storage_protocol']) + self.assertEqual(0, stats['total_capacity_gb']) + self.assertEqual(0, stats['free_capacity_gb']) + self.assertEqual(24.0, stats['pools'][0]['total_capacity_gb']) + self.assertEqual(3.0, stats['pools'][0]['free_capacity_gb']) + self.assertEqual(87.5, stats['pools'][0]['capacity_utilization']) + self.assertEqual(3, stats['pools'][0]['total_volumes']) + self.assertEqual(GOODNESS_FUNCTION, + stats['pools'][0]['goodness_function']) + self.assertEqual(FILTER_FUNCTION, + stats['pools'][0]['filter_function']) + self.assertEqual(None, stats['pools'][0][THROUGHPUT]) + self.assertEqual(None, stats['pools'][0][BANDWIDTH]) + self.assertEqual(None, stats['pools'][0][LATENCY]) + self.assertEqual(None, stats['pools'][0][IO_SIZE]) + self.assertEqual(None, stats['pools'][0][QUEUE_LENGTH]) + self.assertEqual(None, stats['pools'][0][AVG_BUSY_PERC]) + + expected = [ + mock.call.getStorageSystemInfo(), + mock.call.getCPG(HP3PAR_CPG), + mock.call.getCPGAvailableSpace(HP3PAR_CPG), + mock.call.getCPG(HP3PAR_CPG2), + mock.call.getCPGAvailableSpace(HP3PAR_CPG2)] + + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) + + @mock.patch('hp3parclient.version', "3.2.1") + def test_get_volume_stats3(self): + # Testing when the client version is incompatible with getCPGStatData + # setup_mock_client drive with the configuration + # and return the mock HTTP 3PAR client + config = self.setup_configuration() + config.filter_function = FILTER_FUNCTION + config.goodness_function = GOODNESS_FUNCTION + mock_client = self.setup_driver(config=config) + mock_client.getCPG.return_value = self.cpgs[0] + mock_client.getStorageSystemInfo.return_value = { + 'serialNumber': '1234' + } + + # cpg has no limit + mock_client.getCPGAvailableSpace.return_value = { + "capacityEfficiency": {u'compaction': 594.4}, + "rawFreeMiB": 1024.0 * 6, + "usableFreeMiB": 1024.0 * 3 + } + + with mock.patch.object(hpcommon.HP3PARCommon, + '_create_client') as mock_create_client: + mock_create_client.return_value = mock_client + self.driver._login() + + stats = self.driver.get_volume_stats(True) + self.assertEqual('iSCSI', stats['storage_protocol']) + self.assertEqual(0, stats['total_capacity_gb']) + self.assertEqual(0, stats['free_capacity_gb']) + self.assertEqual(24.0, stats['pools'][0]['total_capacity_gb']) + self.assertEqual(3.0, stats['pools'][0]['free_capacity_gb']) + self.assertEqual(87.5, stats['pools'][0]['capacity_utilization']) + self.assertEqual(3, stats['pools'][0]['total_volumes']) + self.assertEqual(GOODNESS_FUNCTION, + stats['pools'][0]['goodness_function']) + self.assertEqual(FILTER_FUNCTION, + stats['pools'][0]['filter_function']) + self.assertEqual(None, stats['pools'][0][THROUGHPUT]) + self.assertEqual(None, stats['pools'][0][BANDWIDTH]) + self.assertEqual(None, stats['pools'][0][LATENCY]) + self.assertEqual(None, stats['pools'][0][IO_SIZE]) + self.assertEqual(None, stats['pools'][0][QUEUE_LENGTH]) + self.assertEqual(None, stats['pools'][0][AVG_BUSY_PERC]) + + expected = [ + mock.call.getStorageSystemInfo(), + mock.call.getCPG(HP3PAR_CPG), + mock.call.getCPGAvailableSpace(HP3PAR_CPG), + mock.call.getCPG(HP3PAR_CPG2), + mock.call.getCPGAvailableSpace(HP3PAR_CPG2)] + + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) def test_create_host(self): # setup_mock_client drive with default configuration diff --git a/cinder/volume/drivers/san/hp/hp_3par_common.py b/cinder/volume/drivers/san/hp/hp_3par_common.py index 8c6aa7302..84ec963f2 100644 --- a/cinder/volume/drivers/san/hp/hp_3par_common.py +++ b/cinder/volume/drivers/san/hp/hp_3par_common.py @@ -71,8 +71,10 @@ from taskflow.patterns import linear_flow LOG = logging.getLogger(__name__) MIN_CLIENT_VERSION = '3.1.2' +GETCPGSTATDATA_VERSION = '3.2.2' DEDUP_API_VERSION = 30201120 FLASH_CACHE_API_VERSION = 30201200 +SRSTATLD_API_VERSION = 30201200 hp3par_opts = [ cfg.StrOpt('hp3par_api_url', @@ -116,6 +118,19 @@ hp3par_opts = [ CONF = cfg.CONF CONF.register_opts(hp3par_opts) +# Input/output (total read/write) operations per second. +THROUGHPUT = 'throughput' +# Data processed (total read/write) per unit time: kilobytes per second. +BANDWIDTH = 'bandwidth' +# Response time (total read/write): microseconds. +LATENCY = 'latency' +# IO size (total read/write): kilobytes. +IO_SIZE = 'io_size' +# Queue length for processing IO requests +QUEUE_LENGTH = 'queue_length' +# Average busy percentage +AVG_BUSY_PERC = 'avg_busy_perc' + class HP3PARCommon(object): """Class that contains common code for the 3PAR drivers. @@ -181,10 +196,11 @@ class HP3PARCommon(object): 2.0.46 - Improved VLUN creation and deletion logic. #1469816 2.0.47 - Changed initialize_connection to use getHostVLUNs. #1475064 2.0.48 - Adding changes to support 3PAR iSCSI multipath. + 2.0.49 - Added client CPG stats to driver volume stats. bug #1482741 """ - VERSION = "2.0.48" + VERSION = "2.0.49" stats = {} @@ -240,13 +256,21 @@ class HP3PARCommon(object): cl = client.HP3ParClient(self.config.hp3par_api_url) client_version = hp3parclient.version - if (client_version < MIN_CLIENT_VERSION): + if client_version < MIN_CLIENT_VERSION: ex_msg = (_('Invalid hp3parclient version found (%(found)s). ' 'Version %(minimum)s or greater required.') % {'found': client_version, 'minimum': MIN_CLIENT_VERSION}) LOG.error(ex_msg) raise exception.InvalidInput(reason=ex_msg) + if client_version < GETCPGSTATDATA_VERSION: + # getCPGStatData is only found in client version 3.2.2 or later + LOG.warning(_LW("getCPGStatData requires " + "hp3parclient version " + "'%(getcpgstatdata_version)s' " + "version '%(version)s' is installed.") % + {'getcpgstatdata_version': GETCPGSTATDATA_VERSION, + 'version': client_version}) return cl @@ -298,6 +322,13 @@ class HP3PARCommon(object): "rest_ver": hp3parclient.get_version_string()}) if self.config.hp3par_debug: self.client.debug_rest(True) + if self.API_VERSION < SRSTATLD_API_VERSION: + # Firmware version not compatible with srstatld + LOG.warning(_LW("srstatld requires " + "WSAPI version '%(srstatld_version)s' " + "version '%(version)s' is installed.") % + {'srstatld_version': SRSTATLD_API_VERSION, + 'version': self.API_VERSION}) def check_for_setup_error(self): self.client_login() @@ -703,6 +734,22 @@ class HP3PARCommon(object): for cpg_name in self.config.hp3par_cpg: try: cpg = self.client.getCPG(cpg_name) + if (self.API_VERSION >= SRSTATLD_API_VERSION + and hp3parclient.version >= GETCPGSTATDATA_VERSION): + interval = 'daily' + history = '7d' + stat_capabilities = self.client.getCPGStatData(cpg_name, + interval, + history) + else: + stat_capabilities = { + THROUGHPUT: None, + BANDWIDTH: None, + LATENCY: None, + IO_SIZE: None, + QUEUE_LENGTH: None, + AVG_BUSY_PERC: None + } if 'numTDVVs' in cpg: total_volumes = int( cpg['numFPVVs'] + cpg['numTPVVs'] + cpg['numTDVVs'] @@ -748,6 +795,12 @@ class HP3PARCommon(object): 'dest_cpg': cpg_name}), 'total_volumes': total_volumes, 'capacity_utilization': capacity_utilization, + THROUGHPUT: stat_capabilities[THROUGHPUT], + BANDWIDTH: stat_capabilities[BANDWIDTH], + LATENCY: stat_capabilities[LATENCY], + IO_SIZE: stat_capabilities[IO_SIZE], + QUEUE_LENGTH: stat_capabilities[QUEUE_LENGTH], + AVG_BUSY_PERC: stat_capabilities[AVG_BUSY_PERC], 'filter_function': filter_function, 'goodness_function': goodness_function, 'multiattach': True,