]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
3PAR: Adding performance metrics to volume status
authorJoseph Vokt <joseph.vokt@hp.com>
Fri, 10 Jul 2015 20:10:40 +0000 (13:10 -0700)
committerJoseph Vokt <joseph.vokt@hp.com>
Fri, 14 Aug 2015 16:54:57 +0000 (09:54 -0700)
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

cinder/tests/unit/test_hp3par.py
cinder/volume/drivers/san/hp/hp_3par_common.py

index de7e333f6ca32946bbc33bb12c28fee84224fd96..18434a3672ba4ce56ddf9d2f6a602f5524f510dc 100644 (file)
@@ -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
index 8c6aa730278f59663f0d57247fc4a85b2fd5e88c..84ec963f26dbda5c8ad68c10bcf0dc63828dbb1d 100644 (file)
@@ -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,