Default SG rules for the Security Group "Default"

Added in the API os-security-group-default-rules

This allows create, delete, list, and get (of individual rules) for
rules that will be pre-populated into the Security Group "default"
that is populated in all projects on creation.

These rules will not be applied retroactively, as it is designed
to allow the creation of a "reasonable" base-line set of sg rules.

The new rules live in a separate table that mirrors the relevant
structures of the security_group_rules table.

Added unit tests/API samples for the new API calls

Related to bp default-rules-for-default-security-group

DocImpact

Change-Id: I7ab51e68aff562bb869538197a0eca158fc3220c
This commit is contained in:
Morgan Fainberg 2013-02-06 15:39:54 -08:00 committed by Gerrit Code Review
parent 30c2a8f66e
commit 59aaf1dff9
32 changed files with 1089 additions and 1 deletions

View File

@ -384,6 +384,14 @@
"namespace": "http://docs.openstack.org/compute/ext/rescue/api/v1.1",
"updated": "2011-08-18T00:00:00+00:00"
},
{
"alias": "os-security-group-default-rules",
"description": "Default rules for security group support.",
"links": [],
"name": "SecurityGroupDefaultRules",
"namespace": "http://docs.openstack.org/compute/ext/securitygroupdefaultrules/api/v1.1",
"updated": "2013-02-05T00:00:00+00:00"
},
{
"alias": "os-security-groups",
"description": "Security group support.",

View File

@ -162,6 +162,9 @@
<extension alias="os-rescue" updated="2011-08-18T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/rescue/api/v1.1" name="Rescue">
<description>Instance rescue mode.</description>
</extension>
<extension alias="os-security-group-default-rules" updated="2013-02-05T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/securitygroupdefaultrules/api/v1.1" name="SecurityGroupDefaultRules">
<description>Default rules for security group support.</description>
</extension>
<extension alias="os-security-groups" updated="2011-07-21T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/securitygroups/api/v1.1" name="SecurityGroups">
<description>Security group support.</description>
</extension>

View File

@ -0,0 +1,8 @@
{
"security_group_default_rule": {
"ip_protocol": "TCP",
"from_port": "80",
"to_port": "80",
"cidr": "10.10.12.0/24"
}
}

View File

@ -0,0 +1,7 @@
<?xml version='1.0' encoding='UTF-8'?>
<security_group_default_rule>
<ip_protocol>TCP</ip_protocol>
<from_port>80</from_port>
<to_port>80</to_port>
<cidr>10.10.12.0/24</cidr>
</security_group_default_rule>

View File

@ -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
}
}

View File

@ -0,0 +1,9 @@
<?xml version='1.0' encoding='UTF-8'?>
<security_group_default_rule xmlns="http://docs.openstack.org/compute/api/v1.1" id="1">
<ip_protocol>TCP</ip_protocol>
<from_port>80</from_port>
<to_port>80</to_port>
<ip_range>
<cidr>10.10.10.0/24</cidr>
</ip_range>
</security_group_default_rule>

View File

@ -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
}
]
}

View File

@ -0,0 +1,11 @@
<?xml version='1.0' encoding='UTF-8'?>
<security_group_default_rules xmlns="http://docs.openstack.org/compute/api/v1.1">
<security_group_default_rule id="1">
<ip_protocol>TCP</ip_protocol>
<from_port>80</from_port>
<to_port>80</to_port>
<ip_range>
<cidr>10.10.10.0/24</cidr>
</ip_range>
</security_group_default_rule>
</security_group_default_rules>

View File

@ -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"
}
}
}

View File

@ -0,0 +1,9 @@
<?xml version='1.0' encoding='UTF-8'?>
<security_group_default_rule xmlns="http://docs.openstack.org/compute/api/v1.1" id="1">
<from_port>80</from_port>
<to_port>80</to_port>
<ip_protocol>TCP</ip_protocol>
<ip_range>
<cidr>10.10.10.0/24</cidr>
</ip_range>
</security_group_default_rule>

View File

@ -78,6 +78,7 @@
"compute_extension:quotas:update": "rule:admin_api",
"compute_extension:quota_classes": "",
"compute_extension:rescue": "",
"compute_extension:security_group_default_rules": "rule:admin_api",
"compute_extension:security_groups": "",
"compute_extension:server_diagnostics": "rule:admin_api",
"compute_extension:server_password": "",

View File

@ -0,0 +1,210 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# 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 xml.dom import minidom
import webob
from webob import exc
from nova.api.openstack.compute.contrib import security_groups as sg
from nova.api.openstack import extensions
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
from nova import exception
from nova.openstack.common import log as logging
LOG = logging.getLogger(__name__)
authorize = extensions.extension_authorizer('compute',
'security_group_default_rules')
sg_nsmap = {None: wsgi.XMLNS_V11}
def make_default_rule(elem):
elem.set('id')
proto = xmlutil.SubTemplateElement(elem, 'ip_protocol')
proto.text = 'ip_protocol'
from_port = xmlutil.SubTemplateElement(elem, 'from_port')
from_port.text = 'from_port'
to_port = xmlutil.SubTemplateElement(elem, 'to_port')
to_port.text = 'to_port'
ip_range = xmlutil.SubTemplateElement(elem, 'ip_range',
selector='ip_range')
cidr = xmlutil.SubTemplateElement(ip_range, 'cidr')
cidr.text = 'cidr'
class SecurityGroupDefaultRulesTemplate(xmlutil.TemplateBuilder):
def construct(self):
root = xmlutil.TemplateElement('security_group_default_rules')
elem = xmlutil.SubTemplateElement(root, 'security_group_default_rule',
selector='security_group_default_rules')
make_default_rule(elem)
return xmlutil.MasterTemplate(root, 1, nsmap=sg_nsmap)
class SecurityGroupDefaultRuleTemplate(xmlutil.TemplateBuilder):
def construct(self):
root = xmlutil.TemplateElement('security_group_default_rule',
selector='security_group_default_rule')
make_default_rule(root)
return xmlutil.MasterTemplate(root, 1, nsmap=sg_nsmap)
class SecurityGroupDefaultRulesXMLDeserializer(wsgi.MetadataXMLDeserializer):
def default(self, string):
dom = minidom.parseString(string)
security_group_rule = self._extract_security_group_default_rule(dom)
return {'body': {'security_group_default_rule': security_group_rule}}
def _extract_security_group_default_rule(self, node):
sg_rule = {}
sg_rule_node = self.find_first_child_named(node,
'security_group_default_rule')
if sg_rule_node is not None:
ip_protocol_node = self.find_first_child_named(sg_rule_node,
"ip_protocol")
if ip_protocol_node is not None:
sg_rule['ip_protocol'] = self.extract_text(ip_protocol_node)
from_port_node = self.find_first_child_named(sg_rule_node,
"from_port")
if from_port_node is not None:
sg_rule['from_port'] = self.extract_text(from_port_node)
to_port_node = self.find_first_child_named(sg_rule_node, "to_port")
if to_port_node is not None:
sg_rule['to_port'] = self.extract_text(to_port_node)
cidr_node = self.find_first_child_named(sg_rule_node, "cidr")
if cidr_node is not None:
sg_rule['cidr'] = self.extract_text(cidr_node)
return sg_rule
class SecurityGroupDefaultRulesController(sg.SecurityGroupControllerBase):
@wsgi.serializers(xml=SecurityGroupDefaultRuleTemplate)
@wsgi.deserializers(xml=SecurityGroupDefaultRulesXMLDeserializer)
def create(self, req, body):
context = self._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 as exp:
raise exc.HTTPBadRequest(explanation=unicode(exp))
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)
@wsgi.serializers(xml=SecurityGroupDefaultRuleTemplate)
def show(self, req, id):
context = self._authorize_context(req)
authorize(context)
id = self._validate_id(id)
LOG.debug(_("Showing security_group_default_rule with id %s") % id)
try:
rule = self.security_group_api.get_default_rule(context, id)
except exception.SecurityGroupDefaultRuleNotFound:
raise exc.HTTPNotFound(_("security group default rule not found"))
fmt_rule = self._format_security_group_default_rule(rule)
return {"security_group_default_rule": fmt_rule}
def delete(self, req, id):
context = self._authorize_context(req)
authorize(context)
id = self._validate_id(id)
rule = self.security_group_api.get_default_rule(context, id)
self.security_group_api.remove_default_rules(context, [rule['id']])
return webob.Response(status_int=204)
@wsgi.serializers(xml=SecurityGroupDefaultRulesTemplate)
def index(self, req):
context = self._authorize_context(req)
authorize(context)
ret = {'security_group_default_rules': []}
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)
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 Security_group_default_rules(extensions.ExtensionDescriptor):
"""Default rules for security group support."""
name = "SecurityGroupDefaultRules"
alias = "os-security-group-default-rules"
namespace = ("http://docs.openstack.org/compute/ext/"
"securitygroupdefaultrules/api/v1.1")
updated = "2013-02-05T00:00:00+00:00"
def get_resources(self):
resources = [
extensions.ResourceExtension('os-security-group-default-rules',
SecurityGroupDefaultRulesController(),
collection_actions={'create': 'POST',
'delete': 'DELETE',
'index': 'GET'},
member_actions={'show': 'GET'})]
return resources

View File

@ -3128,6 +3128,46 @@ class SecurityGroupAPI(base.Base):
self.trigger_rules_refresh(context, id=security_group['id'])
self.trigger_handler('security_group_rule_destroy', context, rule_ids)
def remove_default_rules(self, context, rule_ids):
for rule_id in rule_ids:
self.db.security_group_default_rule_destroy(context, rule_id)
def add_default_rules(self, context, vals):
rules = [self.db.security_group_default_rule_create(context, v)
for v in vals]
return rules
def default_rule_exists(self, context, values):
"""Indicates whether the specified rule values are already
defined in the default security group rules.
"""
for rule in self.db.security_group_default_rule_list(context):
is_duplicate = True
keys = ('cidr', 'from_port', 'to_port', 'protocol')
for key in keys:
if rule.get(key) != values.get(key):
is_duplicate = False
break
if is_duplicate:
return rule.get('id') or True
return False
def get_all_default_rules(self, context):
try:
rules = self.db.security_group_default_rule_list(context)
except Exception:
msg = 'cannot get default security group rules'
raise exception.SecurityGroupDefaultRuleNotFound(msg)
return rules
def get_default_rule(self, context, id):
try:
return self.db.security_group_default_rule_get(context, id)
except exception.NotFound:
msg = _("Rule (%s) not found") % id
self.raise_not_found(msg)
@staticmethod
def raise_invalid_property(msg):
raise NotImplementedError()

View File

@ -1206,6 +1206,28 @@ def security_group_rule_count_by_group(context, security_group_id):
###################
def security_group_default_rule_get(context, security_group_rule_default_id):
return IMPL.security_group_default_rule_get(context,
security_group_rule_default_id)
def security_group_default_rule_destroy(context,
security_group_rule_default_id):
return IMPL.security_group_default_rule_destroy(
context, security_group_rule_default_id)
def security_group_default_rule_create(context, values):
return IMPL.security_group_default_rule_create(context, values)
def security_group_default_rule_list(context):
return IMPL.security_group_default_rule_list(context)
###################
def provider_fw_rule_create(context, rule):
"""Add a firewall rule at the provider level (all hosts & instances)."""
return IMPL.provider_fw_rule_create(context, rule)

View File

@ -3180,6 +3180,16 @@ def security_group_ensure_default(context, session=None):
'project_id': context.project_id}
default_group = security_group_create(context, values,
session=session)
for default_rule in security_group_default_rule_list(context):
# This is suboptimal, it should be programmatic to know
# the values of the default_rule
rule_values = {'protocol': default_rule.protocol,
'from_port': default_rule.from_port,
'to_port': default_rule.to_port,
'cidr': default_rule.cidr,
'parent_group_id': default_group.id,
}
security_group_rule_create(context, rule_values)
return (False, default_group)
@ -3280,6 +3290,56 @@ def security_group_rule_count_by_group(context, security_group_id):
###################
def _security_group_rule_get_default_query(context, session=None):
return model_query(context, models.SecurityGroupIngressDefaultRule,
session=session)
@require_context
def security_group_default_rule_get(context, security_group_rule_default_id,
session=None):
result = _security_group_rule_get_default_query(context, session=session).\
filter_by(id=security_group_rule_default_id).\
first()
if not result:
raise exception.SecurityGroupDefaultRuleNotFound(
rule_id=security_group_rule_default_id)
return result
@require_admin_context
def security_group_default_rule_destroy(context,
security_group_rule_default_id):
session = get_session()
with session.begin():
count = _security_group_rule_get_default_query(context,
session=session).\
filter_by(id=security_group_rule_default_id).\
soft_delete()
if count == 0:
raise exception.SecurityGroupDefaultRuleNotFound(
rule_id=security_group_rule_default_id)
@require_admin_context
def security_group_default_rule_create(context, values):
security_group_default_rule_ref = models.SecurityGroupIngressDefaultRule()
security_group_default_rule_ref.update(values)
security_group_default_rule_ref.save()
return security_group_default_rule_ref
@require_context
def security_group_default_rule_list(context, session=None):
return _security_group_rule_get_default_query(context, session=session).\
all()
###################
@require_admin_context
def provider_fw_rule_create(context, rule):
fw_rule_ref = models.ProviderFirewallRule()

View File

@ -0,0 +1,61 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# 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 sqlalchemy import Column, DateTime, Integer, MetaData, String, Table
from nova.db.sqlalchemy import types
from nova.openstack.common import log as logging
LOG = logging.getLogger(__name__)
def upgrade(migrate_engine):
meta = MetaData()
meta.bind = migrate_engine
security_group_default_rules = Table('security_group_default_rules', meta,
Column('created_at', DateTime),
Column('updated_at', DateTime),
Column('deleted_at', DateTime),
Column('deleted', Integer, default=0),
Column('id', Integer, primary_key=True, nullable=False),
Column('protocol', String(length=5)),
Column('from_port', Integer),
Column('to_port', Integer),
Column('cidr', types.CIDR()),
mysql_engine='InnoDB',
mysql_charset='utf8',
)
try:
security_group_default_rules.create()
except Exception:
msg = "Exception while creating table 'security_group_default_rules"
LOG.exception(msg)
raise
def downgrade(migrate_engine):
meta = MetaData()
meta.bind = migrate_engine
security_group_default_rules = Table('security_group_default_rules',
meta,
autoload=True)
try:
security_group_default_rules.drop()
except Exception:
msg = "Exception while droppping table 'security_group_default_rules'"
LOG.exception(msg)
raise

View File

@ -541,6 +541,15 @@ class SecurityGroupIngressRule(BASE, NovaBase):
'SecurityGroupIngressRule.deleted == 0)')
class SecurityGroupIngressDefaultRule(BASE, NovaBase):
__tablename__ = 'security_group_default_rules'
id = Column(Integer, primary_key=True)
protocol = Column(String(5)) # "tcp", "udp" or "icmp"
from_port = Column(Integer)
to_port = Column(Integer)
cidr = Column(types.CIDR())
class ProviderFirewallRule(BASE, NovaBase):
"""Represents a rule in a security group."""
__tablename__ = 'provider_fw_rules'

View File

@ -732,6 +732,10 @@ class SecurityGroupNotExistsForInstance(Invalid):
" the instance %(instance_id)s")
class SecurityGroupDefaultRuleNotFound(Invalid):
message = _("Security group default rule (%rule_id)s not found.")
class MigrationNotFound(NotFound):
message = _("Migration %(migration_id)s could not be found.")

View File

@ -0,0 +1,467 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# 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 lxml import etree
import webob
from nova.api.openstack.compute.contrib import security_group_default_rules
from nova.api.openstack import wsgi
from nova import context
import nova.db
from nova.openstack.common import cfg
from nova import test
from nova.tests.api.openstack import fakes
CONF = cfg.CONF
class AttrDict(dict):
def __getattr__(self, k):
return self[k]
def security_group_default_rule_template(**kwargs):
rule = kwargs.copy()
rule.setdefault('ip_protocol', 'TCP')
rule.setdefault('from_port', 22)
rule.setdefault('to_port', 22)
rule.setdefault('cidr', '10.10.10.0/24')
return rule
def security_group_default_rule_db(security_group_default_rule, id=None):
attrs = security_group_default_rule.copy()
if id is not None:
attrs['id'] = id
return AttrDict(attrs)
class TestSecurityGroupDefaultRules(test.TestCase):
def setUp(self):
super(TestSecurityGroupDefaultRules, self).setUp()
self.controller = \
security_group_default_rules.SecurityGroupDefaultRulesController()
def test_create_security_group_default_rule(self):
sgr = security_group_default_rule_template()
req = fakes.HTTPRequest.blank(
'/v2/fake/os-security-group-default-rules', use_admin_context=True)
sgr_dict = dict(security_group_default_rule=sgr)
res_dict = self.controller.create(req, sgr_dict)
security_group_default_rule = res_dict['security_group_default_rule']
self.assertEqual(security_group_default_rule['ip_protocol'],
sgr['ip_protocol'])
self.assertEqual(security_group_default_rule['from_port'],
sgr['from_port'])
self.assertEqual(security_group_default_rule['to_port'],
sgr['to_port'])
self.assertEqual(security_group_default_rule['ip_range']['cidr'],
sgr['cidr'])
def test_create_security_group_default_rule_with_no_to_port(self):
sgr = security_group_default_rule_template()
del sgr['to_port']
req = fakes.HTTPRequest.blank(
'/v2/fake/os-security-group-default-rules', use_admin_context=True)
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
req, {'security_group_default_rule': sgr})
def test_create_security_group_default_rule_with_no_from_port(self):
sgr = security_group_default_rule_template()
del sgr['from_port']
req = fakes.HTTPRequest.blank(
'/v2/fake/os-security-group-default-rules', use_admin_context=True)
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
req, {'security_group_default_rule': sgr})
def test_create_security_group_default_rule_with_no_ip_protocol(self):
sgr = security_group_default_rule_template()
del sgr['ip_protocol']
req = fakes.HTTPRequest.blank(
'/v2/fake/os-security-group-default-rules', use_admin_context=True)
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
req, {'security_group_default_rule': sgr})
def test_create_security_group_default_rule_with_no_cidr(self):
sgr = security_group_default_rule_template()
del sgr['cidr']
req = fakes.HTTPRequest.blank(
'/v2/fake/os-security-group-default-rules', use_admin_context=True)
res_dict = self.controller.create(req,
{'security_group_default_rule': sgr})
security_group_default_rule = res_dict['security_group_default_rule']
self.assertNotEquals(security_group_default_rule['id'], 0)
self.assertEquals(security_group_default_rule['ip_range']['cidr'],
'0.0.0.0/0')
def test_create_security_group_default_rule_with_blank_to_port(self):
sgr = security_group_default_rule_template(to_port='')
req = fakes.HTTPRequest.blank(
'/v2/fake/os-security-group-default-rules', use_admin_context=True)
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
req, {'security_group_default_rule': sgr})
def test_create_security_group_default_rule_with_blank_from_port(self):
sgr = security_group_default_rule_template(from_port='')
req = fakes.HTTPRequest.blank(
'/v2/fake/os-security-group-default-rules', use_admin_context=True)
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
req, {'security_group_default_rule': sgr})
def test_create_security_group_default_rule_with_blank_ip_protocol(self):
sgr = security_group_default_rule_template(ip_protocol='')
req = fakes.HTTPRequest.blank(
'/v2/fake/os-security-group-default-rules', use_admin_context=True)
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
req, {'security_group_default_rule': sgr})
def test_create_security_group_default_rule_with_blank_cidr(self):
sgr = security_group_default_rule_template(cidr='')
req = fakes.HTTPRequest.blank(
'/v2/fake/os-security-group-default-rules', use_admin_context=True)
res_dict = self.controller.create(req,
{'security_group_default_rule': sgr})
security_group_default_rule = res_dict['security_group_default_rule']
self.assertNotEquals(security_group_default_rule['id'], 0)
self.assertEquals(security_group_default_rule['ip_range']['cidr'],
'0.0.0.0/0')
def test_create_security_group_default_rule_non_numerical_to_port(self):
sgr = security_group_default_rule_template(to_port='invalid')
req = fakes.HTTPRequest.blank(
'/v2/fake/os-security-group-default-rules', use_admin_context=True)
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
req, {'security_group_default_rule': sgr})
def test_create_security_group_default_rule_non_numerical_from_port(self):
sgr = security_group_default_rule_template(from_port='invalid')
req = fakes.HTTPRequest.blank(
'/v2/fake/os-security-group-default-rules', use_admin_context=True)
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
req, {'security_group_default_rule': sgr})
def test_create_security_group_default_rule_invalid_ip_protocol(self):
sgr = security_group_default_rule_template(ip_protocol='invalid')
req = fakes.HTTPRequest.blank(
'/v2/fake/os-security-group-default-rules', use_admin_context=True)
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
req, {'security_group_default_rule': sgr})
def test_create_security_group_default_rule_invalid_cidr(self):
sgr = security_group_default_rule_template(cidr='10.10.2222.0/24')
req = fakes.HTTPRequest.blank(
'/v2/fake/os-security-group-default-rules', use_admin_context=True)
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
req, {'security_group_default_rule': sgr})
def test_create_security_group_default_rule_invalid_to_port(self):
sgr = security_group_default_rule_template(to_port='666666')
req = fakes.HTTPRequest.blank(
'/v2/fake/os-security-group-default-rules', use_admin_context=True)
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
req, {'security_group_default_rule': sgr})
def test_create_security_group_default_rule_invalid_from_port(self):
sgr = security_group_default_rule_template(from_port='666666')
req = fakes.HTTPRequest.blank(
'/v2/fake/os-security-group-default-rules', use_admin_context=True)
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
req, {'security_group_default_rule': sgr})
def test_create_security_group_default_rule_with_no_body(self):
req = fakes.HTTPRequest.blank(
'/v2/fake/os-security-group-default-rules', use_admin_context=True)
self.assertRaises(webob.exc.HTTPUnprocessableEntity,
self.controller.create, req, None)
def test_create_duplicate_security_group_default_rule(self):
sgr = security_group_default_rule_template()
req = fakes.HTTPRequest.blank(
'/v2/fake/os-security-group-default-rules', use_admin_context=True)
self.controller.create(req, {'security_group_default_rule': sgr})
req = fakes.HTTPRequest.blank(
'/v2/fake/os-security-group-default-rules', use_admin_context=True)
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
req, {'security_group_default_rule': sgr})
def test_security_group_default_rules_list(self):
self.test_create_security_group_default_rule()
rules = [dict(id=1,
ip_protocol='TCP',
from_port=22,
to_port=22,
ip_range=dict(cidr='10.10.10.0/24'))]
expected = {'security_group_default_rules': rules}
req = fakes.HTTPRequest.blank(
'/v2/fake/os-security-group-default-rules', use_admin_context=True)
res_dict = self.controller.index(req)
self.assertEqual(res_dict, expected)
def test_default_security_group_default_rule_show(self):
sgr = security_group_default_rule_template(id=1)
self.test_create_security_group_default_rule()
req = fakes.HTTPRequest.blank(
'/v2/fake/os-security-group-default-rules', use_admin_context=True)
res_dict = self.controller.show(req, '1')
security_group_default_rule = res_dict['security_group_default_rule']
self.assertEqual(security_group_default_rule['ip_protocol'],
sgr['ip_protocol'])
self.assertEqual(security_group_default_rule['to_port'],
sgr['to_port'])
self.assertEqual(security_group_default_rule['from_port'],
sgr['from_port'])
self.assertEqual(security_group_default_rule['ip_range']['cidr'],
sgr['cidr'])
def test_delete_security_group_default_rule(self):
sgr = security_group_default_rule_template(id=1)
self.test_create_security_group_default_rule()
self.called = False
def security_group_default_rule_destroy(context, id):
self.called = True
def return_security_group_default_rule(context, id):
self.assertEquals(sgr['id'], id)
return security_group_default_rule_db(sgr)
self.stubs.Set(nova.db, 'security_group_default_rule_destroy',
security_group_default_rule_destroy)
self.stubs.Set(nova.db, 'security_group_default_rule_get',
return_security_group_default_rule)
req = fakes.HTTPRequest.blank(
'/v2/fake/os-security-group-default-rules', use_admin_context=True)
self.controller.delete(req, '1')
self.assertTrue(self.called)
def test_security_group_ensure_default(self):
sgr = security_group_default_rule_template(id=1)
self.test_create_security_group_default_rule()
ctxt = context.get_admin_context()
setattr(ctxt, 'project_id', 'new_project_id')
_, sg = nova.db.security_group_ensure_default(ctxt)
rules = nova.db.security_group_rule_get_by_security_group(ctxt, sg.id)
security_group_rule = rules[0]
self.assertEqual(sgr['id'], security_group_rule.id)
self.assertEqual(sgr['ip_protocol'], security_group_rule.protocol)
self.assertEqual(sgr['from_port'], security_group_rule.from_port)
self.assertEqual(sgr['to_port'], security_group_rule.to_port)
self.assertEqual(sgr['cidr'], security_group_rule.cidr)
class TestSecurityGroupDefaultRulesXMLDeserializer(test.TestCase):
def setUp(self):
super(TestSecurityGroupDefaultRulesXMLDeserializer, self).setUp()
deserializer = security_group_default_rules.\
SecurityGroupDefaultRulesXMLDeserializer()
self.deserializer = deserializer
def test_create_request(self):
serial_request = """
<security_group_default_rule>
<from_port>22</from_port>
<to_port>22</to_port>
<ip_protocol>TCP</ip_protocol>
<cidr>10.10.10.0/24</cidr>
</security_group_default_rule>"""
request = self.deserializer.deserialize(serial_request)
expected = {
"security_group_default_rule": {
"from_port": "22",
"to_port": "22",
"ip_protocol": "TCP",
"cidr": "10.10.10.0/24"
},
}
self.assertEqual(request['body'], expected)
def test_create_no_to_port_request(self):
serial_request = """
<security_group_default_rule>
<from_port>22</from_port>
<ip_protocol>TCP</ip_protocol>
<cidr>10.10.10.0/24</cidr>
</security_group_default_rule>"""
request = self.deserializer.deserialize(serial_request)
expected = {
"security_group_default_rule": {
"from_port": "22",
"ip_protocol": "TCP",
"cidr": "10.10.10.0/24"
},
}
self.assertEqual(request['body'], expected)
def test_create_no_from_port_request(self):
serial_request = """
<security_group_default_rule>
<to_port>22</to_port>
<ip_protocol>TCP</ip_protocol>
<cidr>10.10.10.0/24</cidr>
</security_group_default_rule>"""
request = self.deserializer.deserialize(serial_request)
expected = {
"security_group_default_rule": {
"to_port": "22",
"ip_protocol": "TCP",
"cidr": "10.10.10.0/24"
},
}
self.assertEqual(request['body'], expected)
def test_create_no_ip_protocol_request(self):
serial_request = """
<security_group_default_rule>
<from_port>22</from_port>
<to_port>22</to_port>
<cidr>10.10.10.0/24</cidr>
</security_group_default_rule>"""
request = self.deserializer.deserialize(serial_request)
expected = {
"security_group_default_rule": {
"from_port": "22",
"to_port": "22",
"cidr": "10.10.10.0/24"
},
}
self.assertEqual(request['body'], expected)
def test_create_no_cidr_request(self):
serial_request = """
<security_group_default_rule>
<from_port>22</from_port>
<to_port>22</to_port>
<ip_protocol>TCP</ip_protocol>
</security_group_default_rule>"""
request = self.deserializer.deserialize(serial_request)
expected = {
"security_group_default_rule": {
"from_port": "22",
"to_port": "22",
"ip_protocol": "TCP",
},
}
self.assertEqual(request['body'], expected)
class TestSecurityGroupDefaultRuleXMLSerializer(test.TestCase):
def setUp(self):
super(TestSecurityGroupDefaultRuleXMLSerializer, self).setUp()
self.namespace = wsgi.XMLNS_V11
self.rule_serializer =\
security_group_default_rules.SecurityGroupDefaultRuleTemplate()
self.index_serializer =\
security_group_default_rules.SecurityGroupDefaultRulesTemplate()
def _tag(self, elem):
tagname = elem.tag
self.assertEqual(tagname[0], '{')
tmp = tagname.partition('}')
namespace = tmp[0][1:]
self.assertEqual(namespace, self.namespace)
return tmp[2]
def _verify_security_group_default_rule(self, raw_rule, tree):
self.assertEqual(raw_rule['id'], tree.get('id'))
seen = set()
expected = set(['ip_protocol', 'from_port', 'to_port', 'ip_range',
'ip_range/cidr'])
for child in tree:
child_tag = self._tag(child)
seen.add(child_tag)
if child_tag == 'ip_range':
for gr_child in child:
gr_child_tag = self._tag(gr_child)
self.assertTrue(gr_child_tag in raw_rule[child_tag])
seen.add('%s/%s' % (child_tag, gr_child_tag))
self.assertEqual(gr_child.text,
raw_rule[child_tag][gr_child_tag])
else:
self.assertEqual(child.text, raw_rule[child_tag])
self.assertEqual(seen, expected)
def test_rule_serializer(self):
raw_rule = dict(id='123',
ip_protocol='TCP',
from_port='22',
to_port='22',
ip_range=dict(cidr='10.10.10.0/24'))
rule = dict(security_group_default_rule=raw_rule)
text = self.rule_serializer.serialize(rule)
tree = etree.fromstring(text)
self.assertEqual('security_group_default_rule', self._tag(tree))
self._verify_security_group_default_rule(raw_rule, tree)
def test_index_serializer(self):
rules = [dict(id='123',
ip_protocol='TCP',
from_port='22',
to_port='22',
ip_range=dict(cidr='10.10.10.0/24')),
dict(id='234',
ip_protocol='UDP',
from_port='23456',
to_port='234567',
ip_range=dict(cidr='10.12.0.0/18')),
dict(id='345',
ip_protocol='tcp',
from_port='3456',
to_port='4567',
ip_range=dict(cidr='192.168.1.0/32'))]
rules_dict = dict(security_group_default_rules=rules)
text = self.index_serializer.serialize(rules_dict)
tree = etree.fromstring(text)
self.assertEqual('security_group_default_rules', self._tag(tree))
self.assertEqual(len(rules), len(tree))
for idx, child in enumerate(tree):
self._verify_security_group_default_rule(rules[idx], child)

View File

@ -193,6 +193,7 @@ class ExtensionControllerTest(ExtensionTestCase):
"Quotas",
"Rescue",
"SchedulerHints",
"SecurityGroupDefaultRules",
"SecurityGroups",
"ServerDiagnostics",
"ServerPassword",

View File

@ -156,6 +156,7 @@ policy_data = """
"compute_extension:quotas:update": "",
"compute_extension:quota_classes": "",
"compute_extension:rescue": "",
"compute_extension:security_group_default_rules": "",
"compute_extension:security_groups": "",
"compute_extension:server_diagnostics": "",
"compute_extension:server_password": "",

View File

@ -392,6 +392,14 @@
"namespace": "http://docs.openstack.org/compute/ext/rescue/api/v1.1",
"updated": "%(timestamp)s"
},
{
"alias": "os-security-group-default-rules",
"description": "%(text)s",
"links": [],
"name": "SecurityGroupDefaultRules",
"namespace": "http://docs.openstack.org/compute/ext/securitygroupdefaultrules/api/v1.1",
"updated": "%(timestamp)s"
},
{
"alias": "os-security-groups",
"description": "%(text)s",

View File

@ -147,6 +147,9 @@
<extension alias="os-rescue" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/rescue/api/v1.1" name="Rescue">
<description>%(text)s</description>
</extension>
<extension alias="os-security-group-default-rules" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/securitygroupdefaultrules/api/v1.1" name="SecurityGroupDefaultRules">
<description>%(text)s</description>
</extension>
<extension alias="os-security-groups" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/securitygroups/api/v1.1" name="SecurityGroups">
<description>%(text)s</description>
</extension>

View File

@ -0,0 +1,8 @@
{
"security_group_default_rule": {
"ip_protocol": "TCP",
"from_port": "80",
"to_port": "80",
"cidr": "10.10.10.0/24"
}
}

View File

@ -0,0 +1,7 @@
<?xml version='1.0' encoding='UTF-8'?>
<security_group_default_rule>
<ip_protocol>TCP</ip_protocol>
<from_port>80</from_port>
<to_port>80</to_port>
<cidr>10.10.10.0/24</cidr>
</security_group_default_rule>

View File

@ -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
}
}

View File

@ -0,0 +1,9 @@
<?xml version='1.0' encoding='UTF-8'?>
<security_group_default_rule xmlns="http://docs.openstack.org/compute/api/v1.1" id="1">
<ip_protocol>TCP</ip_protocol>
<from_port>80</from_port>
<to_port>80</to_port>
<ip_range>
<cidr>10.10.10.0/24</cidr>
</ip_range>
</security_group_default_rule>

View File

@ -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
}
]
}

View File

@ -0,0 +1,11 @@
<?xml version='1.0' encoding='UTF-8'?>
<security_group_default_rules xmlns="http://docs.openstack.org/compute/api/v1.1">
<security_group_default_rule id="1">
<ip_protocol>TCP</ip_protocol>
<from_port>80</from_port>
<to_port>80</to_port>
<ip_range>
<cidr>10.10.10.0/24</cidr>
</ip_range>
</security_group_default_rule>
</security_group_default_rules>

View File

@ -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"
}
}
}

View File

@ -0,0 +1,9 @@
<?xml version='1.0' encoding='UTF-8'?>
<security_group_default_rule xmlns="http://docs.openstack.org/compute/api/v1.1" id="1">
<from_port>80</from_port>
<to_port>80</to_port>
<ip_protocol>TCP</ip_protocol>
<ip_range>
<cidr>10.10.10.0/24</cidr>
</ip_range>
</security_group_default_rule>

View File

@ -1127,7 +1127,39 @@ class SecurityGroupsSampleJsonTest(ServersSampleBase):
subs, response)
class SecurityGroupsSampleXmlTest(SecurityGroupsSampleJsonTest):
class SecurityGroupsSampleXmlTest(ApiSampleTestBase):
ctype = 'xml'
class SecurityGroupDefaultRulesSampleJsonTest(ServersSampleBase):
extension_name = ('nova.api.openstack.compute.contrib'
'.security_group_default_rules'
'.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.assertEqual(response.status, 200)
return self._verify_response(
'security-group-default-rules-create-resp', {}, response)
def test_security_group_default_rules_list(self):
self.test_security_group_default_rules_create()
response = self._do_get('os-security-group-default-rules')
return self._verify_response('security-group-default-rules-list-resp',
{}, response)
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)
return self._verify_response('security-group-default-rules-show-resp',
{}, response)
class SecurityGroupDefaultRulesSampleXmlTest(
SecurityGroupDefaultRulesSampleJsonTest):
ctype = 'xml'