diff --git a/doc/api_samples/os-security-groups/security-group-rules-post-req.json b/doc/api_samples/os-security-groups/security-group-rules-post-req.json index c5116c8f0b30..2480f1087860 100644 --- a/doc/api_samples/os-security-groups/security-group-rules-post-req.json +++ b/doc/api_samples/os-security-groups/security-group-rules-post-req.json @@ -1,9 +1,9 @@ { "security_group_rule": { - "parent_group_id": "21111111-1111-1111-1111-111111111112", + "parent_group_id": "d6f86d8c-06ef-4bef-8e7c-8bf8f9ba9b7a", "ip_protocol": "tcp", "from_port": 22, "to_port": 22, "cidr": "10.0.0.0/24" } -} \ No newline at end of file +} diff --git a/doc/api_samples/os-security-groups/security-group-rules-post-resp.json b/doc/api_samples/os-security-groups/security-group-rules-post-resp.json index 5bfd28910674..42cb9314dc4a 100644 --- a/doc/api_samples/os-security-groups/security-group-rules-post-resp.json +++ b/doc/api_samples/os-security-groups/security-group-rules-post-resp.json @@ -2,12 +2,12 @@ "security_group_rule": { "from_port": 22, "group": {}, - "id": "00000000-0000-0000-0000-000000000000", + "id": "baed7fb4-16b9-4b99-a2fd-02d0b1a4d9b2", "ip_protocol": "tcp", "ip_range": { "cidr": "10.0.0.0/24" }, - "parent_group_id": "11111111-1111-1111-1111-111111111111", + "parent_group_id": "d6f86d8c-06ef-4bef-8e7c-8bf8f9ba9b7a", "to_port": 22 } -} \ No newline at end of file +} diff --git a/doc/api_samples/os-security-groups/security-groups-create-resp.json b/doc/api_samples/os-security-groups/security-groups-create-resp.json index 52235062f330..b3e93d7e7f53 100644 --- a/doc/api_samples/os-security-groups/security-groups-create-resp.json +++ b/doc/api_samples/os-security-groups/security-groups-create-resp.json @@ -1,9 +1,9 @@ { "security_group": { "description": "default", - "id": 1, + "id": "4e469db4-3b60-43c7-8dfa-2c60e2f27075", "name": "default", "rules": [], "tenant_id": "6f70656e737461636b20342065766572" } -} \ No newline at end of file +} diff --git a/doc/api_samples/os-security-groups/security-groups-get-resp.json b/doc/api_samples/os-security-groups/security-groups-get-resp.json index 52235062f330..b3e93d7e7f53 100644 --- a/doc/api_samples/os-security-groups/security-groups-get-resp.json +++ b/doc/api_samples/os-security-groups/security-groups-get-resp.json @@ -1,9 +1,9 @@ { "security_group": { "description": "default", - "id": 1, + "id": "4e469db4-3b60-43c7-8dfa-2c60e2f27075", "name": "default", "rules": [], "tenant_id": "6f70656e737461636b20342065766572" } -} \ No newline at end of file +} diff --git a/doc/api_samples/os-security-groups/security-groups-list-get-resp.json b/doc/api_samples/os-security-groups/security-groups-list-get-resp.json index d4b225ac45a0..2f6a020e8fc8 100644 --- a/doc/api_samples/os-security-groups/security-groups-list-get-resp.json +++ b/doc/api_samples/os-security-groups/security-groups-list-get-resp.json @@ -2,10 +2,10 @@ "security_groups": [ { "description": "default", - "id": 1, + "id": "4e469db4-3b60-43c7-8dfa-2c60e2f27075", "name": "default", "rules": [], "tenant_id": "6f70656e737461636b20342065766572" } ] -} \ No newline at end of file +} diff --git a/doc/api_samples/os-security-groups/server-security-groups-list-resp.json b/doc/api_samples/os-security-groups/server-security-groups-list-resp.json index d4b225ac45a0..2f6a020e8fc8 100644 --- a/doc/api_samples/os-security-groups/server-security-groups-list-resp.json +++ b/doc/api_samples/os-security-groups/server-security-groups-list-resp.json @@ -2,10 +2,10 @@ "security_groups": [ { "description": "default", - "id": 1, + "id": "4e469db4-3b60-43c7-8dfa-2c60e2f27075", "name": "default", "rules": [], "tenant_id": "6f70656e737461636b20342065766572" } ] -} \ No newline at end of file +} diff --git a/nova/api/openstack/compute/schemas/security_groups.py b/nova/api/openstack/compute/schemas/security_groups.py index 964abc496674..50484aaf0ec2 100644 --- a/nova/api/openstack/compute/schemas/security_groups.py +++ b/nova/api/openstack/compute/schemas/security_groups.py @@ -12,6 +12,8 @@ # License for the specific language governing permissions and limitations # under the License. +import copy + from nova.api.validation import parameter_types create = { @@ -93,7 +95,7 @@ index_query = { } # TODO(stephenfin): Remove additionalProperties in a future API version -server_sg_index_query = { +index_server_query = { 'type': 'object', 'properties': {}, 'additionalProperties': True, @@ -139,6 +141,108 @@ remove_security_group = { 'additionalProperties': True, } +_security_group_rule_response = { + 'type': 'object', + 'properties': { + 'from_port': {'type': ['integer', 'null'], 'minimum': -1}, + 'group': { + 'type': 'object', + 'properties': { + 'name': {'type': 'string'}, + 'tenant_id': parameter_types.project_id, + }, + 'required': [], + 'additionalProperties': False, + }, + 'id': {'type': 'string', 'format': 'uuid'}, + 'ip_protocol': {'type': ['string', 'null']}, + 'ip_range': { + 'type': 'object', + 'properties': { + 'cidr': {'type': 'string', 'format': 'cidr'}, + }, + 'required': [], + 'additionalProperties': False, + }, + 'parent_group_id': {'type': 'string', 'format': 'uuid'}, + 'to_port': {'type': ['integer', 'null'], 'minimum': -1}, + }, + 'required': [ + 'from_port', + 'group', + 'id', + 'ip_protocol', + 'ip_range', + 'parent_group_id', + 'to_port', + ], + 'additionalProperties': False, +} + +_security_group_response = { + 'type': 'object', + 'properties': { + 'description': {'type': ['string', 'null']}, + 'id': {'type': 'string', 'format': 'uuid'}, + 'name': {'type': 'string'}, + 'rules': {'type': 'array', 'items': _security_group_rule_response}, + 'tenant_id': parameter_types.project_id, + }, + 'required': ['description', 'id', 'name', 'rules', 'tenant_id'], + 'additionalProperties': False, + +} + +show_response = { + 'type': 'object', + 'properties': { + 'security_group': _security_group_response, + }, + 'required': ['security_group'], + 'additionalProperties': False, +} + +delete_response = {'type': 'null'} + +index_response = { + 'type': 'object', + 'properties': { + 'security_groups': { + 'type': 'array', + 'items': _security_group_response, + }, + }, + 'required': ['security_groups'], + 'additionalProperties': False, +} + +create_response = copy.deepcopy(show_response) + +update_response = copy.deepcopy(show_response) + +create_rule_response = { + 'type': 'object', + 'properties': { + 'security_group_rule': _security_group_rule_response, + }, + 'required': ['security_group_rule'], + 'additionalProperties': False, +} + +delete_rule_response = {'type': 'null'} + +index_server_response = { + 'type': 'object', + 'properties': { + 'security_groups': { + 'type': 'array', + 'items': _security_group_response, + }, + }, + 'required': ['security_groups'], + 'additionalProperties': False, +} + add_security_group_response = { 'type': 'null', } diff --git a/nova/api/openstack/compute/security_groups.py b/nova/api/openstack/compute/security_groups.py index 4004143ec231..7f162258bae8 100644 --- a/nova/api/openstack/compute/security_groups.py +++ b/nova/api/openstack/compute/security_groups.py @@ -131,12 +131,14 @@ class SecurityGroupControllerBase(object): return group_rule_data_by_rule_group_id +@validation.validated class SecurityGroupController(SecurityGroupControllerBase, wsgi.Controller): """The Security group API controller for the OpenStack API.""" @wsgi.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @wsgi.expected_errors((400, 404)) @validation.query_schema(schema.show_query) + @validation.response_body_schema(schema.show_response) def show(self, req, id): """Return data about the given security group.""" context = req.environ['nova.context'] @@ -157,6 +159,7 @@ class SecurityGroupController(SecurityGroupControllerBase, wsgi.Controller): @wsgi.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @wsgi.expected_errors((400, 404)) @wsgi.response(202) + @validation.response_body_schema(schema.delete_response) def delete(self, req, id): """Delete a security group.""" context = req.environ['nova.context'] @@ -175,6 +178,7 @@ class SecurityGroupController(SecurityGroupControllerBase, wsgi.Controller): @wsgi.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @validation.query_schema(schema.index_query) @wsgi.expected_errors(404) + @validation.response_body_schema(schema.index_response) def index(self, req): """Returns a list of security groups.""" context = req.environ['nova.context'] @@ -199,6 +203,7 @@ class SecurityGroupController(SecurityGroupControllerBase, wsgi.Controller): @wsgi.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @wsgi.expected_errors((400, 403)) @validation.schema(schema.create) + @validation.response_body_schema(schema.create_response) def create(self, req, body): """Creates a new security group.""" context = req.environ['nova.context'] @@ -222,6 +227,7 @@ class SecurityGroupController(SecurityGroupControllerBase, wsgi.Controller): @wsgi.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @wsgi.expected_errors((400, 404)) @validation.schema(schema.update) + @validation.response_body_schema(schema.update_response) def update(self, req, id, body): """Update a security group.""" context = req.environ['nova.context'] @@ -251,12 +257,15 @@ class SecurityGroupController(SecurityGroupControllerBase, wsgi.Controller): group_ref)} -class SecurityGroupRulesController(SecurityGroupControllerBase, - wsgi.Controller): +@validation.validated +class SecurityGroupRulesController( + SecurityGroupControllerBase, wsgi.Controller, +): @wsgi.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @wsgi.expected_errors((400, 403, 404)) @validation.schema(schema.create_rules) + @validation.response_body_schema(schema.create_rule_response) def create(self, req, body): context = req.environ['nova.context'] context.can(sg_policies.POLICY_NAME % 'rule:create', @@ -330,6 +339,7 @@ class SecurityGroupRulesController(SecurityGroupControllerBase, @wsgi.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION) @wsgi.expected_errors((400, 404, 409)) @wsgi.response(202) + @validation.response_body_schema(schema.delete_rule_response) def delete(self, req, id): context = req.environ['nova.context'] context.can(sg_policies.POLICY_NAME % 'rule:delete', @@ -350,12 +360,14 @@ class SecurityGroupRulesController(SecurityGroupControllerBase, raise exc.HTTPBadRequest(explanation=exp.format_message()) +@validation.validated class ServerSecurityGroupController( SecurityGroupControllerBase, wsgi.Controller ): @wsgi.expected_errors(404) - @validation.query_schema(schema.server_sg_index_query) + @validation.query_schema(schema.index_server_query) + @validation.response_body_schema(schema.index_server_response) def index(self, req, server_id): """Returns a list of security groups for the given instance.""" context = req.environ['nova.context'] diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-security-groups/security-group-rules-post-req.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-security-groups/security-group-rules-post-req.json.tpl index da976bcd1ee2..2480f1087860 100644 --- a/nova/tests/functional/api_sample_tests/api_samples/os-security-groups/security-group-rules-post-req.json.tpl +++ b/nova/tests/functional/api_sample_tests/api_samples/os-security-groups/security-group-rules-post-req.json.tpl @@ -1,6 +1,6 @@ { "security_group_rule": { - "parent_group_id": "21111111-1111-1111-1111-111111111112", + "parent_group_id": "d6f86d8c-06ef-4bef-8e7c-8bf8f9ba9b7a", "ip_protocol": "tcp", "from_port": 22, "to_port": 22, diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-security-groups/security-group-rules-post-resp.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-security-groups/security-group-rules-post-resp.json.tpl index bb72d2768a6f..86173c8dfe64 100644 --- a/nova/tests/functional/api_sample_tests/api_samples/os-security-groups/security-group-rules-post-resp.json.tpl +++ b/nova/tests/functional/api_sample_tests/api_samples/os-security-groups/security-group-rules-post-resp.json.tpl @@ -4,10 +4,10 @@ "group": {}, "ip_protocol": "tcp", "to_port": 22, - "parent_group_id": "11111111-1111-1111-1111-111111111111", + "parent_group_id": "d6f86d8c-06ef-4bef-8e7c-8bf8f9ba9b7a", "ip_range": { "cidr": "10.0.0.0/24" }, - "id": "00000000-0000-0000-0000-000000000000" + "id": "%(uuid)s" } } diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-security-groups/security-groups-create-resp.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-security-groups/security-groups-create-resp.json.tpl index 13fd8e4d8f03..88973ebf92a2 100644 --- a/nova/tests/functional/api_sample_tests/api_samples/os-security-groups/security-groups-create-resp.json.tpl +++ b/nova/tests/functional/api_sample_tests/api_samples/os-security-groups/security-groups-create-resp.json.tpl @@ -1,7 +1,7 @@ { "security_group": { "description": "%(description)s", - "id": 1, + "id": "%(uuid)s", "name": "%(group_name)s", "rules": [], "tenant_id": "6f70656e737461636b20342065766572" diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-security-groups/security-groups-get-resp.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-security-groups/security-groups-get-resp.json.tpl index e0ccd183a3a6..e89bb6e54cac 100644 --- a/nova/tests/functional/api_sample_tests/api_samples/os-security-groups/security-groups-get-resp.json.tpl +++ b/nova/tests/functional/api_sample_tests/api_samples/os-security-groups/security-groups-get-resp.json.tpl @@ -1,7 +1,7 @@ { "security_group": { "description": "default", - "id": 1, + "id": "%(uuid)s", "name": "default", "rules": [], "tenant_id": "6f70656e737461636b20342065766572" diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-security-groups/security-groups-list-get-resp.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-security-groups/security-groups-list-get-resp.json.tpl index 0a3f1b21de43..8101b7fed0b1 100644 --- a/nova/tests/functional/api_sample_tests/api_samples/os-security-groups/security-groups-list-get-resp.json.tpl +++ b/nova/tests/functional/api_sample_tests/api_samples/os-security-groups/security-groups-list-get-resp.json.tpl @@ -2,7 +2,7 @@ "security_groups": [ { "description": "default", - "id": 1, + "id": "%(uuid)s", "name": "default", "rules": [], "tenant_id": "6f70656e737461636b20342065766572" diff --git a/nova/tests/functional/api_sample_tests/api_samples/os-security-groups/server-security-groups-list-resp.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/os-security-groups/server-security-groups-list-resp.json.tpl index 0a3f1b21de43..8101b7fed0b1 100644 --- a/nova/tests/functional/api_sample_tests/api_samples/os-security-groups/server-security-groups-list-resp.json.tpl +++ b/nova/tests/functional/api_sample_tests/api_samples/os-security-groups/server-security-groups-list-resp.json.tpl @@ -2,7 +2,7 @@ "security_groups": [ { "description": "default", - "id": 1, + "id": "%(uuid)s", "name": "default", "rules": [], "tenant_id": "6f70656e737461636b20342065766572" diff --git a/nova/tests/functional/api_sample_tests/test_security_groups.py b/nova/tests/functional/api_sample_tests/test_security_groups.py index 3b5ca55b5fe5..6b3a9094002c 100644 --- a/nova/tests/functional/api_sample_tests/test_security_groups.py +++ b/nova/tests/functional/api_sample_tests/test_security_groups.py @@ -13,13 +13,15 @@ # License for the specific language governing permissions and limitations # under the License. +from oslo_utils.fixture import uuidsentinel as uuids + from nova.tests.functional.api_sample_tests import test_servers import nova.tests.functional.api_samples_test_base as astb def fake_get(*args, **kwargs): nova_group = {} - nova_group['id'] = 1 + nova_group['id'] = uuids.security_group nova_group['description'] = 'default' nova_group['name'] = 'default' nova_group['project_id'] = astb.PROJECT_ID @@ -47,8 +49,7 @@ def fake_list(context, names=None, ids=None, project=None, search_opts=None): return [fake_get()] -def fake_get_instance_security_groups(context, instance_uuid, - detailed=False): +def fake_get_instance_security_groups(context, instance_uuid, detailed=False): return [fake_get()] @@ -61,8 +62,8 @@ def fake_create_security_group_rule(context, security_group, new_rule): 'from_port': 22, 'to_port': 22, 'cidr': '10.0.0.0/24', - 'id': '00000000-0000-0000-0000-000000000000', - 'parent_group_id': '11111111-1111-1111-1111-111111111111', + 'id': uuids.security_group_rule, + 'parent_group_id': 'd6f86d8c-06ef-4bef-8e7c-8bf8f9ba9b7a', 'protocol': 'tcp', 'group_id': None } @@ -75,7 +76,7 @@ def fake_remove_rules(context, security_group, rule_ids): def fake_get_rule(context, id): return { 'id': id, - 'parent_group_id': '11111111-1111-1111-1111-111111111111' + 'parent_group_id': 'd6f86d8c-06ef-4bef-8e7c-8bf8f9ba9b7a' } @@ -83,7 +84,7 @@ class SecurityGroupsJsonTest(test_servers.ServersSampleBase): sample_dir = 'os-security-groups' def setUp(self): - super(SecurityGroupsJsonTest, self).setUp() + super().setUp() path = 'nova.network.security_group_api.' self.stub_out(path + 'get', fake_get) self.stub_out(path + 'get_instances_security_groups_bindings', @@ -97,15 +98,13 @@ class SecurityGroupsJsonTest(test_servers.ServersSampleBase): fake_create_security_group) self.stub_out(path + 'create_security_group_rule', fake_create_security_group_rule) - self.stub_out(path + 'remove_rules', - fake_remove_rules) - self.stub_out(path + 'get_rule', - fake_get_rule) + self.stub_out(path + 'remove_rules', fake_remove_rules) + self.stub_out(path + 'get_rule', fake_get_rule) def _get_create_subs(self): return { - 'group_name': 'default', - "description": "default", + 'group_name': 'default', + "description": "default", } def _create_security_group(self): @@ -115,7 +114,7 @@ class SecurityGroupsJsonTest(test_servers.ServersSampleBase): def _add_group(self, uuid): subs = { - 'group_name': 'test' + 'group_name': 'test' } return self._do_post('servers/%s/action' % uuid, 'security-group-add-post-req', subs) @@ -134,8 +133,7 @@ class SecurityGroupsJsonTest(test_servers.ServersSampleBase): def test_security_groups_get(self): # Get api sample of security groups get request. - security_group_id = '11111111-1111-1111-1111-111111111111' - response = self._do_get('os-security-groups/%s' % security_group_id) + response = self._do_get('os-security-groups/%s' % uuids.security_group) self._verify_response('security-groups-get-resp', {}, response, 200) def test_security_groups_list_server(self): @@ -157,7 +155,7 @@ class SecurityGroupsJsonTest(test_servers.ServersSampleBase): uuid = self._post_server() self._add_group(uuid) subs = { - 'group_name': 'test' + 'group_name': 'test' } response = self._do_post('servers/%s/action' % uuid, 'security-group-remove-post-req', subs) @@ -172,5 +170,5 @@ class SecurityGroupsJsonTest(test_servers.ServersSampleBase): def test_security_group_rules_remove(self): response = self._do_delete( - 'os-security-group-rules/00000000-0000-0000-0000-000000000000') + f'os-security-group-rules/{uuids.security_group_rule}') self.assertEqual(202, response.status_code) diff --git a/nova/tests/unit/api/openstack/compute/test_security_groups.py b/nova/tests/unit/api/openstack/compute/test_security_groups.py index d6f82a728e94..7e54ee717faa 100644 --- a/nova/tests/unit/api/openstack/compute/test_security_groups.py +++ b/nova/tests/unit/api/openstack/compute/test_security_groups.py @@ -513,14 +513,14 @@ class TestSecurityGroupsV21(test.TestCase): rule1 = security_group_rule_template( cidr='10.2.3.124/24', parent_group_id=uuids.parent_group_id, - group_id={}, id=88, + group_id={}, id=uuids.sg_rule_1, protocol='TCP') rule2 = security_group_rule_template( cidr='10.2.3.125/24', parent_group_id=uuids.parent_group_id, - id=99, protocol=88, + id=uuids.sg_rule_2, protocol=88, group_id='HAS_BEEN_DELETED') - sg = security_group_template(id=1, + sg = security_group_template(id=uuids.sg, name='test', description='test-desc', rules=[rule1, rule2]) @@ -533,8 +533,8 @@ class TestSecurityGroupsV21(test.TestCase): expected_rule = security_group_rule_template( ip_range={'cidr': '10.2.3.124/24'}, parent_group_id=uuids.parent_group_id, - group={}, id=88, ip_protocol='TCP') - expected = security_group_template(id=1, + group={}, id=uuids.sg_rule_1, ip_protocol='TCP') + expected = security_group_template(id=uuids.sg, name='test', description='test-desc', rules=[expected_rule]) @@ -542,10 +542,9 @@ class TestSecurityGroupsV21(test.TestCase): expected = {'security_groups': [expected]} with mock.patch( - 'nova.network.security_group_api.list', - return_value=[ - security_group_db( - secgroup) for secgroup in groups]) as mock_list: + 'nova.network.security_group_api.list', + return_value=[security_group_db(secgroup) for secgroup in groups], + ) as mock_list: res_dict = self.controller.index(self.req) self.assertEqual(res_dict, expected) diff --git a/nova/tests/unit/policies/test_security_groups.py b/nova/tests/unit/policies/test_security_groups.py index 990794f2fd04..eced438ae2a0 100644 --- a/nova/tests/unit/policies/test_security_groups.py +++ b/nova/tests/unit/policies/test_security_groups.py @@ -170,6 +170,8 @@ class SecurityGroupsPolicyTest(base.BasePolicyTest): @mock.patch('nova.network.security_group_api.list') def test_list_security_groups_policy(self, mock_get): + mock_get.return_value = [] + rule_name = policies.POLICY_NAME % 'get' self.common_policy_auth(self.project_reader_authorized_contexts, rule_name, @@ -178,6 +180,14 @@ class SecurityGroupsPolicyTest(base.BasePolicyTest): @mock.patch('nova.network.security_group_api.get') def test_show_security_groups_policy(self, mock_get): + mock_get.return_value = { + 'id': uuids.sg_id, + 'description': None, + 'name': 'foo', + 'project_id': uuids.project_id, + 'rules': [], + } + rule_name = policies.POLICY_NAME % 'show' self.common_policy_auth(self.project_reader_authorized_contexts, rule_name, @@ -187,6 +197,14 @@ class SecurityGroupsPolicyTest(base.BasePolicyTest): @mock.patch('nova.network.security_group_api.get') @mock.patch('nova.network.security_group_api.update_security_group') def test_update_security_groups_policy(self, mock_update, mock_get): + mock_update.return_value = { + 'id': uuids.sg_id, + 'description': None, + 'name': 'foo', + 'project_id': uuids.project_id, + 'rules': [], + } + rule_name = policies.POLICY_NAME % 'update' body = {'security_group': { 'name': 'test', @@ -198,6 +216,14 @@ class SecurityGroupsPolicyTest(base.BasePolicyTest): @mock.patch('nova.network.security_group_api.create_security_group') def test_create_security_groups_policy(self, mock_create): + mock_create.return_value = { + 'id': uuids.sg_id, + 'description': None, + 'name': 'foo', + 'project_id': uuids.project_id, + 'rules': [], + } + rule_name = policies.POLICY_NAME % 'create' body = {'security_group': { 'name': 'test', @@ -219,6 +245,20 @@ class SecurityGroupsPolicyTest(base.BasePolicyTest): @mock.patch('nova.network.security_group_api.get') @mock.patch('nova.network.security_group_api.create_security_group_rule') def test_create_security_group_rules_policy(self, mock_create, mock_get): + mock_get.return_value = { + 'id': uuids.sg_id, + 'name': 'foo', + 'project_id': uuids.project_id, + } + mock_create.return_value = { + 'id': uuids.sg_rule_id, + 'parent_group_id': uuids.sg_id, + 'protocol': 'tcp', + 'from_port': 22, + 'to_port': 22, + 'cidr': '10.0.0.0/24', + } + rule_name = policies.POLICY_NAME % 'rule:create' body = {'security_group_rule': { 'ip_protocol': 'test', 'group_id': uuids.fake_id,