diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 560a2d0ddaf..1a574ec6f79 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -202,6 +202,7 @@ def __init__(self): self.volume_api = volume.API() self.compute_api = compute.API(network_api=self.network_api, volume_api=self.volume_api) + self.sgh = utils.import_object(FLAGS.security_group_handler) def __str__(self): return 'CloudController' @@ -622,6 +623,7 @@ def revoke_security_group_ingress(self, context, group_name=None, except KeyError: prevalues.append(kwargs) rule_id = None + rule_ids = [] for values in prevalues: rulesvalues = self._rule_args_to_dict(context, values) if not rulesvalues: @@ -634,11 +636,14 @@ def revoke_security_group_ingress(self, context, group_name=None, values_for_rule) if rule_id: db.security_group_rule_destroy(context, rule_id) + rule_ids.append(rule_id) if rule_id: # NOTE(vish): we removed a rule, so refresh self.compute_api.trigger_security_group_rules_refresh( context, security_group_id=security_group['id']) + self.sgh.trigger_security_group_rule_destroy_refresh( + context, rule_ids) return True raise exception.EC2APIError(_("No rule for the specified parameters.")) @@ -685,15 +690,19 @@ def authorize_security_group_ingress(self, context, group_name=None, raise exception.EC2APIError(_(err) % values_for_rule) postvalues.append(values_for_rule) + rule_ids = [] for values_for_rule in postvalues: security_group_rule = db.security_group_rule_create( context, values_for_rule) + rule_ids.append(security_group_rule['id']) if postvalues: self.compute_api.trigger_security_group_rules_refresh( context, security_group_id=security_group['id']) + self.sgh.trigger_security_group_rule_create_refresh( + context, rule_ids) return True raise exception.EC2APIError(_("No rule for the specified parameters.")) @@ -744,6 +753,8 @@ def create_security_group(self, context, group_name, group_description): 'description': group_description} group_ref = db.security_group_create(context, group) + self.sgh.trigger_security_group_create_refresh(context, group) + return {'securityGroupSet': [self._format_security_group(context, group_ref)]} @@ -765,6 +776,9 @@ def delete_security_group(self, context, group_name=None, group_id=None, raise notfound(security_group_id=group_id) LOG.audit(_("Delete security group %s"), group_name, context=context) db.security_group_destroy(context, security_group.id) + + self.sgh.trigger_security_group_destroy_refresh(context, + security_group.id) return True def get_console_output(self, context, instance_id, **kwargs): diff --git a/nova/api/openstack/compute/contrib/security_groups.py b/nova/api/openstack/compute/contrib/security_groups.py index e1e5a47fe06..d7e851468af 100644 --- a/nova/api/openstack/compute/contrib/security_groups.py +++ b/nova/api/openstack/compute/contrib/security_groups.py @@ -179,6 +179,7 @@ class SecurityGroupController(object): def __init__(self): self.compute_api = compute.API() super(SecurityGroupController, self).__init__() + self.sgh = utils.import_object(FLAGS.security_group_handler) def _format_security_group_rule(self, context, rule): sg_rule = {} @@ -236,6 +237,8 @@ def delete(self, req, id): security_group = self._get_security_group(context, id) LOG.audit(_("Delete security group %s"), id, context=context) db.security_group_destroy(context, security_group.id) + self.sgh.trigger_security_group_destroy_refresh( + context, security_group.id) return webob.Response(status_int=202) @@ -290,6 +293,7 @@ def create(self, req, body): 'name': group_name, 'description': group_description} group_ref = db.security_group_create(context, group) + self.sgh.trigger_security_group_create_refresh(context, group) return {'security_group': self._format_security_group(context, group_ref)} @@ -366,7 +370,8 @@ def create(self, req, body): raise exc.HTTPBadRequest(explanation=msg) security_group_rule = db.security_group_rule_create(context, values) - + self.sgh.trigger_security_group_rule_create_refresh( + context, [security_group_rule['id']]) self.compute_api.trigger_security_group_rules_refresh(context, security_group_id=security_group['id']) @@ -495,6 +500,8 @@ def delete(self, req, id): LOG.audit(msg, security_group['name'], context=context) db.security_group_rule_destroy(context, rule['id']) + self.sgh.trigger_security_group_rule_destroy_refresh( + context, [rule['id']]) self.compute_api.trigger_security_group_rules_refresh(context, security_group_id=security_group['id']) @@ -505,6 +512,7 @@ class SecurityGroupActionController(wsgi.Controller): def __init__(self, *args, **kwargs): super(SecurityGroupActionController, self).__init__(*args, **kwargs) self.compute_api = compute.API() + self.sgh = utils.import_object(FLAGS.security_group_handler) @wsgi.action('addSecurityGroup') def _addSecurityGroup(self, req, id, body): @@ -528,6 +536,8 @@ def _addSecurityGroup(self, req, id, body): try: instance = self.compute_api.get(context, id) self.compute_api.add_security_group(context, instance, group_name) + self.sgh.trigger_instance_add_security_group_refresh( + context, instance, group_name) except exception.SecurityGroupNotFound as exp: raise exc.HTTPNotFound(explanation=unicode(exp)) except exception.InstanceNotFound as exp: @@ -560,6 +570,8 @@ def _removeSecurityGroup(self, req, id, body): instance = self.compute_api.get(context, id) self.compute_api.remove_security_group(context, instance, group_name) + self.sgh.trigger_instance_remove_security_group_refresh( + context, instance, group_name) except exception.SecurityGroupNotFound as exp: raise exc.HTTPNotFound(explanation=unicode(exp)) except exception.InstanceNotFound as exp: diff --git a/nova/flags.py b/nova/flags.py index af490e287da..4277d1eda60 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -451,6 +451,9 @@ def _get_my_ip(): cfg.StrOpt('volume_api_class', default='nova.volume.api.API', help='The volume API class to use'), + cfg.StrOpt('security_group_handler', + default='nova.network.quantum.sg.NullSecurityGroupHandler', + help='security group handler class') ] FLAGS.register_opts(global_opts) diff --git a/nova/network/manager.py b/nova/network/manager.py index fe6cfa07aef..5ad14f8396d 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -685,6 +685,7 @@ def __init__(self, network_driver=None, *args, **kwargs): self.floating_dns_manager = temp self.network_api = network_api.API() self.compute_api = compute_api.API() + self.sgh = utils.import_object(FLAGS.security_group_handler) # NOTE(tr3buchet: unless manager subclassing NetworkManager has # already imported ipam, import nova ipam here @@ -761,7 +762,9 @@ def _do_trigger_security_group_members_refresh_for_instance(self, groups = instance_ref['security_groups'] group_ids = [group['id'] for group in groups] self.compute_api.trigger_security_group_members_refresh(admin_context, - group_ids) + group_ids) + self.sgh.trigger_security_group_members_refresh(admin_context, + group_ids) def get_floating_ips_by_fixed_address(self, context, fixed_address): # NOTE(jkoelker) This is just a stub function. Managers supporting diff --git a/nova/network/quantum/sg.py b/nova/network/quantum/sg.py new file mode 100644 index 00000000000..93ba6be1197 --- /dev/null +++ b/nova/network/quantum/sg.py @@ -0,0 +1,159 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012 Nicira Networks, 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. + +'''Implement Security Groups abstraction and API. + +The nova security_group_handler flag specifies which class is to be used +to implement the security group calls. + +The NullSecurityGroupHandler provides a "no-op" plugin that is loaded +by default and has no impact on current system behavior. In the future, +special purposes classes that inherit from SecurityGroupHandlerBase +will provide enhanced functionality and will be loadable via the +security_group_handler flag. +''' + +from nova import log as logging + + +LOG = logging.getLogger('nova.network.api.quantum.sg') + + +class SecurityGroupHandlerBase(object): + + def __init__(self): + raise NotImplementedError() + + def trigger_security_group_create_refresh(self, context, group): + '''Called when a rule is added to a security_group. + + :param context: the security context. + :param group: the new group added. group is a dictionary that contains + the following: user_id, project_id, name, description).''' + raise NotImplementedError() + + def trigger_security_group_destroy_refresh(self, context, + security_group_id): + '''Called when a rule is added to a security_group. + + :param context: the security context. + :param security_group_id: the security group identifier.''' + raise NotImplementedError() + + def trigger_security_group_rule_create_refresh(self, context, + rule_ids): + '''Called when a rule is added to a security_group. + + :param context: the security context. + :param rule_ids: a list of rule ids that have been affected.''' + raise NotImplementedError() + + def trigger_security_group_rule_destroy_refresh(self, context, + rule_ids): + '''Called when a rule is removed from a security_group. + + :param context: the security context. + :param rule_ids: a list of rule ids that have been affected.''' + raise NotImplementedError() + + def trigger_instance_add_security_group_refresh(self, context, instance, + group_name): + '''Called when a security group gains a new member. + + :param context: the security context. + :param instance: the instance to be associated. + :param group_name: the name of the security group to be associated.''' + raise NotImplementedError() + + def trigger_instance_remove_security_group_refresh(self, context, instance, + group_name): + '''Called when a security group loses a member. + + :param context: the security context. + :param instance: the instance to be associated. + :param group_name: the name of the security group to be associated.''' + raise NotImplementedError() + + def trigger_security_group_members_refresh(self, context, group_ids): + '''Called when a security group gains or loses a member. + + :param context: the security context. + :param group_ids: a list of security group identifiers.''' + raise NotImplementedError() + + +class NullSecurityGroupHandler(SecurityGroupHandlerBase): + + def __init__(self): + pass + + def trigger_security_group_create_refresh(self, context, group): + '''Called when a rule is added to a security_group. + + :param context: the security context. + :param group: the new group added. group is a dictionary that contains + the following: user_id, project_id, name, description).''' + pass + + def trigger_security_group_destroy_refresh(self, context, + security_group_id): + '''Called when a rule is added to a security_group. + + :param context: the security context. + :param security_group_id: the security group identifier.''' + pass + + def trigger_security_group_rule_create_refresh(self, context, + rule_ids): + '''Called when a rule is added to a security_group. + + :param context: the security context. + :param rule_ids: a list of rule ids that have been affected.''' + pass + + def trigger_security_group_rule_destroy_refresh(self, context, + rule_ids): + '''Called when a rule is removed from a security_group. + + :param context: the security context. + :param rule_ids: a list of rule ids that have been affected.''' + pass + + def trigger_instance_add_security_group_refresh(self, context, instance, + group_name): + '''Called when a security group gains a new member. + + :param context: the security context. + :param instance: the instance to be associated. + :param group_name: the name of the security group to be associated.''' + pass + + def trigger_instance_remove_security_group_refresh(self, context, instance, + group_name): + '''Called when a security group loses a member. + + :param context: the security context. + :param instance: the instance to be associated. + :param group_name: the name of the security group to be associated.''' + pass + + def trigger_security_group_members_refresh(self, context, group_ids): + '''Called when a security group gains or loses a member. + + :param context: the security context. + :param group_ids: a list of security group identifiers.''' + pass