Improve cluster extensions handlers

There handlers available under api/v1/clusters/{id}/extensions

* Re-work GET handler. Since now it's possible to enable extensions
  in more granular way which doesn't affect already enabled
  extensions.
* Add DELETE handler. Accepts comma separated list of extension
  names to disable via query string under 'extension_names' param.

Change-Id: Idd5680b489b4003bb2f58b5279a7257a128cc4e1
Related-Bug: #1614526
(back ported from commit d9f01a6472)
This commit is contained in:
Alexander Gordeev 2016-09-09 16:48:47 +03:00
parent df40b377c0
commit 21f2a940fe
6 changed files with 133 additions and 9 deletions

View File

@ -42,7 +42,8 @@ from nailgun.api.v1.validators.cluster import VmwareAttributesValidator
from nailgun.api.v1.validators.extension import ExtensionValidator
from nailgun import errors
from nailgun.extensions import set_extensions_for_object
from nailgun.extensions import remove_extensions_from_object
from nailgun.extensions import update_extensions_for_object
from nailgun.logger import logger
from nailgun import objects
@ -531,6 +532,32 @@ class ClusterExtensionsHandler(BaseHandler):
* 404 (cluster not found in db)
"""
cluster = self._get_cluster_obj(cluster_id)
data = set(self.checked_data())
set_extensions_for_object(cluster, data)
data = self.checked_data()
update_extensions_for_object(cluster, data)
return cluster.extensions
@handle_errors
@validate
def DELETE(self, cluster_id):
"""Disables the extensions for specified cluster
Takes (JSONed) list of extension names to disable.
:http: * 204 (OK)
* 400 (there is no such extension enabled)
* 404 (cluster not found in db)
"""
cluster = self._get_cluster_obj(cluster_id)
# TODO(agordeev): web.py does not support parsing of array arguments
# in the queryset so we specify the input as comma-separated list
extension_names = list(self.get_param_as_set('extension_names',
default=[]))
try:
data = self.validator.validate_delete(extension_names,
cluster)
except errors.CannotFindExtension as exc:
raise self.http(400, exc.message)
remove_extensions_from_object(cluster, data)
raise self.http(204)

View File

@ -35,4 +35,14 @@ class ExtensionValidator(BasicValidator):
"No such extensions: {0}".format(
", ".join(sorted(not_found_extensions))))
return data
return list(data)
@classmethod
def validate_delete(cls, extension_names, cluster):
not_found_extensions = set(extension_names) - set(cluster.extensions)
if not_found_extensions:
raise errors.CannotFindExtension(
"No such extensions to disable: {0}".format(
", ".join(sorted(not_found_extensions))))
return list(extension_names)

View File

@ -33,5 +33,7 @@ from nailgun.extensions.manager import \
fire_callback_on_cluster_serialization_for_provisioning
from nailgun.extensions.manager import \
fire_callback_on_node_serialization_for_provisioning
from nailgun.extensions.manager import set_extensions_for_object
from nailgun.extensions.manager import node_extension_call
from nailgun.extensions.manager import remove_extensions_from_object
from nailgun.extensions.manager import setup_yaql_context
from nailgun.extensions.manager import update_extensions_for_object

View File

@ -60,8 +60,13 @@ def get_extension(name):
"Cannot find extension with name '{0}'".format(name))
def set_extensions_for_object(obj, extensions_names):
obj.extensions = extensions_names
def remove_extensions_from_object(obj, extensions_names):
obj.extensions = list(set(obj.extensions) - set(extensions_names))
db().flush()
def update_extensions_for_object(obj, extensions_names):
obj.extensions = list(set(obj.extensions + extensions_names))
db().flush()

View File

@ -425,7 +425,8 @@ class TestClusterExtension(BaseIntegrationTest):
self.assertEqual(resp.status_code, 200)
self.db.refresh(self.cluster)
self.assertItemsEqual(self.cluster.extensions, extensions)
for ext in extensions:
self.assertIn(ext, self.cluster.extensions)
def test_enabling_extensions(self):
extensions = 'bareon', 'volume_manager'
@ -443,7 +444,8 @@ class TestClusterExtension(BaseIntegrationTest):
self.assertEqual(resp.status_code, 200)
self.db.refresh(self.cluster)
self.assertItemsEqual(self.cluster.extensions, extensions)
for ext in extensions:
self.assertIn(ext, self.cluster.extensions)
def test_enabling_invalid_extensions(self):
existed_extensions = 'bareon', 'volume_manager'
@ -464,3 +466,64 @@ class TestClusterExtension(BaseIntegrationTest):
self.assertIn(u"No such extensions:", resp.json_body['message'])
self.assertIn(requested_extensions[0], resp.json_body['message'])
self.assertNotIn(requested_extensions[1], resp.json_body['message'])
def test_disabling_invalid_extensions(self):
requested_extensions = 'network_manager', 'bareon'
self.cluster.extensions = 'volume_manager', 'bareon'
self.db.commit()
url = reverse('ClusterExtensionsHandler',
kwargs={'cluster_id': self.cluster.id})
query_str = 'extension_names={0}'.format(
','.join(requested_extensions))
resp = self.app.delete(
'{0}?{1}'.format(url, query_str),
headers=self.default_headers,
expect_errors=True,
)
self.assertEqual(resp.status_code, 400)
self.assertIn(u"No such extensions to disable:",
resp.json_body['message'])
self.assertIn(requested_extensions[0], resp.json_body['message'])
self.assertNotIn(requested_extensions[1], resp.json_body['message'])
def test_disabling_extensions(self):
existed_extensions = 'network_manager', 'volume_manager', 'bareon'
requested_extensions = 'network_manager', 'bareon'
self.cluster.extensions = existed_extensions
self.db.commit()
url = reverse('ClusterExtensionsHandler',
kwargs={'cluster_id': self.cluster.id})
query_str = 'extension_names={0}'.format(
','.join(requested_extensions))
self.app.delete(
'{0}?{1}'.format(url, query_str),
headers=self.default_headers,
)
self.db.refresh(self.cluster)
for ext in requested_extensions:
self.assertNotIn(ext, self.cluster.extensions)
def test_disabling_dublicated_extensions(self):
existed_extensions = 'network_manager', 'volume_manager', 'bareon'
requested_extensions = 'network_manager', 'bareon'
self.cluster.extensions = existed_extensions
self.db.commit()
url = reverse('ClusterExtensionsHandler',
kwargs={'cluster_id': self.cluster.id})
query_str = 'extension_names={0}'.format(
','.join(2 * requested_extensions))
self.app.delete(
'{0}?{1}'.format(url, query_str),
headers=self.default_headers,
)
self.db.refresh(self.cluster)
for ext in requested_extensions:
self.assertNotIn(ext, self.cluster.extensions)

View File

@ -242,6 +242,23 @@ class TestPipeline(BaseExtensionCase):
class TestExtensionValidator(BaseTestCase):
def test_validate_delete_extensions(self):
global_exts = 'volume_manager', 'bareon', 'ultralogger'
cluster = mock.Mock()
cluster.extensions = global_exts
ExtensionValidator.validate_delete(global_exts[:2], cluster)
def test_invalid_delete_extensions(self):
global_exts = 'volume_manager', 'bareon', 'ultralogger'
data = 'volume_manager', 'bareon', 'invalid'
cluster = mock.Mock()
cluster.extensions = global_exts
with self.assertRaisesRegexp(errors.CannotFindExtension,
'No such extensions to disable: invalid'):
ExtensionValidator.validate_delete(data, cluster)
def test_validate_extensions(self):
global_exts = 'volume_manager', 'bareon', 'ultralogger'