From 068028cbe72cf9081925bf394d3289787a08b7f7 Mon Sep 17 00:00:00 2001 From: Tim Laszlo Date: Tue, 3 May 2016 15:20:02 -0500 Subject: [PATCH] Add support for server groups This adds support to create and delete server groups. Change-Id: Iecf0ffabe50801ec9bd2bb9ea2dbb17ead4d7cd2 --- ...server_group_support-dfa472e3dae7d34d.yaml | 3 + shade/_tasks.py | 20 +++++ shade/openstackcloud.py | 80 +++++++++++++++++++ shade/tests/fakes.py | 7 ++ shade/tests/functional/test_server_group.py | 45 +++++++++++ shade/tests/unit/test_server_group.py | 44 ++++++++++ 6 files changed, 199 insertions(+) create mode 100644 releasenotes/notes/add_server_group_support-dfa472e3dae7d34d.yaml create mode 100644 shade/tests/functional/test_server_group.py create mode 100644 shade/tests/unit/test_server_group.py diff --git a/releasenotes/notes/add_server_group_support-dfa472e3dae7d34d.yaml b/releasenotes/notes/add_server_group_support-dfa472e3dae7d34d.yaml new file mode 100644 index 000000000..e90384134 --- /dev/null +++ b/releasenotes/notes/add_server_group_support-dfa472e3dae7d34d.yaml @@ -0,0 +1,3 @@ +--- +features: + - Adds support to create and delete server groups. diff --git a/shade/_tasks.py b/shade/_tasks.py index 8c2e10821..6265dff69 100644 --- a/shade/_tasks.py +++ b/shade/_tasks.py @@ -172,6 +172,26 @@ class ServerRebuild(task_manager.Task): return client.nova_client.servers.rebuild(**self.args) +class ServerGroupList(task_manager.Task): + def main(self, client): + return client.nova_client.server_groups.list(**self.args) + + +class ServerGroupGet(task_manager.Task): + def main(self, client): + return client.nova_client.server_groups.get(**self.args) + + +class ServerGroupCreate(task_manager.Task): + def main(self, client): + return client.nova_client.server_groups.create(**self.args) + + +class ServerGroupDelete(task_manager.Task): + def main(self, client): + return client.nova_client.server_groups.delete(**self.args) + + class HypervisorList(task_manager.Task): def main(self, client): return client.nova_client.hypervisors.list(**self.args) diff --git a/shade/openstackcloud.py b/shade/openstackcloud.py index f86d4d141..075dd63a6 100644 --- a/shade/openstackcloud.py +++ b/shade/openstackcloud.py @@ -1212,6 +1212,20 @@ class OpenStackCloud(object): servers = self.list_servers(detailed=detailed) return _utils._filter_list(servers, name_or_id, filters) + def search_server_groups(self, name_or_id=None, filters=None): + """Seach server groups. + + :param name: server group name or id. + :param filters: a dict containing additional filters to use. + + :returns: a list of dicts containing the server groups + + :raises: ``OpenStackCloudException``: if something goes wrong during + the openstack API call. + """ + server_groups = self.list_server_groups() + return _utils._filter_list(server_groups, name_or_id, filters) + def search_images(self, name_or_id=None, filters=None): images = self.list_images() return _utils._filter_list(images, name_or_id, filters) @@ -1461,6 +1475,15 @@ class OpenStackCloud(object): for server in servers ] + def list_server_groups(self): + """List all available server groups. + + :returns: A list of server group dicts. + + """ + with _utils.shade_exceptions("Error fetching server group list"): + return self.manager.submitTask(_tasks.ServerGroupList()) + @_utils.cache_on_arguments(should_cache_fn=_no_pending_images) def list_images(self, filter_deleted=True): """Get available glance images. @@ -1933,6 +1956,25 @@ class OpenStackCloud(object): self.manager.submitTask(_tasks.ServerGet(server=id)), cloud_name=self.name, region_name=self.region_name)) + def get_server_group(self, name_or_id=None, filters=None): + """Get a server group by name or ID. + + :param name_or_id: Name or ID of the server group. + :param dict filters: + A dictionary of meta data to use for further filtering. Elements + of this dictionary may, themselves, be dictionaries. Example:: + + { + 'policy': 'affinity', + } + + :returns: A server groups dict or None if no matching server group + is found. + + """ + return _utils._get_entity(self.search_server_groups, name_or_id, + filters) + def get_image(self, name_or_id, filters=None): """Get an image by name or ID. @@ -4352,6 +4394,44 @@ class OpenStackCloud(object): self._servers_time = self._servers_time - self._SERVER_AGE return True + def create_server_group(self, name, policies): + """Create a new server group. + + :param name: Name of the server group being created + :param policies: List of policies for the server group. + + :returns: a dict representing the new server group. + + :raises: OpenStackCloudException on operation error. + """ + with _utils.shade_exceptions( + "Unable to create server group {name}".format( + name=name)): + return self.manager.submitTask(_tasks.ServerGroupCreate( + name=name, policies=policies)) + + def delete_server_group(self, name_or_id): + """Delete a server group. + + :param name_or_id: Name or id of the server group to delete + + :returns: True if delete succeeded, False otherwise + + :raises: OpenStackCloudException on operation error. + """ + server_group = self.get_server_group(name_or_id) + if not server_group: + self.log.debug("Server group %s not found for deleting" % + name_or_id) + return False + + with _utils.shade_exceptions( + "Error deleting server group {name}".format(name=name_or_id)): + self.manager.submitTask( + _tasks.ServerGroupDelete(id=server_group['id'])) + + return True + def list_containers(self, full_listing=True): try: return self.manager.submitTask(_tasks.ContainerList( diff --git a/shade/tests/fakes.py b/shade/tests/fakes.py index 01cb822fb..580face58 100644 --- a/shade/tests/fakes.py +++ b/shade/tests/fakes.py @@ -114,6 +114,13 @@ class FakeServer(object): self.interface_ip = interface_ip +class FakeServerGroup(object): + def __init__(self, id, name, policies): + self.id = id + self.name = name + self.policies = policies + + class FakeService(object): def __init__(self, id, name, type, service_type, description='', enabled=True): diff --git a/shade/tests/functional/test_server_group.py b/shade/tests/functional/test_server_group.py new file mode 100644 index 000000000..e300e331a --- /dev/null +++ b/shade/tests/functional/test_server_group.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- + +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +test_server_group +---------------------------------- + +Functional tests for `shade` server_group resource. +""" + +from shade.tests.functional import base + + +class TestServerGroup(base.BaseFunctionalTestCase): + + def setUp(self): + super(TestServerGroup, self).setUp() + + def test_server_group(self): + server_group_name = self.getUniqueString() + self.addCleanup(self.cleanup, server_group_name) + server_group = self.demo_cloud.create_server_group( + server_group_name, ['affinity']) + + server_group_ids = [v['id'] + for v in self.demo_cloud.list_server_groups()] + self.assertIn(server_group['id'], server_group_ids) + + self.demo_cloud.delete_server_group(server_group_name) + + def cleanup(self, server_group_name): + server_group = self.demo_cloud.get_server_group(server_group_name) + if server_group: + self.demo_cloud.delete_server_group(server_group['id']) diff --git a/shade/tests/unit/test_server_group.py b/shade/tests/unit/test_server_group.py new file mode 100644 index 000000000..88fe2bb49 --- /dev/null +++ b/shade/tests/unit/test_server_group.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- + +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +import mock + +import shade +from shade.tests.unit import base +from shade.tests import fakes + + +class TestServerGroup(base.TestCase): + + @mock.patch.object(shade.OpenStackCloud, 'nova_client') + def test_create_server_group(self, mock_nova): + server_group_name = 'my-server-group' + self.cloud.create_server_group(name=server_group_name, + policies=['affinity']) + + mock_nova.server_groups.create.assert_called_once_with( + name=server_group_name, policies=['affinity'] + ) + + @mock.patch.object(shade.OpenStackCloud, 'nova_client') + def test_delete_server_group(self, mock_nova): + mock_nova.server_groups.list.return_value = [ + fakes.FakeServerGroup('1234', 'name', ['affinity']) + ] + self.assertTrue(self.cloud.delete_server_group('1234')) + mock_nova.server_groups.list.assert_called_once_with() + mock_nova.server_groups.delete.assert_called_once_with( + id='1234' + )