Merge "Port security groups extension to v3 API Part 2"
This commit is contained in:
@@ -123,6 +123,7 @@
|
||||
"compute_extension:v3:os-rescue": "",
|
||||
"compute_extension:security_group_default_rules": "rule:admin_api",
|
||||
"compute_extension:security_groups": "",
|
||||
"compute_extension:v3:os-security-groups": "",
|
||||
"compute_extension:server_diagnostics": "rule:admin_api",
|
||||
"compute_extension:v3:os-server-diagnostics": "rule:admin_api",
|
||||
"compute_extension:server_password": "",
|
||||
|
||||
@@ -22,7 +22,6 @@ import webob
|
||||
from webob import exc
|
||||
from xml.dom import minidom
|
||||
|
||||
from nova.api.openstack import common
|
||||
from nova.api.openstack import extensions
|
||||
from nova.api.openstack import wsgi
|
||||
from nova.api.openstack import xmlutil
|
||||
@@ -31,49 +30,11 @@ from nova.compute import api as compute_api
|
||||
from nova import exception
|
||||
from nova.network.security_group import openstack_driver
|
||||
from nova.network.security_group import quantum_driver
|
||||
from nova.virt import netutils
|
||||
|
||||
|
||||
authorize = extensions.extension_authorizer('compute', 'security_groups')
|
||||
softauth = extensions.soft_extension_authorizer('compute', 'security_groups')
|
||||
|
||||
|
||||
def make_rule(elem):
|
||||
elem.set('id')
|
||||
elem.set('parent_group_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'
|
||||
|
||||
group = xmlutil.SubTemplateElement(elem, 'group', selector='group')
|
||||
name = xmlutil.SubTemplateElement(group, 'name')
|
||||
name.text = 'name'
|
||||
tenant_id = xmlutil.SubTemplateElement(group, 'tenant_id')
|
||||
tenant_id.text = 'tenant_id'
|
||||
|
||||
ip_range = xmlutil.SubTemplateElement(elem, 'ip_range',
|
||||
selector='ip_range')
|
||||
cidr = xmlutil.SubTemplateElement(ip_range, 'cidr')
|
||||
cidr.text = 'cidr'
|
||||
|
||||
|
||||
def make_sg(elem):
|
||||
elem.set('id')
|
||||
elem.set('tenant_id')
|
||||
elem.set('name')
|
||||
|
||||
desc = xmlutil.SubTemplateElement(elem, 'description')
|
||||
desc.text = 'description'
|
||||
|
||||
rules = xmlutil.SubTemplateElement(elem, 'rules')
|
||||
rule = xmlutil.SubTemplateElement(rules, 'rule', selector='rules')
|
||||
make_rule(rule)
|
||||
ALIAS = 'os-security-groups'
|
||||
authorize = extensions.extension_authorizer('compute', 'v3:' + ALIAS)
|
||||
softauth = extensions.soft_extension_authorizer('compute', 'v3:' + ALIAS)
|
||||
|
||||
|
||||
def _authorize_context(req):
|
||||
@@ -81,102 +42,6 @@ def _authorize_context(req):
|
||||
authorize(context)
|
||||
return context
|
||||
|
||||
sg_nsmap = {None: wsgi.XMLNS_V11}
|
||||
|
||||
|
||||
class SecurityGroupRuleTemplate(xmlutil.TemplateBuilder):
|
||||
def construct(self):
|
||||
root = xmlutil.TemplateElement('security_group_rule',
|
||||
selector='security_group_rule')
|
||||
make_rule(root)
|
||||
return xmlutil.MasterTemplate(root, 1, nsmap=sg_nsmap)
|
||||
|
||||
|
||||
class SecurityGroupTemplate(xmlutil.TemplateBuilder):
|
||||
def construct(self):
|
||||
root = xmlutil.TemplateElement('security_group',
|
||||
selector='security_group')
|
||||
make_sg(root)
|
||||
return xmlutil.MasterTemplate(root, 1, nsmap=sg_nsmap)
|
||||
|
||||
|
||||
class SecurityGroupsTemplate(xmlutil.TemplateBuilder):
|
||||
def construct(self):
|
||||
root = xmlutil.TemplateElement('security_groups')
|
||||
elem = xmlutil.SubTemplateElement(root, 'security_group',
|
||||
selector='security_groups')
|
||||
make_sg(elem)
|
||||
return xmlutil.MasterTemplate(root, 1, nsmap=sg_nsmap)
|
||||
|
||||
|
||||
class SecurityGroupXMLDeserializer(wsgi.MetadataXMLDeserializer):
|
||||
"""
|
||||
Deserializer to handle xml-formatted security group requests.
|
||||
"""
|
||||
def default(self, string):
|
||||
"""Deserialize an xml-formatted security group create request."""
|
||||
dom = xmlutil.safe_minidom_parse_string(string)
|
||||
security_group = {}
|
||||
sg_node = self.find_first_child_named(dom,
|
||||
'security_group')
|
||||
if sg_node is not None:
|
||||
if sg_node.hasAttribute('name'):
|
||||
security_group['name'] = sg_node.getAttribute('name')
|
||||
desc_node = self.find_first_child_named(sg_node,
|
||||
"description")
|
||||
if desc_node:
|
||||
security_group['description'] = self.extract_text(desc_node)
|
||||
return {'body': {'security_group': security_group}}
|
||||
|
||||
|
||||
class SecurityGroupRulesXMLDeserializer(wsgi.MetadataXMLDeserializer):
|
||||
"""
|
||||
Deserializer to handle xml-formatted security group requests.
|
||||
"""
|
||||
|
||||
def default(self, string):
|
||||
"""Deserialize an xml-formatted security group create request."""
|
||||
dom = xmlutil.safe_minidom_parse_string(string)
|
||||
security_group_rule = self._extract_security_group_rule(dom)
|
||||
return {'body': {'security_group_rule': security_group_rule}}
|
||||
|
||||
def _extract_security_group_rule(self, node):
|
||||
"""Marshal the security group rule attribute of a parsed request."""
|
||||
sg_rule = {}
|
||||
sg_rule_node = self.find_first_child_named(node,
|
||||
'security_group_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)
|
||||
|
||||
parent_group_id_node = self.find_first_child_named(sg_rule_node,
|
||||
"parent_group_id")
|
||||
if parent_group_id_node is not None:
|
||||
sg_rule['parent_group_id'] = self.extract_text(
|
||||
parent_group_id_node)
|
||||
|
||||
group_id_node = self.find_first_child_named(sg_rule_node,
|
||||
"group_id")
|
||||
if group_id_node is not None:
|
||||
sg_rule['group_id'] = self.extract_text(group_id_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
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def translate_exceptions():
|
||||
@@ -197,250 +62,6 @@ def translate_exceptions():
|
||||
raise exc.HTTPRequestEntityTooLarge(explanation=msg)
|
||||
|
||||
|
||||
class SecurityGroupControllerBase(object):
|
||||
"""Base class for Security Group controllers."""
|
||||
|
||||
def __init__(self):
|
||||
self.security_group_api = (
|
||||
openstack_driver.get_openstack_security_group_driver())
|
||||
self.compute_api = compute.API(
|
||||
security_group_api=self.security_group_api)
|
||||
|
||||
def _format_security_group_rule(self, context, rule):
|
||||
sg_rule = {}
|
||||
sg_rule['id'] = rule['id']
|
||||
sg_rule['parent_group_id'] = rule['parent_group_id']
|
||||
sg_rule['ip_protocol'] = rule['protocol']
|
||||
sg_rule['from_port'] = rule['from_port']
|
||||
sg_rule['to_port'] = rule['to_port']
|
||||
sg_rule['group'] = {}
|
||||
sg_rule['ip_range'] = {}
|
||||
if rule['group_id']:
|
||||
with translate_exceptions():
|
||||
source_group = self.security_group_api.get(context,
|
||||
id=rule['group_id'])
|
||||
sg_rule['group'] = {'name': source_group.get('name'),
|
||||
'tenant_id': source_group.get('project_id')}
|
||||
else:
|
||||
sg_rule['ip_range'] = {'cidr': rule['cidr']}
|
||||
return sg_rule
|
||||
|
||||
def _format_security_group(self, context, group):
|
||||
security_group = {}
|
||||
security_group['id'] = group['id']
|
||||
security_group['description'] = group['description']
|
||||
security_group['name'] = group['name']
|
||||
security_group['tenant_id'] = group['project_id']
|
||||
security_group['rules'] = []
|
||||
for rule in group['rules']:
|
||||
security_group['rules'] += [self._format_security_group_rule(
|
||||
context, rule)]
|
||||
return security_group
|
||||
|
||||
def _from_body(self, body, key):
|
||||
if not body:
|
||||
raise exc.HTTPUnprocessableEntity()
|
||||
value = body.get(key, None)
|
||||
if value is None:
|
||||
raise exc.HTTPUnprocessableEntity()
|
||||
return value
|
||||
|
||||
|
||||
class SecurityGroupController(SecurityGroupControllerBase):
|
||||
"""The Security group API controller for the OpenStack API."""
|
||||
|
||||
@wsgi.serializers(xml=SecurityGroupTemplate)
|
||||
def show(self, req, id):
|
||||
"""Return data about the given security group."""
|
||||
context = _authorize_context(req)
|
||||
|
||||
with translate_exceptions():
|
||||
id = self.security_group_api.validate_id(id)
|
||||
security_group = self.security_group_api.get(context, None, id,
|
||||
map_exception=True)
|
||||
|
||||
return {'security_group': self._format_security_group(context,
|
||||
security_group)}
|
||||
|
||||
def delete(self, req, id):
|
||||
"""Delete a security group."""
|
||||
context = _authorize_context(req)
|
||||
|
||||
with translate_exceptions():
|
||||
id = self.security_group_api.validate_id(id)
|
||||
security_group = self.security_group_api.get(context, None, id,
|
||||
map_exception=True)
|
||||
self.security_group_api.destroy(context, security_group)
|
||||
|
||||
return webob.Response(status_int=202)
|
||||
|
||||
@wsgi.serializers(xml=SecurityGroupsTemplate)
|
||||
def index(self, req):
|
||||
"""Returns a list of security groups."""
|
||||
context = _authorize_context(req)
|
||||
|
||||
search_opts = {}
|
||||
search_opts.update(req.GET)
|
||||
|
||||
with translate_exceptions():
|
||||
project_id = context.project_id
|
||||
raw_groups = self.security_group_api.list(context,
|
||||
project=project_id,
|
||||
search_opts=search_opts)
|
||||
|
||||
limited_list = common.limited(raw_groups, req)
|
||||
result = [self._format_security_group(context, group)
|
||||
for group in limited_list]
|
||||
|
||||
return {'security_groups':
|
||||
list(sorted(result,
|
||||
key=lambda k: (k['tenant_id'], k['name'])))}
|
||||
|
||||
@wsgi.serializers(xml=SecurityGroupTemplate)
|
||||
@wsgi.deserializers(xml=SecurityGroupXMLDeserializer)
|
||||
def create(self, req, body):
|
||||
"""Creates a new security group."""
|
||||
context = _authorize_context(req)
|
||||
|
||||
security_group = self._from_body(body, 'security_group')
|
||||
|
||||
group_name = security_group.get('name', None)
|
||||
group_description = security_group.get('description', None)
|
||||
|
||||
with translate_exceptions():
|
||||
self.security_group_api.validate_property(group_name, 'name', None)
|
||||
self.security_group_api.validate_property(group_description,
|
||||
'description', None)
|
||||
group_ref = self.security_group_api.create_security_group(
|
||||
context, group_name, group_description)
|
||||
|
||||
return {'security_group': self._format_security_group(context,
|
||||
group_ref)}
|
||||
|
||||
@wsgi.serializers(xml=SecurityGroupTemplate)
|
||||
def update(self, req, id, body):
|
||||
"""Update a security group."""
|
||||
context = _authorize_context(req)
|
||||
|
||||
with translate_exceptions():
|
||||
id = self.security_group_api.validate_id(id)
|
||||
security_group = self.security_group_api.get(context, None, id,
|
||||
map_exception=True)
|
||||
|
||||
security_group_data = self._from_body(body, 'security_group')
|
||||
group_name = security_group_data.get('name', None)
|
||||
group_description = security_group_data.get('description', None)
|
||||
|
||||
with translate_exceptions():
|
||||
self.security_group_api.validate_property(group_name, 'name', None)
|
||||
self.security_group_api.validate_property(group_description,
|
||||
'description', None)
|
||||
group_ref = self.security_group_api.update_security_group(
|
||||
context, security_group, group_name, group_description)
|
||||
|
||||
return {'security_group': self._format_security_group(context,
|
||||
group_ref)}
|
||||
|
||||
|
||||
class SecurityGroupRulesController(SecurityGroupControllerBase):
|
||||
|
||||
@wsgi.serializers(xml=SecurityGroupRuleTemplate)
|
||||
@wsgi.deserializers(xml=SecurityGroupRulesXMLDeserializer)
|
||||
def create(self, req, body):
|
||||
context = _authorize_context(req)
|
||||
|
||||
sg_rule = self._from_body(body, 'security_group_rule')
|
||||
|
||||
with translate_exceptions():
|
||||
parent_group_id = self.security_group_api.validate_id(
|
||||
sg_rule.get('parent_group_id', None))
|
||||
security_group = self.security_group_api.get(context, None,
|
||||
parent_group_id,
|
||||
map_exception=True)
|
||||
try:
|
||||
new_rule = self._rule_args_to_dict(context,
|
||||
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'),
|
||||
group_id=sg_rule.get('group_id'))
|
||||
except Exception as exp:
|
||||
raise exc.HTTPBadRequest(explanation=unicode(exp))
|
||||
|
||||
if new_rule is None:
|
||||
msg = _("Not enough parameters to build a valid rule.")
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
new_rule['parent_group_id'] = security_group['id']
|
||||
|
||||
if 'cidr' in new_rule:
|
||||
net, prefixlen = netutils.get_net_and_prefixlen(new_rule['cidr'])
|
||||
if net != '0.0.0.0' and prefixlen == '0':
|
||||
msg = _("Bad prefix for network in cidr %s") % new_rule['cidr']
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
with translate_exceptions():
|
||||
security_group_rule = (
|
||||
self.security_group_api.create_security_group_rule(
|
||||
context, security_group, new_rule))
|
||||
|
||||
return {"security_group_rule": self._format_security_group_rule(
|
||||
context,
|
||||
security_group_rule)}
|
||||
|
||||
def _rule_args_to_dict(self, context, to_port=None, from_port=None,
|
||||
ip_protocol=None, cidr=None, group_id=None):
|
||||
|
||||
if group_id is not None:
|
||||
group_id = self.security_group_api.validate_id(group_id)
|
||||
|
||||
# check if groupId exists
|
||||
self.security_group_api.get(context, id=group_id)
|
||||
return self.security_group_api.new_group_ingress_rule(
|
||||
group_id, ip_protocol, from_port, to_port)
|
||||
else:
|
||||
cidr = self.security_group_api.parse_cidr(cidr)
|
||||
return self.security_group_api.new_cidr_ingress_rule(
|
||||
cidr, ip_protocol, from_port, to_port)
|
||||
|
||||
def delete(self, req, id):
|
||||
context = _authorize_context(req)
|
||||
|
||||
with translate_exceptions():
|
||||
id = self.security_group_api.validate_id(id)
|
||||
rule = self.security_group_api.get_rule(context, id)
|
||||
group_id = rule['parent_group_id']
|
||||
security_group = self.security_group_api.get(context, None,
|
||||
group_id,
|
||||
map_exception=True)
|
||||
self.security_group_api.remove_rules(context, security_group,
|
||||
[rule['id']])
|
||||
|
||||
return webob.Response(status_int=202)
|
||||
|
||||
|
||||
class ServerSecurityGroupController(SecurityGroupControllerBase):
|
||||
|
||||
@wsgi.serializers(xml=SecurityGroupsTemplate)
|
||||
def index(self, req, server_id):
|
||||
"""Returns a list of security groups for the given instance."""
|
||||
context = _authorize_context(req)
|
||||
|
||||
self.security_group_api.ensure_default(context)
|
||||
|
||||
with translate_exceptions():
|
||||
instance = self.compute_api.get(context, server_id)
|
||||
groups = self.security_group_api.get_instance_security_groups(
|
||||
context, instance['uuid'], True)
|
||||
|
||||
result = [self._format_security_group(context, group)
|
||||
for group in groups]
|
||||
|
||||
return {'security_groups':
|
||||
list(sorted(result,
|
||||
key=lambda k: (k['tenant_id'], k['name'])))}
|
||||
|
||||
|
||||
class SecurityGroupActionController(wsgi.Controller):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(SecurityGroupActionController, self).__init__(*args, **kwargs)
|
||||
@@ -610,12 +231,12 @@ class SecurityGroupServersTemplate(xmlutil.TemplateBuilder):
|
||||
return xmlutil.SlaveTemplate(root, 1)
|
||||
|
||||
|
||||
class Security_groups(extensions.ExtensionDescriptor):
|
||||
class SecurityGroups(extensions.V3APIExtensionBase):
|
||||
"""Security group support."""
|
||||
name = "SecurityGroups"
|
||||
alias = "os-security-groups"
|
||||
namespace = "http://docs.openstack.org/compute/ext/securitygroups/api/v1.1"
|
||||
updated = "2013-05-28T00:00:00+00:00"
|
||||
alias = ALIAS
|
||||
namespace = "http://docs.openstack.org/compute/ext/securitygroups/api/v3"
|
||||
version = 1
|
||||
|
||||
def get_controller_extensions(self):
|
||||
controller = SecurityGroupActionController()
|
||||
@@ -625,24 +246,7 @@ class Security_groups(extensions.ExtensionDescriptor):
|
||||
return [actions, output]
|
||||
|
||||
def get_resources(self):
|
||||
resources = []
|
||||
|
||||
res = extensions.ResourceExtension('os-security-groups',
|
||||
controller=SecurityGroupController())
|
||||
|
||||
resources.append(res)
|
||||
|
||||
res = extensions.ResourceExtension('os-security-group-rules',
|
||||
controller=SecurityGroupRulesController())
|
||||
resources.append(res)
|
||||
|
||||
res = extensions.ResourceExtension(
|
||||
'os-security-groups',
|
||||
controller=ServerSecurityGroupController(),
|
||||
parent=dict(member_name='server', collection_name='servers'))
|
||||
resources.append(res)
|
||||
|
||||
return resources
|
||||
return []
|
||||
|
||||
|
||||
class NativeSecurityGroupExceptions(object):
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -200,6 +200,7 @@ policy_data = """
|
||||
"compute_extension:v3:os-rescue": "",
|
||||
"compute_extension:security_group_default_rules": "",
|
||||
"compute_extension:security_groups": "",
|
||||
"compute_extension:v3:os-security-groups": "",
|
||||
"compute_extension:server_diagnostics": "",
|
||||
"compute_extension:v3:os-server-diagnostics": "",
|
||||
"compute_extension:server_password": "",
|
||||
|
||||
@@ -81,6 +81,7 @@ nova.api.v3.extensions =
|
||||
rescue = nova.api.openstack.compute.plugins.v3.rescue:Rescue
|
||||
scheduler_hints = nova.api.openstack.compute.plugins.v3.scheduler_hints:SchedulerHints
|
||||
server_diagnostics = nova.api.openstack.compute.plugins.v3.server_diagnostics:ServerDiagnostics
|
||||
security_groups = nova.api.openstack.compute.plugins.v3.security_groups:SecurityGroups
|
||||
servers = nova.api.openstack.compute.plugins.v3.servers:Servers
|
||||
simple_tenant_usage = nova.api.openstack.compute.plugins.v3.simple_tenant_usage:SimpleTenantUsage
|
||||
|
||||
|
||||
Reference in New Issue
Block a user