From c7d9924a08d35a47b4bc113b8b452ed7a1dd8929 Mon Sep 17 00:00:00 2001 From: He Jie Xu Date: Fri, 26 Sep 2014 13:58:09 +0800 Subject: [PATCH] Port security-group-default-rules extension into v2.1 This patch port security-group-default-rules into v2.1. This patch just move the v2 code into v2.1 and share the unittest. The other v2.1 related fix and improvement will be addressed by subsequent patchset. Partially implements blueprint v2-on-v3-api Change-Id: I4d65993d6413160d2c7c2eb2ce0c3f66768e1a36 --- ...curity-group-default-rules-create-req.json | 8 + ...urity-group-default-rules-create-resp.json | 11 ++ ...ecurity-group-default-rules-list-resp.json | 13 ++ ...ecurity-group-default-rules-show-resp.json | 11 ++ etc/nova/policy.json | 2 + .../v3/security_group_default_rules.py | 158 ++++++++++++++++++ .../test_security_group_default_rules.py | 41 +++-- nova/tests/fake_policy.py | 1 + ...ty-group-default-rules-create-req.json.tpl | 8 + ...y-group-default-rules-create-resp.json.tpl | 11 ++ ...ity-group-default-rules-list-resp.json.tpl | 13 ++ ...ity-group-default-rules-show-resp.json.tpl | 11 ++ .../v3/test_security_group_default_rules.py | 40 +++++ setup.cfg | 1 + 14 files changed, 317 insertions(+), 12 deletions(-) create mode 100644 doc/v3/api_samples/os-security-group-default-rules/security-group-default-rules-create-req.json create mode 100644 doc/v3/api_samples/os-security-group-default-rules/security-group-default-rules-create-resp.json create mode 100644 doc/v3/api_samples/os-security-group-default-rules/security-group-default-rules-list-resp.json create mode 100644 doc/v3/api_samples/os-security-group-default-rules/security-group-default-rules-show-resp.json create mode 100644 nova/api/openstack/compute/plugins/v3/security_group_default_rules.py create mode 100644 nova/tests/integrated/v3/api_samples/os-security-group-default-rules/security-group-default-rules-create-req.json.tpl create mode 100644 nova/tests/integrated/v3/api_samples/os-security-group-default-rules/security-group-default-rules-create-resp.json.tpl create mode 100644 nova/tests/integrated/v3/api_samples/os-security-group-default-rules/security-group-default-rules-list-resp.json.tpl create mode 100644 nova/tests/integrated/v3/api_samples/os-security-group-default-rules/security-group-default-rules-show-resp.json.tpl create mode 100644 nova/tests/integrated/v3/test_security_group_default_rules.py diff --git a/doc/v3/api_samples/os-security-group-default-rules/security-group-default-rules-create-req.json b/doc/v3/api_samples/os-security-group-default-rules/security-group-default-rules-create-req.json new file mode 100644 index 0000000000..8836d0eecc --- /dev/null +++ b/doc/v3/api_samples/os-security-group-default-rules/security-group-default-rules-create-req.json @@ -0,0 +1,8 @@ +{ + "security_group_default_rule": { + "ip_protocol": "TCP", + "from_port": "80", + "to_port": "80", + "cidr": "10.10.10.0/24" + } +} \ No newline at end of file diff --git a/doc/v3/api_samples/os-security-group-default-rules/security-group-default-rules-create-resp.json b/doc/v3/api_samples/os-security-group-default-rules/security-group-default-rules-create-resp.json new file mode 100644 index 0000000000..f757b727a4 --- /dev/null +++ b/doc/v3/api_samples/os-security-group-default-rules/security-group-default-rules-create-resp.json @@ -0,0 +1,11 @@ +{ + "security_group_default_rule": { + "from_port": 80, + "id": 1, + "ip_protocol": "TCP", + "ip_range": { + "cidr": "10.10.10.0/24" + }, + "to_port": 80 + } +} \ No newline at end of file diff --git a/doc/v3/api_samples/os-security-group-default-rules/security-group-default-rules-list-resp.json b/doc/v3/api_samples/os-security-group-default-rules/security-group-default-rules-list-resp.json new file mode 100644 index 0000000000..c083640c3e --- /dev/null +++ b/doc/v3/api_samples/os-security-group-default-rules/security-group-default-rules-list-resp.json @@ -0,0 +1,13 @@ +{ + "security_group_default_rules": [ + { + "from_port": 80, + "id": 1, + "ip_protocol": "TCP", + "ip_range": { + "cidr": "10.10.10.0/24" + }, + "to_port": 80 + } + ] +} \ No newline at end of file diff --git a/doc/v3/api_samples/os-security-group-default-rules/security-group-default-rules-show-resp.json b/doc/v3/api_samples/os-security-group-default-rules/security-group-default-rules-show-resp.json new file mode 100644 index 0000000000..f757b727a4 --- /dev/null +++ b/doc/v3/api_samples/os-security-group-default-rules/security-group-default-rules-show-resp.json @@ -0,0 +1,11 @@ +{ + "security_group_default_rule": { + "from_port": 80, + "id": 1, + "ip_protocol": "TCP", + "ip_range": { + "cidr": "10.10.10.0/24" + }, + "to_port": 80 + } +} \ No newline at end of file diff --git a/etc/nova/policy.json b/etc/nova/policy.json index 9fbc3ef002..2a8b6e8bcf 100644 --- a/etc/nova/policy.json +++ b/etc/nova/policy.json @@ -242,6 +242,8 @@ "compute_extension:v3:os-rescue:discoverable": "", "compute_extension:v3:os-scheduler-hints:discoverable": "", "compute_extension:security_group_default_rules": "rule:admin_api", + "compute_extension:v3:os-security-group-default-rules:discoverable": "", + "compute_extension:v3:os-security-group-default-rules": "rule:admin_api", "compute_extension:security_groups": "", "compute_extension:v3:os-security-groups": "", "compute_extension:v3:os-security-groups:discoverable": "", diff --git a/nova/api/openstack/compute/plugins/v3/security_group_default_rules.py b/nova/api/openstack/compute/plugins/v3/security_group_default_rules.py new file mode 100644 index 0000000000..12f25504d4 --- /dev/null +++ b/nova/api/openstack/compute/plugins/v3/security_group_default_rules.py @@ -0,0 +1,158 @@ +# Copyright 2013 Metacloud Inc. +# +# 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 webob import exc + +from nova.api.openstack.compute.plugins.v3 import security_groups as sg +from nova.api.openstack import extensions +from nova.api.openstack import wsgi +from nova import exception +from nova.i18n import _ +from nova.network.security_group import openstack_driver + + +ALIAS = "os-security-group-default-rules" +authorize = extensions.extension_authorizer('compute', 'v3:' + ALIAS) + + +def _authorize_context(req): + context = req.environ['nova.context'] + authorize(context) + return context + + +class SecurityGroupDefaultRulesController(sg.SecurityGroupControllerBase): + + def __init__(self): + self.security_group_api = ( + openstack_driver.get_openstack_security_group_driver()) + + @extensions.expected_errors((400, 501)) + def create(self, req, body): + context = sg._authorize_context(req) + authorize(context) + + sg_rule = self._from_body(body, 'security_group_default_rule') + + try: + values = self._rule_args_to_dict(to_port=sg_rule.get('to_port'), + from_port=sg_rule.get('from_port'), + ip_protocol=sg_rule.get('ip_protocol'), + cidr=sg_rule.get('cidr')) + except (exception.InvalidCidr, + exception.InvalidInput, + exception.InvalidIpProtocol, + exception.InvalidPortRange) as ex: + raise exc.HTTPBadRequest(explanation=ex.format_message()) + + if values is None: + msg = _('Not enough parameters to build a valid rule.') + raise exc.HTTPBadRequest(explanation=msg) + + if self.security_group_api.default_rule_exists(context, values): + msg = _('This default rule already exists.') + raise exc.HTTPBadRequest(explanation=msg) + security_group_rule = self.security_group_api.add_default_rules( + context, [values])[0] + fmt_rule = self._format_security_group_default_rule( + security_group_rule) + return {'security_group_default_rule': fmt_rule} + + def _rule_args_to_dict(self, to_port=None, from_port=None, + ip_protocol=None, cidr=None): + cidr = self.security_group_api.parse_cidr(cidr) + return self.security_group_api.new_cidr_ingress_rule( + cidr, ip_protocol, from_port, to_port) + + @extensions.expected_errors((400, 404, 501)) + def show(self, req, id): + context = sg._authorize_context(req) + authorize(context) + + try: + id = self.security_group_api.validate_id(id) + except exception.Invalid as ex: + raise exc.HTTPBadRequest(explanation=ex.format_message()) + + try: + rule = self.security_group_api.get_default_rule(context, id) + except exception.SecurityGroupNotFound as ex: + raise exc.HTTPNotFound(explanation=ex.format_message()) + + fmt_rule = self._format_security_group_default_rule(rule) + return {"security_group_default_rule": fmt_rule} + + @extensions.expected_errors((400, 404, 501)) + @wsgi.response(204) + def delete(self, req, id): + context = sg._authorize_context(req) + authorize(context) + + try: + id = self.security_group_api.validate_id(id) + except exception.Invalid as ex: + raise exc.HTTPBadRequest(explanation=ex.format_message()) + + try: + rule = self.security_group_api.get_default_rule(context, id) + except exception.SecurityGroupNotFound as ex: + raise exc.HTTPNotFound(explanation=ex.format_message()) + + try: + self.security_group_api.remove_default_rules(context, [rule['id']]) + except exception.SecurityGroupDefaultRuleNotFound as ex: + raise exc.HTTPNotFound(explanation=ex.format_message()) + + @extensions.expected_errors((404, 501)) + def index(self, req): + + context = sg._authorize_context(req) + authorize(context) + + ret = {'security_group_default_rules': []} + try: + for rule in self.security_group_api.get_all_default_rules(context): + rule_fmt = self._format_security_group_default_rule(rule) + ret['security_group_default_rules'].append(rule_fmt) + except exception.SecurityGroupDefaultRuleNotFound as ex: + raise exc.HTTPNotFound(explanation=ex.format_message()) + + return ret + + def _format_security_group_default_rule(self, rule): + sg_rule = {} + sg_rule['id'] = rule['id'] + sg_rule['ip_protocol'] = rule['protocol'] + sg_rule['from_port'] = rule['from_port'] + sg_rule['to_port'] = rule['to_port'] + sg_rule['ip_range'] = {} + sg_rule['ip_range'] = {'cidr': rule['cidr']} + return sg_rule + + +class SecurityGroupDefaultRules(extensions.V3APIExtensionBase): + """Default rules for security group support.""" + name = "SecurityGroupDefaultRules" + alias = ALIAS + version = 1 + + def get_resources(self): + resources = [ + extensions.ResourceExtension(ALIAS, + SecurityGroupDefaultRulesController())] + + return resources + + def get_controller_extensions(self): + return [] diff --git a/nova/tests/api/openstack/compute/contrib/test_security_group_default_rules.py b/nova/tests/api/openstack/compute/contrib/test_security_group_default_rules.py index af543dede0..abe0724f78 100644 --- a/nova/tests/api/openstack/compute/contrib/test_security_group_default_rules.py +++ b/nova/tests/api/openstack/compute/contrib/test_security_group_default_rules.py @@ -16,7 +16,10 @@ from lxml import etree from oslo.config import cfg import webob -from nova.api.openstack.compute.contrib import security_group_default_rules +from nova.api.openstack.compute.contrib import \ + security_group_default_rules as security_group_default_rules_v2 +from nova.api.openstack.compute.plugins.v3 import \ + security_group_default_rules as security_group_default_rules_v21 from nova.api.openstack import wsgi from nova import context import nova.db @@ -48,12 +51,14 @@ def security_group_default_rule_db(security_group_default_rule, id=None): return AttrDict(attrs) -class TestSecurityGroupDefaultRulesNeutron(test.TestCase): +class TestSecurityGroupDefaultRulesNeutronV21(test.TestCase): + controller_cls = (security_group_default_rules_v21. + SecurityGroupDefaultRulesController) + def setUp(self): self.flags(security_group_api='neutron') - super(TestSecurityGroupDefaultRulesNeutron, self).setUp() - self.controller = \ - security_group_default_rules.SecurityGroupDefaultRulesController() + super(TestSecurityGroupDefaultRulesNeutronV21, self).setUp() + self.controller = self.controller_cls() def test_create_security_group_default_rule_not_implemented_neutron(self): sgr = security_group_default_rule_template() @@ -81,11 +86,18 @@ class TestSecurityGroupDefaultRulesNeutron(test.TestCase): req, '602ed77c-a076-4f9b-a617-f93b847b62c5') -class TestSecurityGroupDefaultRules(test.TestCase): +class TestSecurityGroupDefaultRulesNeutronV2(test.TestCase): + controller_cls = (security_group_default_rules_v2. + SecurityGroupDefaultRulesController) + + +class TestSecurityGroupDefaultRulesV21(test.TestCase): + controller_cls = (security_group_default_rules_v21. + SecurityGroupDefaultRulesController) + def setUp(self): - super(TestSecurityGroupDefaultRules, self).setUp() - self.controller = \ - security_group_default_rules.SecurityGroupDefaultRulesController() + super(TestSecurityGroupDefaultRulesV21, self).setUp() + self.controller = self.controller_cls() def test_create_security_group_default_rule(self): sgr = security_group_default_rule_template() @@ -323,10 +335,15 @@ class TestSecurityGroupDefaultRules(test.TestCase): self.assertEqual(sgr['cidr'], security_group_rule.cidr) +class TestSecurityGroupDefaultRulesV2(test.TestCase): + controller_cls = (security_group_default_rules_v2. + SecurityGroupDefaultRulesController) + + class TestSecurityGroupDefaultRulesXMLDeserializer(test.TestCase): def setUp(self): super(TestSecurityGroupDefaultRulesXMLDeserializer, self).setUp() - deserializer = security_group_default_rules.\ + deserializer = security_group_default_rules_v2.\ SecurityGroupDefaultRulesXMLDeserializer() self.deserializer = deserializer @@ -423,9 +440,9 @@ class TestSecurityGroupDefaultRuleXMLSerializer(test.TestCase): super(TestSecurityGroupDefaultRuleXMLSerializer, self).setUp() self.namespace = wsgi.XMLNS_V11 self.rule_serializer =\ - security_group_default_rules.SecurityGroupDefaultRuleTemplate() + security_group_default_rules_v2.SecurityGroupDefaultRuleTemplate() self.index_serializer =\ - security_group_default_rules.SecurityGroupDefaultRulesTemplate() + security_group_default_rules_v2.SecurityGroupDefaultRulesTemplate() def _tag(self, elem): tagname = elem.tag diff --git a/nova/tests/fake_policy.py b/nova/tests/fake_policy.py index f9c89f3c72..b4c11f8a4e 100644 --- a/nova/tests/fake_policy.py +++ b/nova/tests/fake_policy.py @@ -279,6 +279,7 @@ policy_data = """ "compute_extension:rescue": "", "compute_extension:v3:os-rescue": "", "compute_extension:security_group_default_rules": "", + "compute_extension:v3:os-security-group-default-rules": "", "compute_extension:security_groups": "", "compute_extension:v3:os-security-groups": "", "compute_extension:server_diagnostics": "", diff --git a/nova/tests/integrated/v3/api_samples/os-security-group-default-rules/security-group-default-rules-create-req.json.tpl b/nova/tests/integrated/v3/api_samples/os-security-group-default-rules/security-group-default-rules-create-req.json.tpl new file mode 100644 index 0000000000..8836d0eecc --- /dev/null +++ b/nova/tests/integrated/v3/api_samples/os-security-group-default-rules/security-group-default-rules-create-req.json.tpl @@ -0,0 +1,8 @@ +{ + "security_group_default_rule": { + "ip_protocol": "TCP", + "from_port": "80", + "to_port": "80", + "cidr": "10.10.10.0/24" + } +} \ No newline at end of file diff --git a/nova/tests/integrated/v3/api_samples/os-security-group-default-rules/security-group-default-rules-create-resp.json.tpl b/nova/tests/integrated/v3/api_samples/os-security-group-default-rules/security-group-default-rules-create-resp.json.tpl new file mode 100644 index 0000000000..ae6c62bfd6 --- /dev/null +++ b/nova/tests/integrated/v3/api_samples/os-security-group-default-rules/security-group-default-rules-create-resp.json.tpl @@ -0,0 +1,11 @@ +{ + "security_group_default_rule": { + "from_port": 80, + "id": 1, + "ip_protocol": "TCP", + "ip_range":{ + "cidr": "10.10.10.0/24" + }, + "to_port": 80 + } +} \ No newline at end of file diff --git a/nova/tests/integrated/v3/api_samples/os-security-group-default-rules/security-group-default-rules-list-resp.json.tpl b/nova/tests/integrated/v3/api_samples/os-security-group-default-rules/security-group-default-rules-list-resp.json.tpl new file mode 100644 index 0000000000..c083640c3e --- /dev/null +++ b/nova/tests/integrated/v3/api_samples/os-security-group-default-rules/security-group-default-rules-list-resp.json.tpl @@ -0,0 +1,13 @@ +{ + "security_group_default_rules": [ + { + "from_port": 80, + "id": 1, + "ip_protocol": "TCP", + "ip_range": { + "cidr": "10.10.10.0/24" + }, + "to_port": 80 + } + ] +} \ No newline at end of file diff --git a/nova/tests/integrated/v3/api_samples/os-security-group-default-rules/security-group-default-rules-show-resp.json.tpl b/nova/tests/integrated/v3/api_samples/os-security-group-default-rules/security-group-default-rules-show-resp.json.tpl new file mode 100644 index 0000000000..97b5259a18 --- /dev/null +++ b/nova/tests/integrated/v3/api_samples/os-security-group-default-rules/security-group-default-rules-show-resp.json.tpl @@ -0,0 +1,11 @@ +{ + "security_group_default_rule": { + "id": 1, + "from_port": 80, + "to_port": 80, + "ip_protocol": "TCP", + "ip_range": { + "cidr": "10.10.10.0/24" + } + } +} \ No newline at end of file diff --git a/nova/tests/integrated/v3/test_security_group_default_rules.py b/nova/tests/integrated/v3/test_security_group_default_rules.py new file mode 100644 index 0000000000..99882ce865 --- /dev/null +++ b/nova/tests/integrated/v3/test_security_group_default_rules.py @@ -0,0 +1,40 @@ +# Copyright 2014 IBM Corp. +# +# 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 nova.tests.integrated.v3 import api_sample_base + + +class SecurityGroupDefaultRulesSampleJsonTest( + api_sample_base.ApiSampleTestBaseV3): + extension_name = 'os-security-group-default-rules' + + def test_security_group_default_rules_create(self): + response = self._do_post('os-security-group-default-rules', + 'security-group-default-rules-create-req', + {}) + self._verify_response('security-group-default-rules-create-resp', + {}, response, 200) + + def test_security_group_default_rules_list(self): + self.test_security_group_default_rules_create() + response = self._do_get('os-security-group-default-rules') + self._verify_response('security-group-default-rules-list-resp', + {}, response, 200) + + def test_security_group_default_rules_show(self): + self.test_security_group_default_rules_create() + rule_id = '1' + response = self._do_get('os-security-group-default-rules/%s' % rule_id) + self._verify_response('security-group-default-rules-show-resp', + {}, response, 200) diff --git a/setup.cfg b/setup.cfg index 65da974910..ccc350c3dc 100644 --- a/setup.cfg +++ b/setup.cfg @@ -114,6 +114,7 @@ nova.api.v3.extensions = remote_consoles = nova.api.openstack.compute.plugins.v3.remote_consoles:RemoteConsoles rescue = nova.api.openstack.compute.plugins.v3.rescue:Rescue scheduler_hints = nova.api.openstack.compute.plugins.v3.scheduler_hints:SchedulerHints + security_group_default_rules = nova.api.openstack.compute.plugins.v3.security_group_default_rules:SecurityGroupDefaultRules security_groups = nova.api.openstack.compute.plugins.v3.security_groups:SecurityGroups server_diagnostics = nova.api.openstack.compute.plugins.v3.server_diagnostics:ServerDiagnostics server_external_events = nova.api.openstack.compute.plugins.v3.server_external_events:ServerExternalEvents