diff --git a/ironic_tempest_plugin/services/baremetal/v1/json/baremetal_client.py b/ironic_tempest_plugin/services/baremetal/v1/json/baremetal_client.py index 559e21d4e5..663ca9c200 100644 --- a/ironic_tempest_plugin/services/baremetal/v1/json/baremetal_client.py +++ b/ironic_tempest_plugin/services/baremetal/v1/json/baremetal_client.py @@ -40,6 +40,11 @@ class BaremetalClient(base.BaremetalClient): """List all existing ports.""" return self._list_request('ports', **kwargs) + @base.handle_errors + def list_portgroups(self, **kwargs): + """List all existing port groups.""" + return self._list_request('portgroups', **kwargs) + @base.handle_errors def list_node_ports(self, uuid): """List all ports associated with the node.""" @@ -104,6 +109,15 @@ class BaremetalClient(base.BaremetalClient): """ return self._show_request('ports', uuid) + @base.handle_errors + def show_portgroup(self, portgroup_ident): + """Gets a specific port group. + + :param portgroup_ident: Name or UUID of the port group. + :return: Serialized port group as a dictionary. + """ + return self._show_request('portgroups', portgroup_ident) + @base.handle_errors def show_port_by_address(self, address): """Gets a specific port by address. @@ -184,6 +198,30 @@ class BaremetalClient(base.BaremetalClient): return self._create_request('ports', port) + @base.handle_errors + def create_portgroup(self, node_uuid, **kwargs): + """Create a port group with the specified parameters. + + :param node_uuid: The UUID of the node which owns the port group. + :param kwargs: + address: MAC address of the port group. Optional. + extra: Meta data of the port group. Default: {'foo': 'bar'}. + name: Name of the port group. Optional. + uuid: UUID of the port group. Optional. + :return: A tuple with the server response and the created port group. + """ + portgroup = {'extra': kwargs.get('extra', {'foo': 'bar'})} + + portgroup['node_uuid'] = node_uuid + + if kwargs.get('address'): + portgroup['address'] = kwargs['address'] + + if kwargs.get('name'): + portgroup['name'] = kwargs['name'] + + return self._create_request('portgroups', portgroup) + @base.handle_errors def delete_node(self, uuid): """Deletes a node having the specified UUID. @@ -214,6 +252,15 @@ class BaremetalClient(base.BaremetalClient): """ return self._delete_request('ports', uuid) + @base.handle_errors + def delete_portgroup(self, portgroup_ident): + """Deletes a port group having the specified UUID or name. + + :param portgroup_ident: Name or UUID of the port group. + :return: A tuple with the server response and the response body. + """ + return self._delete_request('portgroups', portgroup_ident) + @base.handle_errors def update_node(self, uuid, patch=None, **kwargs): """Update the specified node. diff --git a/ironic_tempest_plugin/tests/api/admin/base.py b/ironic_tempest_plugin/tests/api/admin/base.py index f6b634ca05..e1d4e867f8 100644 --- a/ironic_tempest_plugin/tests/api/admin/base.py +++ b/ironic_tempest_plugin/tests/api/admin/base.py @@ -33,7 +33,7 @@ SUPPORTED_DRIVERS = ['fake'] # NOTE(jroll): resources must be deleted in a specific order, this list # defines the resource types to clean up, and the correct order. -RESOURCE_TYPES = ['port', 'node', 'chassis'] +RESOURCE_TYPES = ['port', 'node', 'chassis', 'portgroup'] def creates(resource): @@ -200,6 +200,18 @@ class BaseBaremetalTest(api_version_utils.BaseMicroversionTest, return resp, body + @classmethod + @creates('portgroup') + def create_portgroup(cls, node_uuid, **kwargs): + """Wrapper utility for creating test port groups. + + :param node_uuid: The unique identifier of the node. + :return: Created port group. + """ + resp, body = cls.client.create_portgroup(node_uuid=node_uuid, **kwargs) + + return resp, body + @classmethod def delete_chassis(cls, chassis_id): """Deletes a chassis having the specified UUID. @@ -248,6 +260,20 @@ class BaseBaremetalTest(api_version_utils.BaseMicroversionTest, return resp + @classmethod + def delete_portgroup(cls, portgroup_ident): + """Deletes a port group having the specified UUID or name. + + :param portgroup_ident: The name or UUID of the port group. + :return: Server response. + """ + resp, body = cls.client.delete_portgroup(portgroup_ident) + + if portgroup_ident in cls.created_objects['portgroup']: + cls.created_objects['portgroup'].remove(portgroup_ident) + + return resp + def validate_self_link(self, resource, uuid, link): """Check whether the given self link formatted correctly.""" expected_link = "{base}/{pref}/{res}/{uuid}".format( diff --git a/ironic_tempest_plugin/tests/api/admin/test_portgroups.py b/ironic_tempest_plugin/tests/api/admin/test_portgroups.py new file mode 100644 index 0000000000..ceb1d11fb3 --- /dev/null +++ b/ironic_tempest_plugin/tests/api/admin/test_portgroups.py @@ -0,0 +1,68 @@ +# 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. + +from tempest.lib.common.utils import data_utils +from tempest.lib import decorators +from tempest.lib import exceptions as lib_exc + +from ironic_tempest_plugin.tests.api.admin import api_microversion_fixture +from ironic_tempest_plugin.tests.api.admin import base + + +class TestPortGroups(base.BaseBaremetalTest): + """Basic positive test cases for port groups.""" + + def setUp(self): + super(TestPortGroups, self).setUp() + self.useFixture( + api_microversion_fixture.APIMicroversionFixture('1.25')) + _, self.chassis = self.create_chassis() + _, self.node = self.create_node(self.chassis['uuid']) + _, self.portgroup = self.create_portgroup( + self.node['uuid'], address=data_utils.rand_mac_address()) + + @decorators.idempotent_id('110cd302-256b-4ddc-be10-fc6c9ad8e649') + def test_create_portgroup_with_address(self): + """Create a port group with specific MAC address.""" + _, body = self.client.show_portgroup(self.portgroup['uuid']) + self.assertEqual(self.portgroup['address'], body['address']) + + @decorators.idempotent_id('4336fa0f-da86-4cec-b788-89f59a7635a5') + def test_create_portgroup_no_address(self): + """Create a port group without setting MAC address.""" + _, portgroup = self.create_portgroup(self.node['uuid']) + _, body = self.client.show_portgroup(portgroup['uuid']) + + self._assertExpected(portgroup, body) + self.assertIsNone(body['address']) + + @decorators.idempotent_id('8378c69f-f806-454b-8ddd-6b7fd93ab12b') + def test_delete_portgroup(self): + """Delete a port group.""" + self.delete_portgroup(self.portgroup['uuid']) + self.assertRaises(lib_exc.NotFound, self.client.show_portgroup, + self.portgroup['uuid']) + + @decorators.idempotent_id('f6be5e70-3e3b-435c-b2fc-bbb2cc9b3185') + def test_show_portgroup(self): + """Show a specified port group.""" + _, portgroup = self.client.show_portgroup(self.portgroup['uuid']) + self._assertExpected(self.portgroup, portgroup) + + @decorators.idempotent_id('cf2dfd95-5ea1-4109-8ad3-297cd76aa5d3') + def test_list_portgroups(self): + """List port groups.""" + _, body = self.client.list_portgroups() + self.assertIn(self.portgroup['uuid'], + [i['uuid'] for i in body['portgroups']]) + self.assertIn(self.portgroup['address'], + [i['address'] for i in body['portgroups']])