diff options
-rw-r--r-- | nova/api/ec2/cloud.py | 18 | ||||
-rw-r--r-- | nova/api/openstack/compute/contrib/security_group_default_rules.py | 10 | ||||
-rw-r--r-- | nova/api/openstack/compute/contrib/security_groups.py | 71 | ||||
-rw-r--r-- | nova/compute/api.py | 285 | ||||
-rw-r--r-- | nova/conductor/manager.py | 4 | ||||
-rw-r--r-- | nova/network/manager.py | 6 | ||||
-rw-r--r-- | nova/network/security_group/__init__.py | 18 | ||||
-rw-r--r-- | nova/network/security_group/openstack_driver.py | 46 | ||||
-rw-r--r-- | nova/network/security_group/security_group_base.py | 193 | ||||
-rw-r--r-- | nova/tests/compute/test_compute.py | 6 |
10 files changed, 419 insertions, 238 deletions
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 03bf9f890..b3f9bd099 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -214,7 +214,7 @@ class CloudController(object): self.image_service = s3.S3ImageService() self.network_api = network.API() self.volume_api = volume.API() - self.security_group_api = CloudSecurityGroupAPI() + self.security_group_api = get_cloud_security_group_api() self.compute_api = compute.API(network_api=self.network_api, volume_api=self.volume_api, security_group_api=self.security_group_api) @@ -712,8 +712,8 @@ class CloudController(object): self.security_group_api.validate_property(group_name, 'name', allowed) - group_ref = self.security_group_api.create(context, group_name, - group_description) + group_ref = self.security_group_api.create_security_group( + context, group_name, group_description) return {'securityGroupSet': [self._format_security_group(context, group_ref)]} @@ -1662,7 +1662,7 @@ class CloudController(object): return {'imageId': ec2_id} -class CloudSecurityGroupAPI(compute_api.SecurityGroupAPI): +class EC2SecurityGroupExceptions(object): @staticmethod def raise_invalid_property(msg): raise exception.InvalidParameterValue(err=msg) @@ -1689,3 +1689,13 @@ class CloudSecurityGroupAPI(compute_api.SecurityGroupAPI): @staticmethod def raise_not_found(msg): pass + + +class CloudSecurityGroupNovaAPI(compute_api.SecurityGroupAPI, + EC2SecurityGroupExceptions): + pass + + +def get_cloud_security_group_api(): + if cfg.CONF.security_group_api.lower() == 'nova': + return CloudSecurityGroupNovaAPI() diff --git a/nova/api/openstack/compute/contrib/security_group_default_rules.py b/nova/api/openstack/compute/contrib/security_group_default_rules.py index fed1468a8..e2bba8127 100644 --- a/nova/api/openstack/compute/contrib/security_group_default_rules.py +++ b/nova/api/openstack/compute/contrib/security_group_default_rules.py @@ -24,6 +24,7 @@ from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova.api.openstack import xmlutil from nova import exception +from nova.network.security_group import openstack_driver from nova.openstack.common import log as logging @@ -104,6 +105,10 @@ class SecurityGroupDefaultRulesXMLDeserializer(wsgi.MetadataXMLDeserializer): class SecurityGroupDefaultRulesController(sg.SecurityGroupControllerBase): + def __init__(self): + self.security_group_api = ( + openstack_driver.get_openstack_security_group_driver()) + @wsgi.serializers(xml=SecurityGroupDefaultRuleTemplate) @wsgi.deserializers(xml=SecurityGroupDefaultRulesXMLDeserializer) def create(self, req, body): @@ -144,7 +149,8 @@ class SecurityGroupDefaultRulesController(sg.SecurityGroupControllerBase): context = self._authorize_context(req) authorize(context) - id = self._validate_id(id) + id = self.security_group_api.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) @@ -158,7 +164,7 @@ class SecurityGroupDefaultRulesController(sg.SecurityGroupControllerBase): context = self._authorize_context(req) authorize(context) - id = self._validate_id(id) + id = self.security_group_api.validate_id(id) rule = self.security_group_api.get_default_rule(context, id) diff --git a/nova/api/openstack/compute/contrib/security_groups.py b/nova/api/openstack/compute/contrib/security_groups.py index d42dc1b0a..3f48176cc 100644 --- a/nova/api/openstack/compute/contrib/security_groups.py +++ b/nova/api/openstack/compute/contrib/security_groups.py @@ -27,10 +27,12 @@ from nova import compute from nova.compute import api as compute_api from nova import db from nova import exception +from nova.network.security_group import openstack_driver from nova.openstack.common import log as logging from nova import utils from nova.virt import netutils + LOG = logging.getLogger(__name__) authorize = extensions.extension_authorizer('compute', 'security_groups') softauth = extensions.soft_extension_authorizer('compute', 'security_groups') @@ -175,7 +177,8 @@ class SecurityGroupControllerBase(object): """Base class for Security Group controllers.""" def __init__(self): - self.security_group_api = NativeSecurityGroupAPI() + self.security_group_api = ( + openstack_driver.get_openstack_security_group_driver()) self.compute_api = compute.API( security_group_api=self.security_group_api) @@ -214,13 +217,6 @@ class SecurityGroupControllerBase(object): authorize(context) return context - def _validate_id(self, id): - try: - return int(id) - except ValueError: - msg = _("Security group id should be integer") - raise exc.HTTPBadRequest(explanation=msg) - def _from_body(self, body, key): if not body: raise exc.HTTPUnprocessableEntity() @@ -238,7 +234,7 @@ class SecurityGroupController(SecurityGroupControllerBase): """Return data about the given security group.""" context = self._authorize_context(req) - id = self._validate_id(id) + id = self.security_group_api.validate_id(id) security_group = self.security_group_api.get(context, None, id, map_exception=True) @@ -250,7 +246,7 @@ class SecurityGroupController(SecurityGroupControllerBase): """Delete a security group.""" context = self._authorize_context(req) - id = self._validate_id(id) + id = self.security_group_api.validate_id(id) security_group = self.security_group_api.get(context, None, id, map_exception=True) @@ -273,7 +269,7 @@ class SecurityGroupController(SecurityGroupControllerBase): limited_list = common.limited(raw_groups, req) result = [self._format_security_group(context, group) - for group in limited_list] + for group in limited_list] return {'security_groups': list(sorted(result, @@ -294,11 +290,11 @@ class SecurityGroupController(SecurityGroupControllerBase): self.security_group_api.validate_property(group_description, 'description', None) - group_ref = self.security_group_api.create(context, group_name, - group_description) + group_ref = self.security_group_api.create_security_group( + context, group_name, group_description) return {'security_group': self._format_security_group(context, - group_ref)} + group_ref)} class SecurityGroupRulesController(SecurityGroupControllerBase): @@ -310,14 +306,13 @@ class SecurityGroupRulesController(SecurityGroupControllerBase): sg_rule = self._from_body(body, 'security_group_rule') - parent_group_id = self._validate_id(sg_rule.get('parent_group_id', - None)) + 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: - values = self._rule_args_to_dict(context, + 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'), @@ -326,24 +321,21 @@ class SecurityGroupRulesController(SecurityGroupControllerBase): except Exception as exp: raise exc.HTTPBadRequest(explanation=unicode(exp)) - if values is None: + if new_rule is None: msg = _("Not enough parameters to build a valid rule.") raise exc.HTTPBadRequest(explanation=msg) - values['parent_group_id'] = security_group.id + new_rule['parent_group_id'] = security_group['id'] - if 'cidr' in values: - net, prefixlen = netutils.get_net_and_prefixlen(values['cidr']) + 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") % values['cidr'] + msg = _("Bad prefix for network in cidr %s") % new_rule['cidr'] raise exc.HTTPBadRequest(explanation=msg) - if self.security_group_api.rule_exists(security_group, values): - msg = _('This rule already exists in group %s') % parent_group_id - raise exc.HTTPBadRequest(explanation=msg) - - security_group_rule = self.security_group_api.add_rules( - context, parent_group_id, security_group['name'], [values])[0] + 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, @@ -353,8 +345,9 @@ class SecurityGroupRulesController(SecurityGroupControllerBase): ip_protocol=None, cidr=None, group_id=None): if group_id is not None: - group_id = self._validate_id(group_id) - #check if groupId exists + 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) @@ -366,11 +359,11 @@ class SecurityGroupRulesController(SecurityGroupControllerBase): def delete(self, req, id): context = self._authorize_context(req) - id = self._validate_id(id) + id = self.security_group_api.validate_id(id) rule = self.security_group_api.get_rule(context, id) - group_id = rule.parent_group_id + group_id = rule['parent_group_id'] security_group = self.security_group_api.get(context, None, group_id, map_exception=True) @@ -408,7 +401,8 @@ class ServerSecurityGroupController(SecurityGroupControllerBase): class SecurityGroupActionController(wsgi.Controller): def __init__(self, *args, **kwargs): super(SecurityGroupActionController, self).__init__(*args, **kwargs) - self.security_group_api = NativeSecurityGroupAPI() + self.security_group_api = ( + openstack_driver.get_openstack_security_group_driver()) self.compute_api = compute.API( security_group_api=self.security_group_api) @@ -467,6 +461,8 @@ class SecurityGroupsOutputController(wsgi.Controller): def __init__(self, *args, **kwargs): super(SecurityGroupsOutputController, self).__init__(*args, **kwargs) self.compute_api = compute.API() + self.security_group_api = ( + openstack_driver.get_openstack_security_group_driver()) def _extend_servers(self, req, servers): key = "security_groups" @@ -562,7 +558,7 @@ class Security_groups(extensions.ExtensionDescriptor): return resources -class NativeSecurityGroupAPI(compute_api.SecurityGroupAPI): +class NativeSecurityGroupExceptions(object): @staticmethod def raise_invalid_property(msg): raise exc.HTTPBadRequest(explanation=msg) @@ -586,3 +582,8 @@ class NativeSecurityGroupAPI(compute_api.SecurityGroupAPI): @staticmethod def raise_not_found(msg): raise exc.HTTPNotFound(explanation=msg) + + +class NativeNovaSecurityGroupAPI(compute_api.SecurityGroupAPI, + NativeSecurityGroupExceptions): + pass diff --git a/nova/compute/api.py b/nova/compute/api.py index 86e10bc03..90b1e9176 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -26,7 +26,6 @@ import functools import re import string import time -import urllib import uuid from oslo.config import cfg @@ -47,9 +46,10 @@ from nova import exception from nova import hooks from nova.image import glance from nova import network +from nova.network.security_group import openstack_driver +from nova.network.security_group import security_group_base from nova import notifications from nova.openstack.common import excutils -from nova.openstack.common import importutils from nova.openstack.common import jsonutils from nova.openstack.common import log as logging from nova.openstack.common import timeutils @@ -61,7 +61,6 @@ from nova import servicegroup from nova import utils from nova import volume - LOG = logging.getLogger(__name__) compute_opts = [ @@ -81,12 +80,6 @@ compute_opts = [ default='nokernel', help='kernel image that indicates not to use a kernel, but to ' 'use a raw disk image instead'), - cfg.StrOpt('security_group_handler', - default='nova.network.sg.NullSecurityGroupHandler', - help='The full class name of the security group handler class'), - cfg.StrOpt('security_group_api', - default='nova.compute.api.SecurityGroupAPI', - help='The full class name of the security API class'), cfg.StrOpt('multi_instance_display_name_template', default='%(name)s-%(uuid)s', help='When creating multiple instances with a single request ' @@ -191,9 +184,8 @@ class API(base.Base): self.network_api = network_api or network.API() self.volume_api = volume_api or volume.API() self.security_group_api = (security_group_api or - importutils.import_object( - CONF.security_group_api)) - self.sgh = importutils.import_object(CONF.security_group_handler) + openstack_driver.get_openstack_security_group_driver()) + self.sgh = openstack_driver.get_security_group_handler() self.consoleauth_rpcapi = consoleauth_rpcapi.ConsoleAuthAPI() self.scheduler_rpcapi = scheduler_rpcapi.SchedulerAPI() self.compute_rpcapi = compute_rpcapi.ComputeAPI() @@ -864,13 +856,8 @@ class API(base.Base): base_image_ref = base_options['image_ref'] instance['system_metadata']['image_base_image_ref'] = base_image_ref - - # Use 'default' security_group if none specified. - if security_groups is None: - security_groups = ['default'] - elif not isinstance(security_groups, list): - security_groups = [security_groups] - instance['security_groups'] = security_groups + self.security_group_api.populate_security_groups(instance, + security_groups) return instance @@ -2755,7 +2742,7 @@ class KeypairAPI(base.Base): 'fingerprint': key_pair['fingerprint']} -class SecurityGroupAPI(base.Base): +class SecurityGroupAPI(base.Base, security_group_base.SecurityGroupBase): """ Sub-set of the Compute API related to managing security groups and security group rules @@ -2763,7 +2750,7 @@ class SecurityGroupAPI(base.Base): def __init__(self, **kwargs): super(SecurityGroupAPI, self).__init__(**kwargs) self.security_group_rpcapi = compute_rpcapi.SecurityGroupAPI() - self.sgh = importutils.import_object(CONF.security_group_handler) + self.sgh = openstack_driver.get_security_group_handler() def validate_property(self, value, property, allowed): """ @@ -2810,7 +2797,7 @@ class SecurityGroupAPI(base.Base): if not existed: self.sgh.trigger_security_group_create_refresh(context, group) - def create(self, context, name, description): + def create_security_group(self, context, name, description): try: reservations = QUOTAS.reserve(context, security_groups=1) except exception.OverQuota: @@ -2989,164 +2976,15 @@ class SecurityGroupAPI(base.Base): self.trigger_handler('instance_remove_security_group', context, instance, security_group_name) - def trigger_handler(self, event, *args): - handle = getattr(self.sgh, 'trigger_%s_refresh' % event) - handle(*args) - - def trigger_rules_refresh(self, context, id): - """Called when a rule is added to or removed from a security_group.""" - - security_group = self.db.security_group_get(context, id) - - for instance in security_group['instances']: - if instance['host'] is not None: - self.security_group_rpcapi.refresh_instance_security_rules( - context, instance['host'], instance) - - def trigger_members_refresh(self, context, group_ids): - """Called when a security group gains a new or loses a member. - - Sends an update request to each compute node for each instance for - which this is relevant. - """ - # First, we get the security group rules that reference these groups as - # the grantee.. - security_group_rules = set() - for group_id in group_ids: - security_group_rules.update( - self.db.security_group_rule_get_by_security_group_grantee( - context, - group_id)) - - # ..then we distill the rules into the groups to which they belong.. - security_groups = set() - for rule in security_group_rules: - security_group = self.db.security_group_get( - context, - rule['parent_group_id']) - security_groups.add(security_group) - - # ..then we find the instances that are members of these groups.. - instances = {} - for security_group in security_groups: - for instance in security_group['instances']: - if instance['uuid'] not in instances: - instances[instance['uuid']] = instance - - # ..then we send a request to refresh the rules for each instance. - for instance in instances.values(): - if instance['host']: - self.security_group_rpcapi.refresh_instance_security_rules( - context, instance['host'], instance) - - def parse_cidr(self, cidr): - if cidr: - try: - cidr = urllib.unquote(cidr).decode() - except Exception as e: - self.raise_invalid_cidr(cidr, e) - - if not utils.is_valid_cidr(cidr): - self.raise_invalid_cidr(cidr) - - return cidr - else: - return '0.0.0.0/0' - - @staticmethod - def new_group_ingress_rule(grantee_group_id, protocol, from_port, - to_port): - return SecurityGroupAPI._new_ingress_rule(protocol, from_port, - to_port, group_id=grantee_group_id) - - @staticmethod - def new_cidr_ingress_rule(grantee_cidr, protocol, from_port, to_port): - return SecurityGroupAPI._new_ingress_rule(protocol, from_port, - to_port, cidr=grantee_cidr) - - @staticmethod - def _new_ingress_rule(ip_protocol, from_port, to_port, - group_id=None, cidr=None): - values = {} - - if group_id: - values['group_id'] = group_id - # Open everything if an explicit port range or type/code are not - # specified, but only if a source group was specified. - ip_proto_upper = ip_protocol.upper() if ip_protocol else '' - if (ip_proto_upper == 'ICMP' and - from_port is None and to_port is None): - from_port = -1 - to_port = -1 - elif (ip_proto_upper in ['TCP', 'UDP'] and from_port is None - and to_port is None): - from_port = 1 - to_port = 65535 - - elif cidr: - values['cidr'] = cidr - - if ip_protocol and from_port is not None and to_port is not None: - - ip_protocol = str(ip_protocol) - try: - # Verify integer conversions - from_port = int(from_port) - to_port = int(to_port) - except ValueError: - if ip_protocol.upper() == 'ICMP': - raise exception.InvalidInput(reason="Type and" - " Code must be integers for ICMP protocol type") - else: - raise exception.InvalidInput(reason="To and From ports " - "must be integers") - - if ip_protocol.upper() not in ['TCP', 'UDP', 'ICMP']: - raise exception.InvalidIpProtocol(protocol=ip_protocol) - - # Verify that from_port must always be less than - # or equal to to_port - if (ip_protocol.upper() in ['TCP', 'UDP'] and - (from_port > to_port)): - raise exception.InvalidPortRange(from_port=from_port, - to_port=to_port, msg="Former value cannot" - " be greater than the later") - - # Verify valid TCP, UDP port ranges - if (ip_protocol.upper() in ['TCP', 'UDP'] and - (from_port < 1 or to_port > 65535)): - raise exception.InvalidPortRange(from_port=from_port, - to_port=to_port, msg="Valid TCP ports should" - " be between 1-65535") - - # Verify ICMP type and code - if (ip_protocol.upper() == "ICMP" and - (from_port < -1 or from_port > 255 or - to_port < -1 or to_port > 255)): - raise exception.InvalidPortRange(from_port=from_port, - to_port=to_port, msg="For ICMP, the" - " type:code must be valid") - - values['protocol'] = ip_protocol - values['from_port'] = from_port - values['to_port'] = to_port - - else: - # If cidr based filtering, protocol and ports are mandatory - if cidr: - return None - - return values - - def rule_exists(self, security_group, values): - """Indicates whether the specified rule values are already + def rule_exists(self, security_group, new_rule): + """Indicates whether the specified rule is already defined in the given security group. """ for rule in security_group['rules']: is_duplicate = True keys = ('group_id', 'cidr', 'from_port', 'to_port', 'protocol') for key in keys: - if rule.get(key) != values.get(key): + if rule.get(key) != new_rule.get(key): is_duplicate = False break if is_duplicate: @@ -3162,6 +3000,13 @@ class SecurityGroupAPI(base.Base): self.raise_not_found(msg) def add_rules(self, context, id, name, vals): + """Add security group rule(s) to security group. + + Note: the Nova security group API doesn't support adding muliple + security group rules at once but the EC2 one does. Therefore, + this function is writen to support both. + """ + count = QUOTAS.count(context, 'security_group_rules', id) try: projected = count + len(vals) @@ -3231,26 +3076,82 @@ class SecurityGroupAPI(base.Base): msg = _("Rule (%s) not found") % id self.raise_not_found(msg) - @staticmethod - def raise_invalid_property(msg): - raise NotImplementedError() + def validate_id(self, id): + try: + return int(id) + except ValueError: + msg = _("Security group id should be integer") + self.raise_invalid_property(msg) - @staticmethod - def raise_group_already_exists(msg): - raise NotImplementedError() + def create_security_group_rule(self, context, security_group, new_rule): + if self.rule_exists(security_group, new_rule): + msg = (_('This rule already exists in group %s') % + new_rule['parent_group_id']) + self.raise_group_already_exists(msg) + return self.add_rules(context, new_rule['parent_group_id'], + security_group['name'], + [new_rule])[0] - @staticmethod - def raise_invalid_group(msg): - raise NotImplementedError() + def trigger_handler(self, event, *args): + handle = getattr(self.sgh, 'trigger_%s_refresh' % event) + handle(*args) - @staticmethod - def raise_invalid_cidr(cidr, decoding_exception=None): - raise NotImplementedError() + def trigger_rules_refresh(self, context, id): + """Called when a rule is added to or removed from a security_group.""" - @staticmethod - def raise_over_quota(msg): - raise NotImplementedError() + security_group = self.db.security_group_get(context, id) - @staticmethod - def raise_not_found(msg): - raise NotImplementedError() + for instance in security_group['instances']: + if instance['host'] is not None: + self.security_group_rpcapi.refresh_instance_security_rules( + context, instance['host'], instance) + + def trigger_members_refresh(self, context, group_ids): + """Called when a security group gains a new or loses a member. + + Sends an update request to each compute node for each instance for + which this is relevant. + """ + # First, we get the security group rules that reference these groups as + # the grantee.. + security_group_rules = set() + for group_id in group_ids: + security_group_rules.update( + self.db.security_group_rule_get_by_security_group_grantee( + context, + group_id)) + + # ..then we distill the rules into the groups to which they belong.. + security_groups = set() + for rule in security_group_rules: + security_group = self.db.security_group_get( + context, + rule['parent_group_id']) + security_groups.add(security_group) + + # ..then we find the instances that are members of these groups.. + instances = {} + for security_group in security_groups: + for instance in security_group['instances']: + if instance['uuid'] not in instances: + instances[instance['uuid']] = instance + + # ..then we send a request to refresh the rules for each instance. + for instance in instances.values(): + if instance['host']: + self.security_group_rpcapi.refresh_instance_security_rules( + context, instance['host'], instance) + + def get_instance_security_groups(self, req, instance_id): + instance = req.get_db_instance(instance_id) + groups = instance.get('security_groups') + if groups: + return [{'name': group['name']} for group in groups] + + def populate_security_groups(self, instance, security_groups): + # Use 'default' security_group if none specified. + if security_groups is None: + security_groups = ['default'] + elif not isinstance(security_groups, list): + security_groups = [security_groups] + instance['security_groups'] = security_groups diff --git a/nova/conductor/manager.py b/nova/conductor/manager.py index 6202cb8b6..1c54b1f0b 100644 --- a/nova/conductor/manager.py +++ b/nova/conductor/manager.py @@ -20,6 +20,7 @@ from nova.compute import utils as compute_utils from nova import exception from nova import manager from nova import network +from nova.network.security_group import openstack_driver from nova import notifications from nova.openstack.common import jsonutils from nova.openstack.common import log as logging @@ -53,7 +54,8 @@ class ConductorManager(manager.SchedulerDependentManager): def __init__(self, *args, **kwargs): super(ConductorManager, self).__init__(service_name='conductor', *args, **kwargs) - self.security_group_api = compute_api.SecurityGroupAPI() + self.security_group_api = ( + openstack_driver.get_openstack_security_group_driver()) self._network_api = None self._compute_api = None self.quotas = quota.QUOTAS diff --git a/nova/network/manager.py b/nova/network/manager.py index d7f80dbda..a4669def0 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -52,7 +52,6 @@ from eventlet import greenpool import netaddr from oslo.config import cfg -from nova.compute import api as compute_api from nova import context from nova import exception from nova import ipv6 @@ -62,6 +61,7 @@ from nova.network import driver from nova.network import floating_ips from nova.network import model as network_model from nova.network import rpcapi as network_rpcapi +from nova.network.security_group import openstack_driver from nova.openstack.common import excutils from nova.openstack.common import importutils from nova.openstack.common import jsonutils @@ -286,7 +286,9 @@ class NetworkManager(manager.SchedulerDependentManager): CONF.floating_ip_dns_manager) self.network_api = network_api.API() self.network_rpcapi = network_rpcapi.NetworkAPI() - self.security_group_api = compute_api.SecurityGroupAPI() + self.security_group_api = ( + openstack_driver.get_openstack_security_group_driver()) + self.servicegroup_api = servicegroup.API() # NOTE(tr3buchet: unless manager subclassing NetworkManager has diff --git a/nova/network/security_group/__init__.py b/nova/network/security_group/__init__.py new file mode 100644 index 000000000..5f67d7881 --- /dev/null +++ b/nova/network/security_group/__init__.py @@ -0,0 +1,18 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2013 Nicira, Inc. +# All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# @author: Aaron Rosen, Nicira Networks, Inc. diff --git a/nova/network/security_group/openstack_driver.py b/nova/network/security_group/openstack_driver.py new file mode 100644 index 000000000..91ce69891 --- /dev/null +++ b/nova/network/security_group/openstack_driver.py @@ -0,0 +1,46 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2013 Nicira, Inc. +# All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# @author: Aaron Rosen, Nicira Networks, Inc. + +from oslo.config import cfg + +from nova.openstack.common import importutils + +security_group_opts = [ + cfg.StrOpt('security_group_api', + default='nova', + help='The full class name of the security API class'), + cfg.StrOpt('security_group_handler', + default='nova.network.sg.NullSecurityGroupHandler', + help='The full class name of the security group handler class'), +] + +CONF = cfg.CONF +CONF.register_opts(security_group_opts) + +NOVA_DRIVER = ('nova.api.openstack.compute.contrib.security_groups.' + 'NativeNovaSecurityGroupAPI') + + +def get_openstack_security_group_driver(): + if CONF.security_group_api.lower() == 'nova': + return importutils.import_object(NOVA_DRIVER) + + +def get_security_group_handler(): + return importutils.import_object(CONF.security_group_handler) diff --git a/nova/network/security_group/security_group_base.py b/nova/network/security_group/security_group_base.py new file mode 100644 index 000000000..e6abf988a --- /dev/null +++ b/nova/network/security_group/security_group_base.py @@ -0,0 +1,193 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# Copyright 2011 Piston Cloud Computing, Inc. +# Copyright 2012 Red Hat, Inc. +# Copyright 2013 Nicira, Inc. +# All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# @author: Aaron Rosen, Nicira Networks, Inc. + +import urllib + +from oslo.config import cfg + +from nova import exception +from nova import utils + +CONF = cfg.CONF + + +class SecurityGroupBase(object): + + def parse_cidr(self, cidr): + if cidr: + try: + cidr = urllib.unquote(cidr).decode() + except Exception as e: + self.raise_invalid_cidr(cidr, e) + + if not utils.is_valid_cidr(cidr): + self.raise_invalid_cidr(cidr) + + return cidr + else: + return '0.0.0.0/0' + + @staticmethod + def new_group_ingress_rule(grantee_group_id, protocol, from_port, + to_port): + return SecurityGroupBase._new_ingress_rule( + protocol, from_port, to_port, group_id=grantee_group_id) + + @staticmethod + def new_cidr_ingress_rule(grantee_cidr, protocol, from_port, to_port): + return SecurityGroupBase._new_ingress_rule( + protocol, from_port, to_port, cidr=grantee_cidr) + + @staticmethod + def _new_ingress_rule(ip_protocol, from_port, to_port, + group_id=None, cidr=None): + values = {} + + if group_id: + values['group_id'] = group_id + # Open everything if an explicit port range or type/code are not + # specified, but only if a source group was specified. + ip_proto_upper = ip_protocol.upper() if ip_protocol else '' + if (ip_proto_upper == 'ICMP' and + from_port is None and to_port is None): + from_port = -1 + to_port = -1 + elif (ip_proto_upper in ['TCP', 'UDP'] and from_port is None + and to_port is None): + from_port = 1 + to_port = 65535 + + elif cidr: + values['cidr'] = cidr + + if ip_protocol and from_port is not None and to_port is not None: + + ip_protocol = str(ip_protocol) + try: + # Verify integer conversions + from_port = int(from_port) + to_port = int(to_port) + except ValueError: + if ip_protocol.upper() == 'ICMP': + raise exception.InvalidInput(reason="Type and" + " Code must be integers for ICMP protocol type") + else: + raise exception.InvalidInput(reason="To and From ports " + "must be integers") + + if ip_protocol.upper() not in ['TCP', 'UDP', 'ICMP']: + raise exception.InvalidIpProtocol(protocol=ip_protocol) + + # Verify that from_port must always be less than + # or equal to to_port + if (ip_protocol.upper() in ['TCP', 'UDP'] and + (from_port > to_port)): + raise exception.InvalidPortRange(from_port=from_port, + to_port=to_port, msg="Former value cannot" + " be greater than the later") + + # Verify valid TCP, UDP port ranges + if (ip_protocol.upper() in ['TCP', 'UDP'] and + (from_port < 1 or to_port > 65535)): + raise exception.InvalidPortRange(from_port=from_port, + to_port=to_port, msg="Valid TCP ports should" + " be between 1-65535") + + # Verify ICMP type and code + if (ip_protocol.upper() == "ICMP" and + (from_port < -1 or from_port > 255 or + to_port < -1 or to_port > 255)): + raise exception.InvalidPortRange(from_port=from_port, + to_port=to_port, msg="For ICMP, the" + " type:code must be valid") + + values['protocol'] = ip_protocol + values['from_port'] = from_port + values['to_port'] = to_port + + else: + # If cidr based filtering, protocol and ports are mandatory + if cidr: + return None + + return values + + def validate_property(self, value, property, allowed): + pass + + def ensure_default(self, context): + pass + + def trigger_handler(self, event, *args): + pass + + def trigger_rules_refresh(self, context, id): + """Called when a rule is added to or removed from a security_group.""" + pass + + def trigger_members_refresh(self, context, group_ids): + """Called when a security group gains a new or loses a member. + + Sends an update request to each compute node for each instance for + which this is relevant. + """ + pass + + def populate_security_groups(self, instance, security_groups): + """Called when populating the database for an instances + security groups.""" + raise NotImplementedError() + + def create_security_group(self, context, name, description): + raise NotImplementedError() + + def get(self, context, name=None, id=None, map_exception=False): + raise NotImplementedError() + + def list(self, context, names=None, ids=None, project=None, + search_opts=None): + raise NotImplementedError() + + def destroy(self, context, security_group): + raise NotImplementedError() + + def add_rules(self, context, id, name, vals): + raise NotImplementedError() + + def create_security_group_rule(self, context, security_group, new_rule): + raise NotImplementedError() + + def remove_rules(self, context, security_group, rule_ids): + raise NotImplementedError() + + def get_rule(self, context, id): + raise NotImplementedError() + + def get_instance_security_groups(self, req, instance_id): + raise NotImplementedError() + + def add_to_instance(self, context, instance, security_group_name): + raise NotImplementedError() + + def remove_from_instance(self, context, instance, security_group_name): + raise NotImplementedError() diff --git a/nova/tests/compute/test_compute.py b/nova/tests/compute/test_compute.py index 6eb5feb85..542a7e162 100644 --- a/nova/tests/compute/test_compute.py +++ b/nova/tests/compute/test_compute.py @@ -46,6 +46,7 @@ from nova import exception from nova.image import glance from nova.network import api as network_api from nova.network import model as network_model +from nova.network.security_group import openstack_driver from nova.openstack.common import importutils from nova.openstack.common import jsonutils from nova.openstack.common import log as logging @@ -69,7 +70,6 @@ from nova import utils from nova.virt import fake from nova.volume import cinder - QUOTAS = quota.QUOTAS LOG = logging.getLogger(__name__) CONF = cfg.CONF @@ -3649,7 +3649,9 @@ class ComputeAPITestCase(BaseTestCase): super(ComputeAPITestCase, self).setUp() self.stubs.Set(network_api.API, 'get_instance_nw_info', fake_get_nw_info) - self.security_group_api = compute_api.SecurityGroupAPI() + self.security_group_api = ( + openstack_driver.get_openstack_security_group_driver()) + self.compute_api = compute.API( security_group_api=self.security_group_api) self.fake_image = { |