diff --git a/tempest_lib/api_schema/response/compute/v2_1/security_groups.py b/tempest_lib/api_schema/response/compute/v2_1/security_groups.py new file mode 100644 index 0000000..5ed5a5c --- /dev/null +++ b/tempest_lib/api_schema/response/compute/v2_1/security_groups.py @@ -0,0 +1,113 @@ +# Copyright 2014 NEC Corporation. All rights reserved. +# +# 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. + +common_security_group_rule = { + 'from_port': {'type': ['integer', 'null']}, + 'to_port': {'type': ['integer', 'null']}, + 'group': { + 'type': 'object', + 'properties': { + 'tenant_id': {'type': 'string'}, + 'name': {'type': 'string'} + }, + 'additionalProperties': False, + }, + 'ip_protocol': {'type': ['string', 'null']}, + # 'parent_group_id' can be UUID so defining it as 'string' also. + 'parent_group_id': {'type': ['string', 'integer', 'null']}, + 'ip_range': { + 'type': 'object', + 'properties': { + 'cidr': {'type': 'string'} + }, + 'additionalProperties': False, + # When optional argument is provided in request body + # like 'group_id' then, attribute 'cidr' does not + # comes in response body. So it is not 'required'. + }, + 'id': {'type': ['string', 'integer']} +} + +common_security_group = { + 'type': 'object', + 'properties': { + 'id': {'type': ['integer', 'string']}, + 'name': {'type': 'string'}, + 'tenant_id': {'type': 'string'}, + 'rules': { + 'type': 'array', + 'items': { + 'type': ['object', 'null'], + 'properties': common_security_group_rule, + 'additionalProperties': False, + } + }, + 'description': {'type': 'string'}, + }, + 'additionalProperties': False, + 'required': ['id', 'name', 'tenant_id', 'rules', 'description'], +} + +list_security_groups = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'security_groups': { + 'type': 'array', + 'items': common_security_group + } + }, + 'additionalProperties': False, + 'required': ['security_groups'] + } +} + +get_security_group = create_security_group = update_security_group = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'security_group': common_security_group + }, + 'additionalProperties': False, + 'required': ['security_group'] + } +} + +delete_security_group = { + 'status_code': [202] +} + +create_security_group_rule = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'security_group_rule': { + 'type': 'object', + 'properties': common_security_group_rule, + 'additionalProperties': False, + 'required': ['from_port', 'to_port', 'group', 'ip_protocol', + 'parent_group_id', 'id', 'ip_range'] + } + }, + 'additionalProperties': False, + 'required': ['security_group_rule'] + } +} + +delete_security_group_rule = { + 'status_code': [202] +} diff --git a/tempest_lib/services/compute/security_groups_client.py b/tempest_lib/services/compute/security_groups_client.py new file mode 100644 index 0000000..fd93b2b --- /dev/null +++ b/tempest_lib/services/compute/security_groups_client.py @@ -0,0 +1,92 @@ +# Copyright 2012 OpenStack Foundation +# All Rights Reserved. +# +# 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 oslo_serialization import jsonutils as json +from six.moves.urllib import parse as urllib + +from tempest_lib.api_schema.response.compute.v2_1 import \ + security_groups as schema +from tempest_lib.common import rest_client +from tempest_lib import exceptions as lib_exc + + +class SecurityGroupsClient(rest_client.RestClient): + + def list_security_groups(self, **params): + """List all security groups for a user.""" + + url = 'os-security-groups' + if params: + url += '?%s' % urllib.urlencode(params) + + resp, body = self.get(url) + body = json.loads(body) + self.validate_response(schema.list_security_groups, resp, body) + return rest_client.ResponseBody(resp, body) + + def show_security_group(self, security_group_id): + """Get the details of a Security Group.""" + url = "os-security-groups/%s" % security_group_id + resp, body = self.get(url) + body = json.loads(body) + self.validate_response(schema.get_security_group, resp, body) + return rest_client.ResponseBody(resp, body) + + def create_security_group(self, **kwargs): + """Creates a new security group. + + name (Required): Name of security group. + description (Required): Description of security group. + + """ + post_body = json.dumps({'security_group': kwargs}) + resp, body = self.post('os-security-groups', post_body) + body = json.loads(body) + self.validate_response(schema.get_security_group, resp, body) + return rest_client.ResponseBody(resp, body) + + def update_security_group(self, security_group_id, **kwargs): + """Update a security group. + + security_group_id: a security_group to update + name: new name of security group + description: new description of security group + + """ + post_body = json.dumps({'security_group': kwargs}) + resp, body = self.put('os-security-groups/%s' % security_group_id, + post_body) + body = json.loads(body) + self.validate_response(schema.update_security_group, resp, body) + return rest_client.ResponseBody(resp, body) + + def delete_security_group(self, security_group_id): + """Deletes the provided Security Group.""" + resp, body = self.delete( + 'os-security-groups/%s' % security_group_id) + self.validate_response(schema.delete_security_group, resp, body) + return rest_client.ResponseBody(resp, body) + + def is_resource_deleted(self, id): + try: + self.show_security_group(id) + except lib_exc.NotFound: + return True + return False + + @property + def resource_type(self): + """Returns the primary type of resource this client works with.""" + return 'security_group' diff --git a/tempest_lib/tests/services/compute/test_security_groups_client.py b/tempest_lib/tests/services/compute/test_security_groups_client.py new file mode 100644 index 0000000..1d44b89 --- /dev/null +++ b/tempest_lib/tests/services/compute/test_security_groups_client.py @@ -0,0 +1,113 @@ +# Copyright 2015 NEC Corporation. All rights reserved. +# +# 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 oslotest import mockpatch + +from tempest_lib import exceptions as lib_exc +from tempest_lib.services.compute import security_groups_client +from tempest_lib.tests import fake_auth_provider +from tempest_lib.tests.services.compute import base + + +class TestSecurityGroupsClient(base.BaseComputeServiceTest): + + FAKE_SECURITY_GROUP_INFO = [{ + "description": "default", + "id": "3fb26eb3-581b-4420-9963-b0879a026506", + "name": "default", + "rules": [], + "tenant_id": "openstack" + }] + + def setUp(self): + super(TestSecurityGroupsClient, self).setUp() + fake_auth = fake_auth_provider.FakeAuthProvider() + self.client = security_groups_client.SecurityGroupsClient( + fake_auth, 'compute', 'regionOne') + + def _test_list_security_groups(self, bytes_body=False): + self.check_service_client_function( + self.client.list_security_groups, + 'tempest_lib.common.rest_client.RestClient.get', + {"security_groups": self.FAKE_SECURITY_GROUP_INFO}, + to_utf=bytes_body) + + def test_list_security_groups_with_str_body(self): + self._test_list_security_groups() + + def test_list_security_groups_with_bytes_body(self): + self._test_list_security_groups(bytes_body=True) + + def _test_show_security_group(self, bytes_body=False): + self.check_service_client_function( + self.client.show_security_group, + 'tempest_lib.common.rest_client.RestClient.get', + {"security_group": self.FAKE_SECURITY_GROUP_INFO[0]}, + to_utf=bytes_body, + security_group_id='fake-id') + + def test_show_security_group_with_str_body(self): + self._test_show_security_group() + + def test_show_security_group_with_bytes_body(self): + self._test_show_security_group(bytes_body=True) + + def _test_create_security_group(self, bytes_body=False): + post_body = {"name": "test", "description": "test_group"} + self.check_service_client_function( + self.client.create_security_group, + 'tempest_lib.common.rest_client.RestClient.post', + {"security_group": self.FAKE_SECURITY_GROUP_INFO[0]}, + to_utf=bytes_body, + kwargs=post_body) + + def test_create_security_group_with_str_body(self): + self._test_create_security_group() + + def test_create_security_group_with_bytes_body(self): + self._test_create_security_group(bytes_body=True) + + def _test_update_security_group(self, bytes_body=False): + req_body = {"name": "test", "description": "test_group"} + self.check_service_client_function( + self.client.update_security_group, + 'tempest_lib.common.rest_client.RestClient.put', + {"security_group": self.FAKE_SECURITY_GROUP_INFO[0]}, + to_utf=bytes_body, + security_group_id='fake-id', + kwargs=req_body) + + def test_update_security_group_with_str_body(self): + self._test_update_security_group() + + def test_update_security_group_with_bytes_body(self): + self._test_update_security_group(bytes_body=True) + + def test_delete_security_group(self): + self.check_service_client_function( + self.client.delete_security_group, + 'tempest_lib.common.rest_client.RestClient.delete', + {}, status=202, security_group_id='fake-id') + + def test_is_resource_deleted_true(self): + mod = ('tempest_lib.services.compute.security_groups_client.' + 'SecurityGroupsClient.show_security_group') + self.useFixture(mockpatch.Patch(mod, side_effect=lib_exc.NotFound)) + self.assertTrue(self.client.is_resource_deleted('fake-id')) + + def test_is_resource_deleted_false(self): + mod = ('tempest_lib.services.compute.security_groups_client.' + 'SecurityGroupsClient.show_security_group') + self.useFixture(mockpatch.Patch(mod, return_value='success')) + self.assertFalse(self.client.is_resource_deleted('fake-id'))