From 4446ba9920315ff57b8bd39bc2fdfaeec02d056b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=82awek=20Kap=C5=82o=C5=84ski?= Date: Thu, 6 Jul 2017 17:46:14 +0000 Subject: [PATCH] Add validation of required QoS extensions in Neutron Before operations on QoS policies shade can now check if required extensions (qos and qos-default) are available in cloud. Change-Id: I81727e25b72580d748757ead2c9a38708160d6c5 --- shade/openstackcloud.py | 28 ++++- shade/tests/unit/test_qos_policy.py | 174 ++++++++++++++++++++++++++++ 2 files changed, 196 insertions(+), 6 deletions(-) diff --git a/shade/openstackcloud.py b/shade/openstackcloud.py index 73325df04..e4dfb611c 100644 --- a/shade/openstackcloud.py +++ b/shade/openstackcloud.py @@ -1726,6 +1726,9 @@ class OpenStackCloud( :returns: A list of policies ``munch.Munch``. """ + if not self._has_neutron_extension('qos'): + raise OpenStackCloudUnavailableExtension( + 'QoS extension is not available on target cloud') # Translate None from search interface to empty {} for kwargs below if not filters: filters = {} @@ -3174,6 +3177,9 @@ class OpenStackCloud( :returns: The QoS policy object. :raises: OpenStackCloudException on operation error. """ + if not self._has_neutron_extension('qos'): + raise OpenStackCloudUnavailableExtension( + 'QoS extension is not available on target cloud') policy = {} if name: policy['name'] = name @@ -3181,10 +3187,12 @@ class OpenStackCloud( policy['description'] = description if shared is not None: policy['shared'] = shared - # TODO(slaweq): this should be used only if proper API extension is - # available in Neutron if default is not None: - policy['is_default'] = default + if self._has_neutron_extension('qos-default'): + policy['is_default'] = default + else: + self.log.debug("'qos-default' extension is not available on " + "target cloud") if project_id: policy['project_id'] = project_id @@ -3210,6 +3218,9 @@ class OpenStackCloud( :returns: The updated QoS policy object. :raises: OpenStackCloudException on operation error. """ + if not self._has_neutron_extension('qos'): + raise OpenStackCloudUnavailableExtension( + 'QoS extension is not available on target cloud') policy = {} if policy_name: policy['name'] = policy_name @@ -3217,10 +3228,12 @@ class OpenStackCloud( policy['description'] = description if shared is not None: policy['shared'] = shared - # TODO(slaweq): this should be used only if proper API extension is - # available in Neutron if default is not None: - policy['is_default'] = default + if self._has_neutron_extension('qos-default'): + policy['is_default'] = default + else: + self.log.debug("'qos-default' extension is not available on " + "target cloud") if not policy: self.log.debug("No QoS policy data to update") @@ -3246,6 +3259,9 @@ class OpenStackCloud( :raises: OpenStackCloudException on operation error. """ + if not self._has_neutron_extension('qos'): + raise OpenStackCloudUnavailableExtension( + 'QoS extension is not available on target cloud') policy = self.get_qos_policy(name_or_id) if not policy: self.log.debug("QoS policy %s not found for deleting", name_or_id) diff --git a/shade/tests/unit/test_qos_policy.py b/shade/tests/unit/test_qos_policy.py index c26f4e32e..de33b0fea 100644 --- a/shade/tests/unit/test_qos_policy.py +++ b/shade/tests/unit/test_qos_policy.py @@ -36,8 +36,30 @@ class TestQosPolicy(base.RequestsMockTestCase): 'is_default': False } + qos_extension = { + "updated": "2015-06-08T10:00:00-00:00", + "name": "Quality of Service", + "links": [], + "alias": "qos", + "description": "The Quality of Service extension." + } + + qos_default_extension = { + "updated": "2017-041-06T10:00:00-00:00", + "name": "QoS default policy", + "links": [], + "alias": "qos-default", + "description": "Expose the QoS default policy per project" + } + + enabled_neutron_extensions = [qos_extension, qos_default_extension] + def test_get_qos_policy(self): self.register_uris([ + dict(method='GET', + uri=self.get_mock_url( + 'network', 'public', append=['v2.0', 'extensions.json']), + json={'extensions': self.enabled_neutron_extensions}), dict(method='GET', uri=self.get_mock_url( 'network', 'public', @@ -49,8 +71,24 @@ class TestQosPolicy(base.RequestsMockTestCase): self.assertDictEqual(self.mock_policy, r) self.assert_calls() + def test_get_qos_policy_no_qos_extension(self): + self.register_uris([ + dict(method='GET', + uri=self.get_mock_url( + 'network', 'public', append=['v2.0', 'extensions.json']), + json={'extensions': []}) + ]) + self.assertRaises( + exc.OpenStackCloudException, + self.cloud.get_qos_policy, self.policy_name) + self.assert_calls() + def test_create_qos_policy(self): self.register_uris([ + dict(method='GET', + uri=self.get_mock_url( + 'network', 'public', append=['v2.0', 'extensions.json']), + json={'extensions': self.enabled_neutron_extensions}), dict(method='POST', uri=self.get_mock_url( 'network', 'public', @@ -62,8 +100,53 @@ class TestQosPolicy(base.RequestsMockTestCase): self.assertDictEqual(self.mock_policy, policy) self.assert_calls() + def test_create_qos_policy_no_qos_extension(self): + self.register_uris([ + dict(method='GET', + uri=self.get_mock_url( + 'network', 'public', append=['v2.0', 'extensions.json']), + json={'extensions': []}) + ]) + self.assertRaises( + exc.OpenStackCloudException, + self.cloud.create_qos_policy, name=self.policy_name) + self.assert_calls() + + def test_create_qos_policy_no_qos_default_extension(self): + self.register_uris([ + dict(method='GET', + uri=self.get_mock_url( + 'network', 'public', append=['v2.0', 'extensions.json']), + json={'extensions': [self.qos_extension]}), + dict(method='GET', + uri=self.get_mock_url( + 'network', 'public', append=['v2.0', 'extensions.json']), + json={'extensions': [self.qos_extension]}), + dict(method='POST', + uri=self.get_mock_url( + 'network', 'public', + append=['v2.0', 'qos', 'policies.json']), + json={'policy': self.mock_policy}, + validate=dict( + json={'policy': { + 'name': self.policy_name, + 'project_id': self.project_id}})) + ]) + policy = self.cloud.create_qos_policy( + name=self.policy_name, project_id=self.project_id, default=True) + self.assertDictEqual(self.mock_policy, policy) + self.assert_calls() + def test_delete_qos_policy(self): self.register_uris([ + dict(method='GET', + uri=self.get_mock_url( + 'network', 'public', append=['v2.0', 'extensions.json']), + json={'extensions': self.enabled_neutron_extensions}), + dict(method='GET', + uri=self.get_mock_url( + 'network', 'public', append=['v2.0', 'extensions.json']), + json={'extensions': self.enabled_neutron_extensions}), dict(method='GET', uri=self.get_mock_url( 'network', 'public', @@ -79,8 +162,28 @@ class TestQosPolicy(base.RequestsMockTestCase): self.assertTrue(self.cloud.delete_qos_policy(self.policy_name)) self.assert_calls() + def test_delete_qos_policy_no_qos_extension(self): + self.register_uris([ + dict(method='GET', + uri=self.get_mock_url( + 'network', 'public', append=['v2.0', 'extensions.json']), + json={'extensions': []}) + ]) + self.assertRaises( + exc.OpenStackCloudException, + self.cloud.delete_qos_policy, self.policy_name) + self.assert_calls() + def test_delete_qos_policy_not_found(self): self.register_uris([ + dict(method='GET', + uri=self.get_mock_url( + 'network', 'public', append=['v2.0', 'extensions.json']), + json={'extensions': self.enabled_neutron_extensions}), + dict(method='GET', + uri=self.get_mock_url( + 'network', 'public', append=['v2.0', 'extensions.json']), + json={'extensions': self.enabled_neutron_extensions}), dict(method='GET', uri=self.get_mock_url( 'network', 'public', @@ -94,6 +197,14 @@ class TestQosPolicy(base.RequestsMockTestCase): policy1 = dict(id='123', name=self.policy_name) policy2 = dict(id='456', name=self.policy_name) self.register_uris([ + dict(method='GET', + uri=self.get_mock_url( + 'network', 'public', append=['v2.0', 'extensions.json']), + json={'extensions': self.enabled_neutron_extensions}), + dict(method='GET', + uri=self.get_mock_url( + 'network', 'public', append=['v2.0', 'extensions.json']), + json={'extensions': self.enabled_neutron_extensions}), dict(method='GET', uri=self.get_mock_url( 'network', 'public', @@ -109,6 +220,14 @@ class TestQosPolicy(base.RequestsMockTestCase): policy1 = self.mock_policy policy2 = dict(id='456', name=self.policy_name) self.register_uris([ + dict(method='GET', + uri=self.get_mock_url( + 'network', 'public', append=['v2.0', 'extensions.json']), + json={'extensions': self.enabled_neutron_extensions}), + dict(method='GET', + uri=self.get_mock_url( + 'network', 'public', append=['v2.0', 'extensions.json']), + json={'extensions': self.enabled_neutron_extensions}), dict(method='GET', uri=self.get_mock_url( 'network', 'public', @@ -128,6 +247,14 @@ class TestQosPolicy(base.RequestsMockTestCase): expected_policy = copy.copy(self.mock_policy) expected_policy['name'] = 'goofy' self.register_uris([ + dict(method='GET', + uri=self.get_mock_url( + 'network', 'public', append=['v2.0', 'extensions.json']), + json={'extensions': self.enabled_neutron_extensions}), + dict(method='GET', + uri=self.get_mock_url( + 'network', 'public', append=['v2.0', 'extensions.json']), + json={'extensions': self.enabled_neutron_extensions}), dict(method='GET', uri=self.get_mock_url( 'network', 'public', @@ -146,3 +273,50 @@ class TestQosPolicy(base.RequestsMockTestCase): self.policy_id, policy_name='goofy') self.assertDictEqual(expected_policy, policy) self.assert_calls() + + def test_update_qos_policy_no_qos_extension(self): + self.register_uris([ + dict(method='GET', + uri=self.get_mock_url( + 'network', 'public', append=['v2.0', 'extensions.json']), + json={'extensions': []}) + ]) + self.assertRaises( + exc.OpenStackCloudException, + self.cloud.update_qos_policy, self.policy_id, policy_name="goofy") + self.assert_calls() + + def test_update_qos_policy_no_qos_default_extension(self): + expected_policy = copy.copy(self.mock_policy) + expected_policy['name'] = 'goofy' + self.register_uris([ + dict(method='GET', + uri=self.get_mock_url( + 'network', 'public', append=['v2.0', 'extensions.json']), + json={'extensions': [self.qos_extension]}), + dict(method='GET', + uri=self.get_mock_url( + 'network', 'public', append=['v2.0', 'extensions.json']), + json={'extensions': [self.qos_extension]}), + dict(method='GET', + uri=self.get_mock_url( + 'network', 'public', append=['v2.0', 'extensions.json']), + json={'extensions': [self.qos_extension]}), + dict(method='GET', + uri=self.get_mock_url( + 'network', 'public', + append=['v2.0', 'qos', 'policies.json']), + json={'policies': [self.mock_policy]}), + dict(method='PUT', + uri=self.get_mock_url( + 'network', 'public', + append=['v2.0', 'qos', 'policies', + '%s.json' % self.policy_id]), + json={'policy': expected_policy}, + validate=dict( + json={'policy': {'name': "goofy"}})) + ]) + policy = self.cloud.update_qos_policy( + self.policy_id, policy_name='goofy', default=True) + self.assertDictEqual(expected_policy, policy) + self.assert_calls()