From 03676baac5020bafedb3228f561734fb6d89dc8e Mon Sep 17 00:00:00 2001 From: Yu Zhang Date: Tue, 8 May 2018 05:09:30 -0400 Subject: [PATCH] IBM XIV: Report backend state in service list This patch implements reporting backend state in service list. Change-Id: Ife48a8c9b362099e89865fcf0b3beb333704a87c Implements: blueprint report-backend-state-in-service-list --- .../unit/volume/drivers/ibm/fake_pyxcli.py | 2 +- .../unit/volume/drivers/ibm/test_xiv_proxy.py | 41 +++++++++ .../drivers/ibm/ibm_storage/ibm_storage.py | 3 +- .../drivers/ibm/ibm_storage/xiv_proxy.py | 83 +++++++++++-------- ...tate-in-service-list-739a5398eec4a6b7.yaml | 5 ++ 5 files changed, 99 insertions(+), 35 deletions(-) create mode 100644 releasenotes/notes/report-backend-state-in-service-list-739a5398eec4a6b7.yaml diff --git a/cinder/tests/unit/volume/drivers/ibm/fake_pyxcli.py b/cinder/tests/unit/volume/drivers/ibm/fake_pyxcli.py index e826b259f13..9cf9cef806d 100644 --- a/cinder/tests/unit/volume/drivers/ibm/fake_pyxcli.py +++ b/cinder/tests/unit/volume/drivers/ibm/fake_pyxcli.py @@ -26,7 +26,7 @@ pyxcli_client.events = mock.Mock() pyxcli_client.mirroring = mock.Mock() pyxcli_client.transports = fake_pyxcli_exceptions pyxcli_client.mirroring.cg_recovery_manager = mock.Mock() -pyxcli_client.version = '1.1.5' +pyxcli_client.version = '1.1.6' pyxcli_client.mirroring.mirrored_entities = mock.Mock() sys.modules['pyxcli'] = pyxcli_client diff --git a/cinder/tests/unit/volume/drivers/ibm/test_xiv_proxy.py b/cinder/tests/unit/volume/drivers/ibm/test_xiv_proxy.py index 3152e8b0259..0fedd688e7e 100644 --- a/cinder/tests/unit/volume/drivers/ibm/test_xiv_proxy.py +++ b/cinder/tests/unit/volume/drivers/ibm/test_xiv_proxy.py @@ -126,6 +126,8 @@ REPLICA_PARAMS = { 'san_password': cryptish.encrypt(REPLICA_PASSWORD), 'san_clustername': REPLICA_POOL } +TEST_POOL = [ + {'name': 'WTF32', 'size': 10026, 'empty_space': 6925}] class XIVProxyTest(test.TestCase): @@ -189,6 +191,25 @@ class XIVProxyTest(test.TestCase): self.assertRaises(test_mock.cinder.exception.InvalidParameterValue, p.setup, {}) + @mock.patch("cinder.volume.drivers.ibm.ibm_storage.xiv_proxy.client." + "XCLIClient") + def test_setup_should_fail_if_pool_is_invalid(self, mock_xcli): + """Setup should raise exception if pool is invalid""" + driver = mock.MagicMock() + driver.VERSION = "VERSION" + + p = self.proxy( + self.default_storage_info, + mock.MagicMock(), + test_mock.cinder.exception, + driver) + + cmd = mock_xcli.connect_multiendpoint_ssl.return_value.cmd + cmd.pool_list.return_value.as_list = [] + + self.assertRaises(test_mock.cinder.exception.VolumeBackendAPIException, + p.setup, {}) + @mock.patch("cinder.volume.drivers.ibm.ibm_storage.xiv_proxy.client." "XCLIClient") @mock.patch("cinder.volume.drivers.ibm.ibm_storage.xiv_proxy.socket." @@ -338,6 +359,26 @@ class XIVProxyTest(test.TestCase): self.assertRaises(exception.VolumeBackendAPIException, p.setup, {}) + def test_get_volume_stats(self): + driver = mock.MagicMock() + driver.VERSION = "VERSION" + + p = self.proxy( + self.default_storage_info, + mock.MagicMock(), + test_mock.cinder.exception, + driver) + + p.ibm_storage_cli = mock.MagicMock() + + p.ibm_storage_cli.cmd.pool_list.return_value.as_list = TEST_POOL + stats = p.get_volume_stats() + self.assertEqual("up", stats['backend_state']) + + p.ibm_storage_cli.cmd.pool_list.return_value.as_list = None + stats = p.get_volume_stats(refresh=True) + self.assertEqual("down", stats['backend_state']) + def test_create_volume_should_call_xcli(self): """Create volume should call xcli with the correct parameters""" driver = mock.MagicMock() diff --git a/cinder/volume/drivers/ibm/ibm_storage/ibm_storage.py b/cinder/volume/drivers/ibm/ibm_storage/ibm_storage.py index b526577eef0..128ce5bb25a 100644 --- a/cinder/volume/drivers/ibm/ibm_storage/ibm_storage.py +++ b/cinder/volume/drivers/ibm/ibm_storage/ibm_storage.py @@ -77,9 +77,10 @@ class IBMStorageDriver(san.SanDriver, 2.1.0 - Support Consistency groups through Generic volume groups - Support XIV/A9000 Volume independent QoS - Support Consistency groups replication + 2.3.0 - Support Report backend state """ - VERSION = "2.1.0" + VERSION = "2.3.0" # ThirdPartySystems wiki page CI_WIKI_NAME = "IBM_STORAGE_CI" diff --git a/cinder/volume/drivers/ibm/ibm_storage/xiv_proxy.py b/cinder/volume/drivers/ibm/ibm_storage/xiv_proxy.py index 15545a3e8ac..0f7f192b120 100644 --- a/cinder/volume/drivers/ibm/ibm_storage/xiv_proxy.py +++ b/cinder/volume/drivers/ibm/ibm_storage/xiv_proxy.py @@ -57,7 +57,7 @@ SYNC = 'sync' ASYNC = 'async' SYNC_TIMEOUT = 300 SYNCHED_STATES = ['synchronized', 'rpo ok'] -PYXCLI_VERSION = '1.1.5' +PYXCLI_VERSION = '1.1.6' LOG = logging.getLogger(__name__) @@ -96,7 +96,7 @@ DELETE_VOLUME_BASE_ERROR = ("Unable to delete volume '%(volume)s': " MANAGE_VOLUME_BASE_ERROR = _("Unable to manage the volume '%(volume)s': " "%(error)s.") -INCOMPATIBLE_PYXCLI = _('Incompatible pyxcli found. Required: %(required)s ' +INCOMPATIBLE_PYXCLI = _('Incompatible pyxcli found. Mininum: %(required)s ' 'Found: %(found)s') @@ -104,8 +104,8 @@ class XIVProxy(proxy.IBMStorageProxy): """Proxy between the Cinder Volume and Spectrum Accelerate Storage. Supports IBM XIV, Spectrum Accelerate, A9000, A9000R - Version: 2.1.0 - Required pyxcli version: 1.1.5 + Version: 2.3.0 + Required pyxcli version: 1.1.6 .. code:: text @@ -113,6 +113,7 @@ class XIVProxy(proxy.IBMStorageProxy): 2.1.0 - Support Consistency groups through Generic volume groups - Support XIV/A9000 Volume independent QoS - Support groups replication + 2.3.0 - Support Report backend state """ @@ -140,7 +141,7 @@ class XIVProxy(proxy.IBMStorageProxy): def setup(self, context): msg = '' if pyxcli: - if pyxcli.version != PYXCLI_VERSION: + if pyxcli.version < PYXCLI_VERSION: msg = (INCOMPATIBLE_PYXCLI % {'required': PYXCLI_VERSION, 'found': pyxcli.version @@ -184,7 +185,7 @@ class XIVProxy(proxy.IBMStorageProxy): if remote_id: self.ibm_storage_remote_cli = self._init_xcli(remote_id) self._event_service_start() - self._update_stats() + self._get_pool() LOG.info("IBM Storage %(common_ver)s " "xiv_proxy %(proxy_ver)s. ", {'common_ver': self.full_version, @@ -1569,6 +1570,8 @@ class XIVProxy(proxy.IBMStorageProxy): if self.driver: backend_name = self.driver.configuration.safe_get( 'volume_backend_name') + self.meta['stat']['reserved_percentage'] = ( + self.driver.configuration.safe_get('reserved_percentage')) self.meta['stat']["volume_backend_name"] = ( backend_name or '%s_%s_%s_%s' % ( strings.XIV_BACKEND_PREFIX, @@ -1591,33 +1594,7 @@ class XIVProxy(proxy.IBMStorageProxy): 'pool': self.storage_info[storage.FLAG_KEYS['storage_pool']] })) - pools = self._call_xiv_xcli( - "pool_list", - pool=self.storage_info[storage.FLAG_KEYS['storage_pool']]).as_list - if len(pools) != 1: - LOG.error( - "_update_stats: Pool %(pool)s not available on storage", - {'pool': self.storage_info[storage.FLAG_KEYS['storage_pool']]}) - return - pool = pools[0] - - # handle different fields in pool_list between Gen3 and BR - soft_size = pool.get('soft_size') - if soft_size is None: - soft_size = pool.get('size') - hard_size = 0 - else: - hard_size = pool.hard_size - self.meta['stat']['total_capacity_gb'] = int(soft_size) - self.meta['stat']['free_capacity_gb'] = int( - pool.get('empty_space_soft', pool.get('empty_space'))) - self.meta['stat']['reserved_percentage'] = ( - self.driver.configuration.safe_get('reserved_percentage')) - self.meta['stat']['consistent_group_snapshot_enabled'] = True - - # thin/thick provision - self.meta['stat']['thin_provision'] = ('True' if soft_size > hard_size - else 'False') + self._retrieve_pool_stats(self.meta) if self.targets: self.meta['stat']['replication_enabled'] = True @@ -1632,6 +1609,46 @@ class XIVProxy(proxy.IBMStorageProxy): LOG.debug("Exiting XIVProxy::_update_stats: %(stat)s", {'stat': self.meta['stat']}) + @proxy._trace_time + def _get_pool(self): + pools = self._call_xiv_xcli( + "pool_list", pool=self.storage_info[ + storage.FLAG_KEYS['storage_pool']]).as_list + if not pools: + msg = (_( + "Pool %(pool)s not available on storage") % + {'pool': self.storage_info[storage.FLAG_KEYS['storage_pool']]}) + LOG.error(msg) + raise self.meta['exception'].VolumeBackendAPIException(data=msg) + return pools + + def _retrieve_pool_stats(self, data): + try: + pools = self._get_pool() + pool = pools[0] + data['stat']['pool_name'] = pool.get('name') + # handle different fields in pool_list between Gen3 and BR + soft_size = pool.get('soft_size') + if soft_size is None: + soft_size = pool.get('size') + hard_size = 0 + else: + hard_size = pool.hard_size + data['stat']['total_capacity_gb'] = int(soft_size) + data['stat']['free_capacity_gb'] = int( + pool.get('empty_space_soft', pool.get('empty_space'))) + # thin/thick provision + data['stat']['thin_provisioning_support'] = ( + 'True' if soft_size > hard_size else 'False') + data['stat']['backend_state'] = 'up' + except Exception as e: + data['stat']['total_capacity_gb'] = 0 + data['stat']['free_capacity_gb'] = 0 + data['stat']['thin_provision'] = False + data['stat']['backend_state'] = 'down' + error = self._get_code_and_status_or_message(e) + LOG.error(error) + @proxy._trace_time def create_cloned_volume(self, volume, src_vref): """Create cloned volume.""" diff --git a/releasenotes/notes/report-backend-state-in-service-list-739a5398eec4a6b7.yaml b/releasenotes/notes/report-backend-state-in-service-list-739a5398eec4a6b7.yaml new file mode 100644 index 00000000000..bcc28dee942 --- /dev/null +++ b/releasenotes/notes/report-backend-state-in-service-list-739a5398eec4a6b7.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Added flag 'backend_state: up/down' which will give backend state info in + service list. \ No newline at end of file