diff --git a/devstack/plugin.sh b/devstack/plugin.sh index 74a43851c..f126888e0 100755 --- a/devstack/plugin.sh +++ b/devstack/plugin.sh @@ -43,6 +43,9 @@ function configure_fwaas_v1() { function configure_fwaas_v2() { # Add conf file cp $NEUTRON_FWAAS_DIR/etc/neutron_fwaas.conf.sample $NEUTRON_FWAAS_CONF + inicomment $NEUTRON_FWAAS_CONF service_providers service_provider + iniadd $NEUTRON_FWAAS_CONF service_providers service_provider $NEUTRON_FWAAS_SERVICE_PROVIDERV2 + neutron_fwaas_configure_driver fwaas_v2 iniset_multiline $Q_L3_CONF_FILE fwaas agent_version v2 iniset_multiline $Q_L3_CONF_FILE fwaas driver $FWAAS_DRIVER_V2 diff --git a/devstack/settings b/devstack/settings index 2a572a5bd..824f97f75 100644 --- a/devstack/settings +++ b/devstack/settings @@ -8,4 +8,5 @@ NEUTRON_FWAAS_DIR=$DEST/neutron-fwaas NEUTRON_FWAAS_CONF_FILE=neutron_fwaas.conf NEUTRON_FWAAS_CONF=$NEUTRON_CONF_DIR/$NEUTRON_FWAAS_CONF_FILE -neutron_server_config_add $NEUTRON_FWAAS_CONF + +NEUTRON_FWAAS_SERVICE_PROVIDERV2=${NEUTRON_FWAAS_SERVICE_PROVIDERV2:-FIREWALL_V2:fwaas_db:neutron_fwaas.services.firewall.service_drivers.agents.agents.FirewallAgentDriver:default} diff --git a/neutron_fwaas/db/firewall/v2/firewall_db_v2.py b/neutron_fwaas/db/firewall/v2/firewall_db_v2.py index 7dd2fbd5b..f57a8f62b 100644 --- a/neutron_fwaas/db/firewall/v2/firewall_db_v2.py +++ b/neutron_fwaas/db/firewall/v2/firewall_db_v2.py @@ -16,16 +16,16 @@ import copy import netaddr + +from neutron.db import _model_query as model_query from neutron.db import api as db_api -from neutron.db import common_db_mixin as base_db +from neutron.db import common_db_mixin from neutron_lib.api.definitions import constants as fw_const -from neutron_lib.api import validators from neutron_lib import constants as nl_constants from neutron_lib.db import constants as db_constants from neutron_lib.db import model_base from neutron_lib import exceptions from neutron_lib.exceptions import firewall_v2 as f_exc -from oslo_config import cfg from oslo_db import exception as db_exc from oslo_log import log as logging from oslo_utils import uuidutils @@ -36,7 +36,6 @@ from sqlalchemy import orm from sqlalchemy.orm import exc from neutron_fwaas.common import fwaas_constants as const -from neutron_fwaas.extensions import firewall_v2 as fw_ext LOG = logging.getLogger(__name__) @@ -86,7 +85,7 @@ class FirewallRuleV2(model_base.BASEV2, model_base.HasId, HasName, class FirewallGroup(model_base.BASEV2, model_base.HasId, HasName, HasDescription, model_base.HasProject): __tablename__ = 'firewall_groups_v2' - ports = orm.relationship( + port_associations = orm.relationship( 'FirewallGroupPortAssociation', backref=orm.backref('firewall_group_port_associations_v2', cascade='all, delete')) @@ -162,7 +161,42 @@ class FirewallPolicy(model_base.BASEV2, model_base.HasId, HasName, shared = sa.Column(sa.Boolean) -class Firewall_db_mixin_v2(fw_ext.Firewallv2PluginBase, base_db.CommonDbMixin): +def _list_firewall_groups_result_filter_hook(query, filters): + values = filters and filters.get('ports', []) + if values: + query = query.join(FirewallGroupPortAssociation) + query = query.filter(FirewallGroupPortAssociation.port_id.in_(values)) + + return query + + +def _list_firewall_policies_result_filter_hook(query, filters): + values = filters and filters.get('firewall_rules', []) + if values: + query = query.join(FirewallPolicyRuleAssociation) + query = query.filter( + FirewallPolicyRuleAssociation.firewall_rule_id.in_(values)) + + return query + + +class FirewallPluginDb(common_db_mixin.CommonDbMixin): + + def __new__(cls, *args, **kwargs): + model_query.register_hook( + FirewallGroup, + "firewall_group_v2_filter_by_port_association", + query_hook=None, + filter_hook=None, + result_filters=_list_firewall_groups_result_filter_hook) + + model_query.register_hook( + FirewallPolicy, + "firewall_policy_v2_filter_by_firewall_rule_association", + query_hook=None, + filter_hook=None, + result_filters=_list_firewall_policies_result_filter_hook) + return super(FirewallPluginDb, cls).__new__(cls, *args, **kwargs) def _get_firewall_group(self, context, id): try: @@ -267,22 +301,21 @@ class Firewall_db_mixin_v2(fw_ext.Firewallv2PluginBase, base_db.CommonDbMixin): 'shared': firewall_policy['shared']} return self._fields(res, fields) - def _make_firewall_group_dict(self, firewall_group, fields=None): - fwg_ports = [ - port_assoc.port_id for port_assoc in firewall_group['ports'] - ] - res = {'id': firewall_group['id'], - 'tenant_id': firewall_group['tenant_id'], - 'name': firewall_group['name'], - 'description': firewall_group['description'], + def _make_firewall_group_dict(self, firewall_group_db, fields=None): + fwg_ports = [port_assoc.port_id for port_assoc in + firewall_group_db.port_associations] + res = {'id': firewall_group_db['id'], + 'tenant_id': firewall_group_db['tenant_id'], + 'name': firewall_group_db['name'], + 'description': firewall_group_db['description'], 'ingress_firewall_policy_id': - firewall_group['ingress_firewall_policy_id'], + firewall_group_db['ingress_firewall_policy_id'], 'egress_firewall_policy_id': - firewall_group['egress_firewall_policy_id'], - 'admin_state_up': firewall_group['admin_state_up'], + firewall_group_db['egress_firewall_policy_id'], + 'admin_state_up': firewall_group_db['admin_state_up'], 'ports': fwg_ports, - 'status': firewall_group['status'], - 'shared': firewall_group['shared']} + 'status': firewall_group_db['status'], + 'shared': firewall_group_db['shared']} return self._fields(res, fields) def _get_policy_ordered_rules(self, context, policy_id): @@ -292,7 +325,7 @@ class Firewall_db_mixin_v2(fw_ext.Firewallv2PluginBase, base_db.CommonDbMixin): .order_by(FirewallPolicyRuleAssociation.position)) return [self._make_firewall_rule_dict(rule) for rule in query] - def _make_firewall_group_dict_with_rules(self, context, firewall_group_id): + def make_firewall_group_dict_with_rules(self, context, firewall_group_id): firewall_group = self.get_firewall_group(context, firewall_group_id) ingress_policy_id = firewall_group['ingress_firewall_policy_id'] if ingress_policy_id: @@ -359,7 +392,7 @@ class Firewall_db_mixin_v2(fw_ext.Firewallv2PluginBase, base_db.CommonDbMixin): def _get_policy_rule_association(self, context, firewall_policy_id, firewall_rule_id): """Returns the association between a firewall rule and a firewall - policy. Throws an exception if the assocaition does not exist. + policy. Throws an exception if the assocition does not exist. """ try: return self._get_policy_rule_association_query( @@ -374,7 +407,7 @@ class Firewall_db_mixin_v2(fw_ext.Firewallv2PluginBase, base_db.CommonDbMixin): # configuration file makes sense. Can be done some time later # 1. Drop any IPv4 packets for ingress traffic - in_fwr_v4 = {'firewall_rule': { + in_fwr_v4 = { 'description': 'default ingress rule for IPv4', 'name': 'default ingress ipv4 (deny all)', 'shared': False, @@ -387,28 +420,25 @@ class Firewall_db_mixin_v2(fw_ext.Firewallv2PluginBase, base_db.CommonDbMixin): 'source_ip_address': None, 'destination_port': None, 'destination_ip_address': None, - }} + } # 2. Drop any IPv6 packets for ingress traffic in_fwr_v6 = copy.deepcopy(in_fwr_v4) - in_fwr_v6[ - 'firewall_rule']['description'] = 'default ingress rule for IPv6' - in_fwr_v6['firewall_rule']['name'] = 'default ingress ipv6 (deny all)' - in_fwr_v6['firewall_rule']['ip_version'] = nl_constants.IP_VERSION_6 + in_fwr_v6['description'] = 'default ingress rule for IPv6' + in_fwr_v6['name'] = 'default ingress ipv6 (deny all)' + in_fwr_v6['ip_version'] = nl_constants.IP_VERSION_6 # 3. Allow any IPv4 packets for egress traffic eg_fwr_v4 = copy.deepcopy(in_fwr_v4) - eg_fwr_v4[ - 'firewall_rule']['description'] = 'default egress rule for IPv4' - eg_fwr_v4['firewall_rule']['action'] = fw_const.FWAAS_ALLOW - eg_fwr_v4['firewall_rule']['name'] = 'default egress ipv4 (allow all)' + eg_fwr_v4['description'] = 'default egress rule for IPv4' + eg_fwr_v4['action'] = fw_const.FWAAS_ALLOW + eg_fwr_v4['name'] = 'default egress ipv4 (allow all)' # 4. Allow any IPv6 packets for egress traffic eg_fwr_v6 = copy.deepcopy(in_fwr_v6) - eg_fwr_v6[ - 'firewall_rule']['description'] = 'default egress rule for IPv6' - eg_fwr_v6['firewall_rule']['name'] = 'default egress ipv6 (allow all)' - eg_fwr_v6['firewall_rule']['action'] = fw_const.FWAAS_ALLOW + eg_fwr_v6['description'] = 'default egress rule for IPv6' + eg_fwr_v6['name'] = 'default egress ipv6 (allow all)' + eg_fwr_v6['action'] = fw_const.FWAAS_ALLOW return { 'in_ipv4': self.create_firewall_rule(context, in_fwr_v4)['id'], @@ -418,8 +448,7 @@ class Firewall_db_mixin_v2(fw_ext.Firewallv2PluginBase, base_db.CommonDbMixin): } def create_firewall_rule(self, context, firewall_rule): - LOG.debug("create_firewall_rule() called") - fwr = firewall_rule['firewall_rule'] + fwr = firewall_rule self._validate_fwr_protocol_parameters(fwr) self._validate_fwr_src_dst_ip_version(fwr) if not fwr['protocol'] and (fwr['source_port'] or @@ -450,8 +479,7 @@ class Firewall_db_mixin_v2(fw_ext.Firewallv2PluginBase, base_db.CommonDbMixin): return self._make_firewall_rule_dict(fwr_db) def update_firewall_rule(self, context, id, firewall_rule): - LOG.debug("update_firewall_rule() called") - fwr = firewall_rule['firewall_rule'] + fwr = firewall_rule fwr_db = self._get_firewall_rule(context, id) self._validate_fwr_protocol_parameters(fwr, fwr_db=fwr_db) self._validate_fwr_src_dst_ip_version(fwr, fwr_db=fwr_db) @@ -478,24 +506,21 @@ class Firewall_db_mixin_v2(fw_ext.Firewallv2PluginBase, base_db.CommonDbMixin): raise f_exc.FirewallRuleWithPortWithoutProtocolInvalid() fwr_db.update(fwr) # if the rule on a policy, fix audited flag - fwp_ids = self._get_policies_with_rule(context, id) + fwp_ids = self.get_policies_with_rule(context, id) for fwp_id in fwp_ids: fwp_db = self._get_firewall_policy(context, fwp_id) fwp_db['audited'] = False return self._make_firewall_rule_dict(fwr_db) def delete_firewall_rule(self, context, id): - LOG.debug("delete_firewall_rule() called") with context.session.begin(subtransactions=True): fwr = self._get_firewall_rule(context, id) # make sure rule is not associated with any policy - if self._get_policies_with_rule(context, id): + if self.get_policies_with_rule(context, id): raise f_exc.FirewallRuleInUse(firewall_rule_id=id) context.session.delete(fwr) def insert_rule(self, context, id, rule_info): - LOG.debug("insert_rule() called") - self._validate_insert_remove_rule_request(rule_info) firewall_rule_id = rule_info['firewall_rule_id'] # ensure rule is not already assigned to the policy self._ensure_rule_not_already_associated(context, id, firewall_rule_id) @@ -534,8 +559,6 @@ class Firewall_db_mixin_v2(fw_ext.Firewallv2PluginBase, base_db.CommonDbMixin): position, None) def remove_rule(self, context, id, rule_info): - LOG.debug("remove_rule() called") - self._validate_insert_remove_rule_request(rule_info) firewall_rule_id = rule_info['firewall_rule_id'] with context.session.begin(subtransactions=True): self._get_firewall_rule(context, firewall_rule_id) @@ -545,30 +568,15 @@ class Firewall_db_mixin_v2(fw_ext.Firewallv2PluginBase, base_db.CommonDbMixin): None, fwpra_db) def get_firewall_rule(self, context, id, fields=None): - LOG.debug("get_firewall_rule() called") fwr = self._get_firewall_rule(context, id) - policies = self._get_policies_with_rule(context, id) or None + policies = self.get_policies_with_rule(context, id) or None return self._make_firewall_rule_dict(fwr, fields, policies=policies) def get_firewall_rules(self, context, filters=None, fields=None): - LOG.debug("get_firewall_rules() called") return self._get_collection(context, FirewallRuleV2, self._make_firewall_rule_dict, filters=filters, fields=fields) - def _validate_insert_remove_rule_request(self, rule_info): - """Validate rule_info dict - - Check that all mandatory fields are present, otherwise raise - proper exception. - """ - if not rule_info or 'firewall_rule_id' not in rule_info: - raise f_exc.FirewallRuleInfoMissing() - # Validator doesn't return anything if the check passes - if validators.validate_uuid(rule_info['firewall_rule_id']): - raise f_exc.FirewallRuleNotFound( - firewall_rule_id=rule_info['firewall_rule_id']) - def _get_rules_in_policy(self, context, fwpid): """Gets rules in a firewall policy""" with context.session.begin(subtransactions=True): @@ -578,7 +586,7 @@ class Firewall_db_mixin_v2(fw_ext.Firewallv2PluginBase, base_db.CommonDbMixin): fwp_rules = [entry.firewall_rule_id for entry in fw_pol_rule_qry] return fwp_rules - def _get_policies_with_rule(self, context, fwrid): + def get_policies_with_rule(self, context, fwrid): """Gets rules in a firewall policy""" with context.session.begin(subtransactions=True): fw_pol_rule_qry = context.session.query( @@ -644,7 +652,7 @@ class Firewall_db_mixin_v2(fw_ext.Firewallv2PluginBase, base_db.CommonDbMixin): firewall_rule_id=fwr_db['id'], firewall_policy_id=fwp_db['id']) - def _get_fwgs_with_policy(self, context, fwp_id): + def get_fwgs_with_policy(self, context, fwp_id): with context.session.begin(subtransactions=True): fwg_ing_pol_qry = context.session.query( FirewallGroup).filter_by( @@ -719,18 +727,18 @@ class Firewall_db_mixin_v2(fw_ext.Firewallv2PluginBase, base_db.CommonDbMixin): description = kwargs.get('description', '') name = (const.DEFAULT_FWP_INGRESS if policy_type == 'ingress' else const.DEFAULT_FWP_EGRESS) - firewall_policy = {'firewall_policy': { + firewall_policy = { 'name': name, 'description': description, 'audited': False, 'shared': False, 'firewall_rules': fwrs, 'tenant_id': tenant_id, - }} + } return self._do_create_firewall_policy(context, firewall_policy) def _do_create_firewall_policy(self, context, firewall_policy): - fwp = firewall_policy['firewall_policy'] + fwp = firewall_policy with context.session.begin(subtransactions=True): fwp_db = FirewallPolicy( id=uuidutils.generate_uuid(), @@ -744,13 +752,11 @@ class Firewall_db_mixin_v2(fw_ext.Firewallv2PluginBase, base_db.CommonDbMixin): return self._make_firewall_policy_dict(fwp_db) def create_firewall_policy(self, context, firewall_policy): - LOG.debug("create_firewall_policy() called") self._ensure_not_default_resource(firewall_policy, 'firewall_policy') return self._do_create_firewall_policy(context, firewall_policy) def update_firewall_policy(self, context, id, firewall_policy): - LOG.debug("update_firewall_policy() called") - fwp = firewall_policy['firewall_policy'] + fwp = firewall_policy with context.session.begin(subtransactions=True): fwp_db = self._get_firewall_policy(context, id) self._ensure_not_default_resource(fwp_db, 'firewall_policy', @@ -772,7 +778,6 @@ class Firewall_db_mixin_v2(fw_ext.Firewallv2PluginBase, base_db.CommonDbMixin): return self._make_firewall_policy_dict(fwp_db) def delete_firewall_policy(self, context, id): - LOG.debug("delete_firewall_policy() called") with context.session.begin(subtransactions=True): fwp_db = self._get_firewall_policy(context, id) # check if policy in use @@ -786,35 +791,14 @@ class Firewall_db_mixin_v2(fw_ext.Firewallv2PluginBase, base_db.CommonDbMixin): context.session.delete(fwp_db) def get_firewall_policy(self, context, id, fields=None): - LOG.debug("get_firewall_policy() called") fwp = self._get_firewall_policy(context, id) return self._make_firewall_policy_dict(fwp, fields) def get_firewall_policies(self, context, filters=None, fields=None): - LOG.debug("get_firewall_policies() called") return self._get_collection(context, FirewallPolicy, self._make_firewall_policy_dict, filters=filters, fields=fields) - def _validate_tenant_for_fwg_policies(self, context, fwg, fwg_tenant_id): - # On updates, all keys will not be present so fetch and validate. - if 'ingress_firewall_policy_id' in fwg: - fwp_id = fwg['ingress_firewall_policy_id'] - if fwp_id is not None: - fwp = self._get_firewall_policy(context, fwp_id) - if fwg_tenant_id != fwp['tenant_id'] and not fwp['shared']: - raise f_exc.FirewallPolicyConflict( - firewall_policy_id=fwp_id) - - if 'egress_firewall_policy_id' in fwg: - fwp_id = fwg['egress_firewall_policy_id'] - if fwp_id is not None: - fwp = self._get_firewall_policy(context, fwp_id) - if fwg_tenant_id != fwp['tenant_id'] and not fwp['shared']: - raise f_exc.FirewallPolicyConflict( - firewall_policy_id=fwp_id) - return - def _set_ports_for_firewall_group(self, context, fwg_db, fwg): port_id_list = fwg['ports'] if not port_id_list: @@ -833,7 +817,7 @@ class Firewall_db_mixin_v2(fw_ext.Firewallv2PluginBase, base_db.CommonDbMixin): if exc_ports: raise f_exc.FirewallGroupPortInUse(port_ids=exc_ports) - def _get_ports_in_firewall_group(self, context, firewall_group_id): + def get_ports_in_firewall_group(self, context, firewall_group_id): """Get the Ports associated with the firewall group.""" with context.session.begin(subtransactions=True): fw_group_port_qry = context.session.query( @@ -852,27 +836,13 @@ class Firewall_db_mixin_v2(fw_ext.Firewallv2PluginBase, base_db.CommonDbMixin): firewall_group_id=firewall_group_id).delete() return - def _validate_if_firewall_group_on_ports( - self, context, port_ids, fwg_id=None): - """Validate if ports are not associated with any firewall_group. - If any of the ports in the list is already associated with - a firewall_group, raise an exception else just return. - """ - fwg_port_qry = context.session.query( - FirewallGroupPortAssociation.port_id) - fwg_ports = fwg_port_qry.filter( - FirewallGroupPortAssociation.port_id.in_(port_ids), - FirewallGroupPortAssociation.firewall_group_id != fwg_id).all() - if fwg_ports: - port_ids = [entry.port_id for entry in fwg_ports] - raise f_exc.FirewallGroupPortInUse(port_ids=port_ids) - def _get_default_fwg_id(self, context, tenant_id): """Returns an id of default firewall group for given tenant or None""" default_fwg = self._model_query(context, FirewallGroup).filter_by( project_id=tenant_id, name=const.DEFAULT_FWG).first() if default_fwg: return default_fwg.id + return None def _ensure_default_firewall_group(self, context, tenant_id): """Create a default firewall group if one doesn't exist for a tenant @@ -905,19 +875,18 @@ class Firewall_db_mixin_v2(fw_ext.Firewallv2PluginBase, base_db.CommonDbMixin): context, tenant_id, 'egress', **egress_fwp) fwg = { - 'firewall_group': - {'name': const.DEFAULT_FWG, - 'tenant_id': tenant_id, - 'ingress_firewall_policy_id': ingress_fwp_db['id'], - 'egress_firewall_policy_id': egress_fwp_db['id'], - 'ports': [], - 'shared': False, - 'admin_state_up': True, - 'description': 'Default firewall group'} + 'name': const.DEFAULT_FWG, + 'tenant_id': tenant_id, + 'ingress_firewall_policy_id': ingress_fwp_db['id'], + 'egress_firewall_policy_id': egress_fwp_db['id'], + 'ports': [], + 'shared': False, + 'status': nl_constants.INACTIVE, + 'admin_state_up': True, + 'description': 'Default firewall group', } fwg_db = self._create_firewall_group( - context, fwg, status=nl_constants.INACTIVE, - default_fwg=True) + context, fwg, default_fwg=True) context.session.add(DefaultFirewallGroup( firewall_group_id=fwg_db['id'], project_id=tenant_id)) @@ -928,37 +897,36 @@ class Firewall_db_mixin_v2(fw_ext.Firewallv2PluginBase, base_db.CommonDbMixin): LOG.debug("Default FWG was concurrently created") return self._get_default_fwg_id(context, tenant_id) - def _create_firewall_group(self, context, firewall_group, status=None, + def _create_firewall_group(self, context, firewall_group, default_fwg=False): """Create a firewall group If default_fwg is True then a default firewall group is being created for a given tenant. """ - fwg = firewall_group['firewall_group'] + fwg = firewall_group tenant_id = fwg['tenant_id'] - if not status: - status = (nl_constants.CREATED if cfg.CONF.router_distributed - else nl_constants.PENDING_CREATE) + if firewall_group.get('status') is None: + fwg['status'] = nl_constants.CREATED + if default_fwg: # A default firewall group is being created. default_fwg_id = self._get_default_fwg_id(context, tenant_id) if default_fwg_id is not None: # Default fwg for a given tenant exists, fetch it and return - return self.get_firewall_group(default_fwg_id) + return self.get_firewall_group(context, default_fwg_id) else: # An ordinary firewall group is being created BUT let's make sure # that a default firewall group for given tenant exists self._ensure_default_firewall_group(context, tenant_id) - self._validate_tenant_for_fwg_policies(context, fwg, tenant_id) with context.session.begin(subtransactions=True): fwg_db = FirewallGroup( id=uuidutils.generate_uuid(), tenant_id=tenant_id, name=fwg['name'], description=fwg['description'], - status=status, + status=fwg['status'], ingress_firewall_policy_id=fwg['ingress_firewall_policy_id'], egress_firewall_policy_id=fwg['egress_firewall_policy_id'], admin_state_up=fwg['admin_state_up'], @@ -967,15 +935,14 @@ class Firewall_db_mixin_v2(fw_ext.Firewallv2PluginBase, base_db.CommonDbMixin): self._set_ports_for_firewall_group(context, fwg_db, fwg) return self._make_firewall_group_dict(fwg_db) - def create_firewall_group(self, context, firewall_group, status=None): + def create_firewall_group(self, context, firewall_group): self._ensure_not_default_resource(firewall_group, 'firewall_group') - return self._create_firewall_group(context, firewall_group, status) + return self._create_firewall_group(context, firewall_group) def update_firewall_group(self, context, id, firewall_group): - LOG.debug("update_firewall_group() called") - fwg = firewall_group['firewall_group'] + fwg = firewall_group # make sure that no group can be updated to have name=default - self._ensure_not_default_resource(firewall_group, 'firewall_group') + self._ensure_not_default_resource(fwg, 'firewall_group') with context.session.begin(subtransactions=True): fwg_db = self.get_firewall_group(context, id) if _is_default(fwg_db): @@ -990,8 +957,6 @@ class Firewall_db_mixin_v2(fw_ext.Firewallv2PluginBase, base_db.CommonDbMixin): raise FirewallDefaultObjectUpdateRestricted( resource_type='Firewall Group', resource_id=fwg_db['id']) - self._validate_tenant_for_fwg_policies(context, - fwg, fwg_db['tenant_id']) if 'ports' in fwg: LOG.debug("Ports are updated in Firewall Group") self._delete_ports_in_firewall_group(context, id) @@ -1021,7 +986,6 @@ class Firewall_db_mixin_v2(fw_ext.Firewallv2PluginBase, base_db.CommonDbMixin): def delete_firewall_group(self, context, id): # Note: Plugin should ensure that it's okay to delete if the # firewall is active - LOG.debug("delete_firewall_group() called") with context.session.begin(subtransactions=True): # if no such group exists -> don't raise an exception according to @@ -1077,12 +1041,10 @@ class Firewall_db_mixin_v2(fw_ext.Firewallv2PluginBase, base_db.CommonDbMixin): resource_type='Firewall Policy', name=resource['name']) def get_firewall_group(self, context, id, fields=None): - LOG.debug("get_firewall_group() called") fw = self._get_firewall_group(context, id) return self._make_firewall_group_dict(fw, fields) def get_firewall_groups(self, context, filters=None, fields=None): - LOG.debug("get_firewall_groups() called") if context.tenant_id: tenant_id = filters.get('tenant_id') if filters else None tenant_id = tenant_id[0] if tenant_id else context.tenant_id @@ -1091,48 +1053,6 @@ class Firewall_db_mixin_v2(fw_ext.Firewallv2PluginBase, base_db.CommonDbMixin): self._make_firewall_group_dict, filters=filters, fields=fields) - def get_firewall_group_for_port(self, context, port_id): - """Get firewall group is associated with a port - - :param context: context object - :param port_id: Port ID. - """ - filters = {'port_id': [port_id]} - fwg_port_binding = self._get_collection_query( - context, FirewallGroupPortAssociation, filters=filters).first() - if fwg_port_binding: - fwg_id = fwg_port_binding['firewall_group_id'] - return self._make_firewall_group_dict_with_rules(context, fwg_id) - - def _get_default_fwg(self, context, project_id): - query = self._model_query(context, DefaultFirewallGroup) - def_fwg_id = query.filter_by( - project_id=project_id).one().firewall_group_id - return self.get_firewall_group(context, def_fwg_id) - - def set_port_for_default_firewall_group(self, context, port_id, - project_id): - """Set a port into default firewall group - - :param context: Context object - :param port_id: Port ID - :param project_id: ProjectID - """ - with context.session.begin(subtransactions=True): - def_fwg_db = self._get_default_fwg(context, project_id) - try: - self._set_ports_for_firewall_group( - context, def_fwg_db, {'ports': [port_id]}) - except f_exc.FirewallGroupPortInUse: - LOG.warning("Port %s has been already associated with default " - "firewall group %s and skip association", port_id, - def_fwg_db['id']) - else: - # Update default fwg status to PENDING_UPDATE to wait updating - # from agent - self.update_firewall_group_status( - context, def_fwg_db['id'], nl_constants.PENDING_UPDATE) - def _is_default(fwg_db): return fwg_db['name'] == const.DEFAULT_FWG diff --git a/neutron_fwaas/extensions/firewall_v2.py b/neutron_fwaas/extensions/firewall_v2.py index f92d718c0..25aff1578 100644 --- a/neutron_fwaas/extensions/firewall_v2.py +++ b/neutron_fwaas/extensions/firewall_v2.py @@ -126,6 +126,7 @@ class Firewallv2PluginBase(service_base.ServicePluginBase): def get_plugin_description(self): return 'Firewall Service v2 Plugin' + # Firewall Group @abc.abstractmethod def create_firewall_group(self, context, firewall_group): pass @@ -135,7 +136,7 @@ class Firewallv2PluginBase(service_base.ServicePluginBase): pass @abc.abstractmethod - def get_firewall_group(self, context, id): + def get_firewall_group(self, context, id, fields=None): pass @abc.abstractmethod @@ -146,24 +147,13 @@ class Firewallv2PluginBase(service_base.ServicePluginBase): def update_firewall_group(self, context, id, firewall_group): pass + # Firewall Policy @abc.abstractmethod - def get_firewall_rules(self, context, filters=None, fields=None): + def create_firewall_policy(self, context, firewall_policy): pass @abc.abstractmethod - def get_firewall_rule(self, context, id, fields=None): - pass - - @abc.abstractmethod - def create_firewall_rule(self, context, firewall_rule): - pass - - @abc.abstractmethod - def update_firewall_rule(self, context, id, firewall_rule): - pass - - @abc.abstractmethod - def delete_firewall_rule(self, context, id): + def delete_firewall_policy(self, context, id): pass @abc.abstractmethod @@ -174,16 +164,29 @@ class Firewallv2PluginBase(service_base.ServicePluginBase): def get_firewall_policies(self, context, filters=None, fields=None): pass - @abc.abstractmethod - def create_firewall_policy(self, context, firewall_policy): - pass - @abc.abstractmethod def update_firewall_policy(self, context, id, firewall_policy): pass + # Firewall Rule @abc.abstractmethod - def delete_firewall_policy(self, context, id): + def create_firewall_rule(self, context, firewall_rule): + pass + + @abc.abstractmethod + def delete_firewall_rule(self, context, id): + pass + + @abc.abstractmethod + def get_firewall_rule(self, context, id, fields=None): + pass + + @abc.abstractmethod + def get_firewall_rules(self, context, filters=None, fields=None): + pass + + @abc.abstractmethod + def update_firewall_rule(self, context, id, firewall_rule): pass @abc.abstractmethod diff --git a/neutron_fwaas/opts.py b/neutron_fwaas/opts.py index 017042d0a..069445d39 100644 --- a/neutron_fwaas/opts.py +++ b/neutron_fwaas/opts.py @@ -10,19 +10,25 @@ # License for the specific language governing permissions and limitations # under the License. +import neutron.conf.services.provider_configuration + +import neutron_fwaas.services.firewall.service_drivers.agents.\ + firewall_agent_api import neutron_fwaas.extensions.firewall -import neutron_fwaas.services.firewall.agents.firewall_agent_api def list_agent_opts(): return [ ('fwaas', - neutron_fwaas.services.firewall.agents.firewall_agent_api.FWaaSOpts) + neutron_fwaas.services.firewall.service_drivers.agents. + firewall_agent_api.FWaaSOpts), ] def list_opts(): return [ ('quotas', - neutron_fwaas.extensions.firewall.firewall_quota_opts) + neutron_fwaas.extensions.firewall.firewall_quota_opts), + ('service_providers', + neutron.conf.services.provider_configuration.serviceprovider_opts), ] diff --git a/neutron_fwaas/services/firewall/fwaas_plugin_v2.py b/neutron_fwaas/services/firewall/fwaas_plugin_v2.py index 0dc6457d4..8004b0427 100644 --- a/neutron_fwaas/services/firewall/fwaas_plugin_v2.py +++ b/neutron_fwaas/services/firewall/fwaas_plugin_v2.py @@ -12,238 +12,138 @@ # License for the specific language governing permissions and limitations # under the License. -from neutron.common import rpc as n_rpc from neutron.db import servicetype_db as st_db from neutron.services import provider_configuration as provider_conf +from neutron.services import service_base from neutron_lib.api.definitions import firewall_v2 from neutron_lib.api.definitions import portbindings as pb_def +from neutron_lib.api import validators from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants as nl_constants -from neutron_lib import context as neutron_context from neutron_lib.exceptions import firewall_v2 as f_exc -from neutron_lib.plugins import constants as plugin_const from neutron_lib.plugins import directory -from oslo_config import cfg +from oslo_log import helpers as log_helpers from oslo_log import log as logging -import oslo_messaging from neutron_fwaas.common import exceptions from neutron_fwaas.common import fwaas_constants -from neutron_fwaas.db.firewall.v2 import firewall_db_v2 +from neutron_fwaas.extensions.firewall_v2 import Firewallv2PluginBase LOG = logging.getLogger(__name__) -def add_provider_configuration(type_manager, service_type): - type_manager.add_provider_configuration( - service_type, - provider_conf.ProviderConfiguration('neutron_fwaas')) - - -class FirewallAgentApi(object): - """Plugin side of plugin to agent RPC API.""" - - def __init__(self, topic, host): - self.host = host - target = oslo_messaging.Target(topic=topic, version='1.0') - self.client = n_rpc.get_client(target) - - def create_firewall_group(self, context, firewall_group): - cctxt = self.client.prepare(fanout=True) - cctxt.cast(context, 'create_firewall_group', - firewall_group=firewall_group, - host=self.host) - - def update_firewall_group(self, context, firewall_group): - cctxt = self.client.prepare(fanout=True) - cctxt.cast(context, 'update_firewall_group', - firewall_group=firewall_group, - host=self.host) - - def delete_firewall_group(self, context, firewall_group): - cctxt = self.client.prepare(fanout=True) - cctxt.cast(context, 'delete_firewall_group', - firewall_group=firewall_group, - host=self.host) - - -class FirewallCallbacks(object): - target = oslo_messaging.Target(version='1.0') - - def __init__(self, plugin): - super(FirewallCallbacks, self).__init__() - self.plugin = plugin - - def set_firewall_group_status(self, context, fwg_id, status, **kwargs): - """Agent uses this to set a firewall_group's status.""" - LOG.debug("Setting firewall_group %s to status: %s", - fwg_id, status) - # Sanitize status first - if status in (nl_constants.ACTIVE, nl_constants.DOWN, - nl_constants.INACTIVE): - to_update = status - else: - to_update = nl_constants.ERROR - # ignore changing status if firewall_group expects to be deleted - # That case means that while some pending operation has been - # performed on the backend, neutron server received delete request - # and changed firewall status to PENDING_DELETE - updated = self.plugin.update_firewall_group_status( - context, fwg_id, to_update, not_in=(nl_constants.PENDING_DELETE,)) - if updated: - LOG.debug("firewall %s status set: %s", fwg_id, to_update) - return updated and to_update != nl_constants.ERROR - - def firewall_group_deleted(self, context, fwg_id, **kwargs): - """Agent uses this to indicate firewall is deleted.""" - LOG.debug("firewall_group_deleted() called") - try: - with context.session.begin(subtransactions=True): - fwg_db = self.plugin._get_firewall_group(context, fwg_id) - # allow to delete firewalls in ERROR state - if fwg_db.status in (nl_constants.PENDING_DELETE, - nl_constants.ERROR): - self.plugin.delete_db_firewall_group_object(context, - fwg_id) - return True - else: - LOG.warning(('Firewall %(fwg)s unexpectedly deleted by ' - 'agent, status was %(status)s'), - {'fwg': fwg_id, 'status': fwg_db.status}) - fwg_db.update({"status": nl_constants.ERROR}) - return False - except f_exc.FirewallGroupNotFound: - LOG.info('Firewall group %s already deleted', fwg_id) - return True - - def get_firewall_groups_for_project(self, context, **kwargs): - """Gets all firewall_groups and rules on a project.""" - LOG.debug("get_firewall_groups_for_project() called") - fwg_list = [] - for fwg in self.plugin.get_firewall_groups(context): - fwg_with_rules = self.plugin._make_firewall_group_dict_with_rules( - context, fwg['id']) - if fwg['status'] == nl_constants.PENDING_DELETE: - fwg_with_rules['add-port-ids'] = [] - fwg_with_rules['del-port-ids'] = ( - self.plugin._get_ports_in_firewall_group(context, - fwg['id'])) - else: - fwg_with_rules['add-port-ids'] = ( - self.plugin._get_ports_in_firewall_group(context, - fwg['id'])) - fwg_with_rules['del-port-ids'] = [] - fwg_list.append(fwg_with_rules) - return fwg_list - - def get_projects_with_firewall_groups(self, context, **kwargs): - """Get all projects that have firewall_groups.""" - LOG.debug("get_projects_with_firewall_groups() called") - ctx = neutron_context.get_admin_context() - fwg_list = self.plugin.get_firewall_groups(ctx) - fwg_project_list = list(set(fwg['tenant_id'] for fwg in fwg_list)) - return fwg_project_list - - def get_firewall_group_for_port(self, context, **kwargs): - """Get firewall_group is associated with a port.""" - LOG.debug("get_firewall_group_for_port() called") - ctx = context.elevated() - return self.plugin.get_firewall_group_for_port( - ctx, kwargs.get('port_id')) - - @registry.has_registry_receivers -class FirewallPluginV2( - firewall_db_v2.Firewall_db_mixin_v2): - """Implementation of the Neutron Firewall Service Plugin. +class FirewallPluginV2(Firewallv2PluginBase): + """Firewall v2 Neutron service plugin class""" - This class manages the workflow of FWaaS request/response. - Most DB related works are implemented in class - firewall_db_v2.Firewall_db_mixin_v2. - """ - supported_extension_aliases = ["fwaas_v2"] + supported_extension_aliases = [firewall_v2.ALIAS] path_prefix = firewall_v2.API_PREFIX def __init__(self): + super(FirewallPluginV2, self).__init__() """Do the initialization for the firewall service plugin here.""" - self.service_type_manager = st_db.ServiceTypeManager.get_instance() - add_provider_configuration( - self.service_type_manager, plugin_const.FIREWALL) - self.start_rpc_listeners() + # Initialize the Firewall v2 service plugin + service_type_manager = st_db.ServiceTypeManager.get_instance() + service_type_manager.add_provider_configuration( + fwaas_constants.FIREWALL_V2, + provider_conf.ProviderConfiguration('neutron_fwaas')) - self.agent_rpc = FirewallAgentApi( - fwaas_constants.FW_AGENT, - cfg.CONF.host - ) + # Load the default driver + drivers, default_provider = service_base.load_drivers( + fwaas_constants.FIREWALL_V2, self) + LOG.info("Firewall v2 Service Plugin using Service Driver: %s", + default_provider) + + if len(drivers) > 1: + LOG.warning("Multiple drivers configured for Firewall v2, " + "although running multiple drivers in parallel is " + "not yet supported") + + self.driver = drivers[default_provider] @property def _core_plugin(self): return directory.get_plugin() - def start_rpc_listeners(self): - self.endpoints = [FirewallCallbacks(self)] - - self.conn = n_rpc.Connection() - self.conn.create_consumer( - fwaas_constants.FIREWALL_PLUGIN, self.endpoints, fanout=False) - return self.conn.consume_in_threads() - - def _rpc_update_firewall_group(self, context, fwg_id): - status_update = {"firewall_group": {"status": - nl_constants.PENDING_UPDATE}} - super(FirewallPluginV2, self).update_firewall_group( - context, fwg_id, status_update) - fwg_with_rules = self._make_firewall_group_dict_with_rules(context, - fwg_id) - # this is triggered on an update to fwg rule or policy, no - # change in associated ports. - fwg_with_rules['add-port-ids'] = self._get_ports_in_firewall_group( - context, fwg_id) - fwg_with_rules['del-port-ids'] = [] - fwg_with_rules['port_details'] = self._get_fwg_port_details( - context, fwg_with_rules['add-port-ids']) - self.agent_rpc.update_firewall_group(context, fwg_with_rules) - - def _rpc_update_firewall_policy(self, context, firewall_policy_id): - firewall_policy = self.get_firewall_policy(context, firewall_policy_id) - if firewall_policy: - ing_fwg_ids, eg_fwg_ids = self._get_fwgs_with_policy(context, - firewall_policy_id) - for fwg_id in list(set(ing_fwg_ids + eg_fwg_ids)): - self._rpc_update_firewall_group(context, fwg_id) - def _ensure_update_firewall_group(self, context, fwg_id): + """Checks if the firewall group can be updated + + Raises FirewallGroupInPendingState if the firewall group is in pending + state. + :param context: neutron context + :param fwg_id: firewall group ID to check + :return: Firewall group dict + """ fwg = self.get_firewall_group(context, fwg_id) if fwg['status'] in [nl_constants.PENDING_CREATE, nl_constants.PENDING_UPDATE, nl_constants.PENDING_DELETE]: - raise f_exc.FirewallGroupInPendingState(firewall_id=fwg_id, - pending_state=fwg['status']) + raise f_exc.FirewallGroupInPendingState( + firewall_id=fwg_id, pending_state=fwg['status']) + return fwg - def _ensure_update_firewall_policy(self, context, firewall_policy_id): - firewall_policy = self.get_firewall_policy(context, firewall_policy_id) - if firewall_policy: - ing_fwg_ids, eg_fwg_ids = self._get_fwgs_with_policy( - context, firewall_policy_id) - for fwg_id in list(set(ing_fwg_ids + eg_fwg_ids)): - self._ensure_update_firewall_group(context, fwg_id) + def _ensure_update_firewall_policy(self, context, fwp_id): + """Checks if the firewall policy can be updated + + Fetch firewall group associated to the policy and checks if they can be + updated. + :param context: neutron context + :param fwp_id: firewall policy ID to check + """ + fwp = self.get_firewall_policy(context, fwp_id) + ing_fwg_ids, eg_fwg_ids = self._get_fwgs_with_policy(context, fwp) + for fwg_id in list(set(ing_fwg_ids + eg_fwg_ids)): + self._ensure_update_firewall_group(context, fwg_id) def _ensure_update_firewall_rule(self, context, fwr_id): - fwp_ids = self._get_policies_with_rule(context, fwr_id) + """Checks if the firewall rule can be updated + + Fetch firewall policy associated to the rule and checks if they can be + updated. + :param context: neutron context + :param fwr_id: firewall policy ID to check + """ + fwr = self.get_firewall_rule(context, fwr_id) + fwp_ids = self._get_policies_with_rule(context, fwr) for fwp_id in fwp_ids: self._ensure_update_firewall_policy(context, fwp_id) + def _validate_firewall_policies_for_firewall_group(self, context, fwg): + """Validate firewall group and policy owner + + Check if the firewall policy is not shared, it have the same project + owner than the friewall group. + :param context: neutron context + :param fwg: firewall group to validate + """ + for policy_type in ['ingress_firewall_policy_id', + 'egress_firewall_policy_id']: + if fwg.get(policy_type): + fwp = self.get_firewall_policy(context, fwg[policy_type]) + if fwg['tenant_id'] != fwp['tenant_id'] and not fwp['shared']: + raise f_exc.FirewallPolicyConflict( + firewall_policy_id=fwg[policy_type]) + def _validate_ports_for_firewall_group(self, context, tenant_id, fwg_ports): + """Validate firewall group associated ports + + Check if the firewall group associated ports have the same project + owner and is router interface type or a compute layer 2. + :param context: neutron context + :param tenant_id: firewall group project ID + :param fwg_ports: firewall group associated ports + """ # TODO(sridar): elevated context and do we want to use public ? for port_id in fwg_ports: - port_db = self._core_plugin._get_port(context, port_id) - if port_db['tenant_id'] != tenant_id: + port = self._core_plugin.get_port(context, port_id) + + if port['tenant_id'] != tenant_id: raise f_exc.FirewallGroupPortInvalidProject( - port_id=port_id, project_id=port_db['tenant_id']) - device_owner = port_db.get('device_owner', '') + port_id=port_id, project_id=port['tenant_id']) + device_owner = port.get('device_owner', '') if (device_owner not in [nl_constants.DEVICE_OWNER_ROUTER_INTF] and not device_owner.startswith( nl_constants.DEVICE_OWNER_COMPUTE_PREFIX)): @@ -253,6 +153,8 @@ class FirewallPluginV2( self._is_supported_by_fw_l2_driver(context, port_id)): raise exceptions.FirewallGroupPortNotSupported(port_id=port_id) + # TODO(ethuleau): move that check in the driver. Each driver can have + # different support def _is_supported_by_fw_l2_driver(self, context, port_id): """Whether this port is supported by firewall l2 driver""" @@ -277,49 +179,80 @@ class FirewallPluginV2( LOG.warning("Doesn't support vif type %s", port[pb_def.VIF_TYPE]) return False - def _check_no_need_pending(self, context, fwg_id, fwg_body): - fwg_db = self._get_firewall_group(context, fwg_id) - fwp_req_in = fwg_body.get('ingress_firewall_policy_id', None) - fwp_req_eg = fwg_body.get('egress_firewall_policy_id', None) + def _validate_if_firewall_group_on_ports(self, context, firewall_group, + id=None): + """Validate if ports are not associated with any firewall_group. - if ((not fwg_db.ingress_firewall_policy_id and - fwp_req_in is fwg_db.ingress_firewall_policy_id) and - (not fwg_db.egress_firewall_policy_id and - fwp_req_eg is fwg_db.ingress_firewall_policy_id)): - return True - return False + If any of the ports in the list is already associated with + a firewall group, raise an exception else just return. + :param context: neutron context + :param fwg: firewall group to validate + """ + if 'ports' not in firewall_group or not firewall_group['ports']: + return - def _get_fwg_port_details(self, context, fwg_ports): - """Returns a dictionary list of port details. """ - port_details = {} - for port_id in fwg_ports: - port_db = self._core_plugin.get_port(context, port_id) - # Add more parameters below based on requirement. - device_owner = port_db['device_owner'] - port_details[port_id] = { - 'device_owner': device_owner, - 'device': port_db['id'], - 'network_id': port_db['network_id'], - 'fixed_ips': port_db['fixed_ips'], - 'allowed_address_pairs': - port_db.get('allowed_address_pairs', []), - 'port_security_enabled': - port_db.get('port_security_enabled', True), - 'id': port_db['id'] - } - if device_owner.startswith( - nl_constants.DEVICE_OWNER_COMPUTE_PREFIX): - port_details[port_id].update( - {'host': port_db[pb_def.HOST_ID]}) - return port_details + filters = { + 'tenant_id': [firewall_group['tenant_id']], + 'ports': firewall_group['ports'], + } + ports_in_use = set() + for fwg in self.get_firewall_groups(context, filters=filters): + if id is not None and fwg['id'] == id: + continue + ports_in_use |= set(fwg.get('ports', [])) & \ + set(firewall_group['ports']) + if ports_in_use: + raise f_exc.FirewallGroupPortInUse(port_ids=list(ports_in_use)) - def get_project_id_from_port_id(self, context, port_id): - """Returns an ID of project for specified port_id. """ - return self._core_plugin.get_port(context, port_id)['project_id'] + def _get_fwgs_with_policy(self, context, firewall_policy): + """List firewall group IDs which use a firewall policy + + List all firewall group IDs which have the given firewall policy as + ingress or egress. + :param context: neutron context + :param firewall_policy: firewall policy to filter + """ + filters = { + 'tenant_id': [firewall_policy['tenant_id']], + 'ingress_firewall_policy_id': [firewall_policy['id']], + } + ingress_fwp_ids = [fwg['id'] + for fwg in self.get_firewall_groups( + context, filters=filters)] + + filters = { + 'tenant_id': [firewall_policy['tenant_id']], + 'egress_firewall_policy_id': [firewall_policy['id']], + } + egress_fwp_ids = [fwg['id'] + for fwg in self.get_firewall_groups( + context, filters=filters)] + + return ingress_fwp_ids, egress_fwp_ids + + def _get_policies_with_rule(self, context, firewall_rule): + filters = { + 'tenant_id': [firewall_rule['tenant_id']], + 'firewall_rules': [firewall_rule['id']], + } + return [fwp['id'] for fwp in self.get_firewall_policies( + context, filters=filters)] + + def _validate_insert_remove_rule_request(self, rule_info): + """Validate rule_info dict + + Check that all mandatory fields are present, otherwise raise + proper exception. + """ + if not rule_info or 'firewall_rule_id' not in rule_info: + raise f_exc.FirewallRuleInfoMissing() + # Validator doesn't return anything if the check passes + if validators.validate_uuid(rule_info['firewall_rule_id']): + raise f_exc.FirewallRuleNotFound( + firewall_rule_id=rule_info['firewall_rule_id']) @registry.receives(resources.PORT, [events.AFTER_UPDATE]) def handle_update_port(self, resource, event, trigger, **kwargs): - updated_port = kwargs['port'] if not updated_port['device_owner'].startswith( nl_constants.DEVICE_OWNER_COMPUTE_PREFIX): @@ -340,169 +273,145 @@ class FirewallPluginV2( return project_id = updated_port['project_id'] - LOG.debug("Try to associate port %s at %s", port_id, project_id) - self.set_port_for_default_firewall_group(context, port_id, project_id) + fwgs = self.get_firewall_groups( + context, + filters={ + 'tenant_id': [project_id], + 'name': [fwaas_constants.DEFAULT_FWG], + }, + fields=['id', 'ports'], + ) + if len(fwgs) != 1: + # Cannot found default Firewall Group, abandon + LOG.warning("Cannot found default firewall group of project %s", + project_id) + return + default_fwg = fwgs[0] + # Add default firewall group to the port + port_ids = default_fwg.get('ports', []) + [port_id] + try: + self.update_firewall_group(context, default_fwg['id'], + {'firewall_group': {'ports': port_ids}}) + except f_exc.FirewallGroupPortInUse: + LOG.warning("Port %s has been already associated with default " + "firewall group %s and skip association", port_id, + default_fwg['id']) + + # Firewall Group + @log_helpers.log_method_call def create_firewall_group(self, context, firewall_group): - LOG.debug("create_firewall_group() called") - fwgrp = firewall_group['firewall_group'] - fwg_ports = fwgrp['ports'] + firewall_group = firewall_group['firewall_group'] + ports = firewall_group.get('ports', []) - if not fwg_ports: - # no messaging to agent needed, and fw needs to go - # to INACTIVE(no associated ports) state. - status = nl_constants.INACTIVE - fwg = super(FirewallPluginV2, self).create_firewall_group( - context, firewall_group, status) - fwg['ports'] = [] - return fwg - else: - # Validate ports - self._validate_ports_for_firewall_group(context, - firewall_group['firewall_group']['tenant_id'], fwg_ports) + self._validate_firewall_policies_for_firewall_group(context, + firewall_group) + # Validate ports owner type and project + self._validate_ports_for_firewall_group(context, + firewall_group['tenant_id'], + ports) - if (not fwgrp['ingress_firewall_policy_id'] and - not fwgrp['egress_firewall_policy_id']): - # No policy configured - status = nl_constants.INACTIVE - fwg = super(FirewallPluginV2, self).create_firewall_group( - context, firewall_group, status) - return fwg + self._validate_if_firewall_group_on_ports(context, firewall_group) - fwg = super(FirewallPluginV2, self).create_firewall_group( - context, firewall_group) - fwg['ports'] = fwg_ports - - fwg_with_rules = ( - self._make_firewall_group_dict_with_rules(context, fwg['id'])) - - fwg_with_rules['add-port-ids'] = fwg_ports - fwg_with_rules['del-port-ids'] = [] - fwg_with_rules['port_details'] = self._get_fwg_port_details( - context, fwg_ports) - - self.agent_rpc.create_firewall_group(context, fwg_with_rules) - - return fwg - - def update_firewall_group(self, context, id, firewall_group): - LOG.debug("update_firewall_group() called on firewall_group %s", id) - - self._ensure_update_firewall_group(context, id) - - # TODO(sridar): need closure on status when no policy associated. - fwg_current_ports = fwg_new_ports = self._get_ports_in_firewall_group( - context, id) - if 'ports' in firewall_group['firewall_group']: - fwg_new_ports = firewall_group['firewall_group']['ports'] - if len(fwg_new_ports) > 0: - self._validate_ports_for_firewall_group( - context, context.project_id, fwg_new_ports) - - if ((not fwg_new_ports and not fwg_current_ports) or - self._check_no_need_pending(context, - id, firewall_group['firewall_group'])): - # no messaging to agent needed, and we need to continue - # in INACTIVE state - firewall_group['firewall_group']['status'] = nl_constants.INACTIVE - fwg = super(FirewallPluginV2, self).update_firewall_group( - context, id, firewall_group) - if fwg_new_ports: - fwg['ports'] = fwg_new_ports - elif not fwg_new_ports and fwg_current_ports: - fwg['ports'] = fwg_current_ports - else: - fwg['ports'] = [] - return fwg - else: - firewall_group['firewall_group']['status'] = (nl_constants. - PENDING_UPDATE) - fwg = super(FirewallPluginV2, self).update_firewall_group( - context, id, firewall_group) - fwg['ports'] = fwg_new_ports - - fwg_with_rules = ( - self._make_firewall_group_dict_with_rules(context, fwg['id'])) - - # determine ports to add fw to and del from - fwg_with_rules['add-port-ids'] = fwg_new_ports - fwg_with_rules['del-port-ids'] = list( - set(fwg_current_ports).difference(set(fwg_new_ports))) - - # last-port drives agent to ack with status to set state to INACTIVE - fwg_with_rules['last-port'] = not fwg_new_ports - - LOG.debug("update_firewall_group %s: Add Ports: %s, Del Ports: %s", - fwg['id'], - fwg_with_rules['add-port-ids'], - fwg_with_rules['del-port-ids']) - - fwg_with_rules['port_details'] = self._get_fwg_port_details( - context, fwg_with_rules['del-port-ids']) - fwg_with_rules['port_details'].update(self._get_fwg_port_details( - context, fwg_with_rules['add-port-ids'])) - self.agent_rpc.update_firewall_group(context, fwg_with_rules) - - return fwg - - def delete_db_firewall_group_object(self, context, id): - super(FirewallPluginV2, self).delete_firewall_group(context, id) + return self.driver.create_firewall_group(context, firewall_group) + @log_helpers.log_method_call def delete_firewall_group(self, context, id): - LOG.debug("delete_firewall_group() called on firewall_group %s", id) + # if no such group exists -> don't raise an exception according to + # 80fe2ba1, return None + try: + fwg = self.get_firewall_group(context, id) + except f_exc.FirewallGroupNotFound: + return - fwg_db = self._get_firewall_group(context, id) - - if fwg_db['status'] == nl_constants.ACTIVE: + if fwg['status'] == nl_constants.ACTIVE: raise f_exc.FirewallGroupInUse(firewall_id=id) - fwg_with_rules = ( - self._make_firewall_group_dict_with_rules(context, id)) - fwg_with_rules['del-port-ids'] = self._get_ports_in_firewall_group( - context, id) - fwg_with_rules['add-port-ids'] = [] - if not fwg_with_rules['del-port-ids']: - # no ports, no need to talk to the agent - self.delete_db_firewall_group_object(context, id) - else: - status = {"firewall_group": {"status": - nl_constants.PENDING_DELETE}} - super(FirewallPluginV2, self).update_firewall_group( - context, id, status) - # Reflect state change in fwg_with_rules - fwg_with_rules['status'] = status['firewall_group']['status'] - fwg_with_rules['port_details'] = self._get_fwg_port_details( - context, fwg_with_rules['del-port-ids']) - self.agent_rpc.delete_firewall_group(context, fwg_with_rules) + self.driver.delete_firewall_group(context, id) + @log_helpers.log_method_call + def get_firewall_group(self, context, id, fields=None): + return self.driver.get_firewall_group(context, id, fields=fields) + + @log_helpers.log_method_call + def get_firewall_groups(self, context, filters=None, fields=None): + return self.driver.get_firewall_groups(context, filters, fields) + + @log_helpers.log_method_call + def update_firewall_group(self, context, id, firewall_group): + firewall_group = firewall_group['firewall_group'] + ports = firewall_group.get('ports', []) + + old_firewall_group = self._ensure_update_firewall_group(context, id) + firewall_group['tenant_id'] = old_firewall_group['tenant_id'] + + self._validate_firewall_policies_for_firewall_group(context, + firewall_group) + # Validate ports owner type and project + self._validate_ports_for_firewall_group(context, + firewall_group['tenant_id'], + ports) + self._validate_if_firewall_group_on_ports(context, firewall_group, + id=id) + + return self.driver.update_firewall_group(context, id, firewall_group) + + # Firewall Policy + @log_helpers.log_method_call + def create_firewall_policy(self, context, firewall_policy): + firewall_policy = firewall_policy['firewall_policy'] + return self.driver.create_firewall_policy(context, firewall_policy) + + @log_helpers.log_method_call + def delete_firewall_policy(self, context, id): + self.driver.delete_firewall_policy(context, id) + + @log_helpers.log_method_call + def get_firewall_policy(self, context, id, fields=None): + return self.driver.get_firewall_policy(context, id, fields) + + @log_helpers.log_method_call + def get_firewall_policies(self, context, filters=None, fields=None): + return self.driver.get_firewall_policies(context, filters, fields) + + @log_helpers.log_method_call def update_firewall_policy(self, context, id, firewall_policy): - LOG.debug("update_firewall_policy() called") + firewall_policy = firewall_policy['firewall_policy'] self._ensure_update_firewall_policy(context, id) - fwp = super(FirewallPluginV2, - self).update_firewall_policy(context, id, firewall_policy) - self._rpc_update_firewall_policy(context, id) - return fwp + return self.driver.update_firewall_policy(context, id, firewall_policy) + # Firewall Rule + @log_helpers.log_method_call + def create_firewall_rule(self, context, firewall_rule): + firewall_rule = firewall_rule['firewall_rule'] + return self.driver.create_firewall_rule(context, firewall_rule) + + @log_helpers.log_method_call + def delete_firewall_rule(self, context, id): + self.driver.delete_firewall_rule(context, id) + + @log_helpers.log_method_call + def get_firewall_rule(self, context, id, fields=None): + return self.driver.get_firewall_rule(context, id, fields) + + @log_helpers.log_method_call + def get_firewall_rules(self, context, filters=None, fields=None): + return self.driver.get_firewall_rules(context, filters, fields) + + @log_helpers.log_method_call def update_firewall_rule(self, context, id, firewall_rule): - LOG.debug("update_firewall_rule() called") + firewall_rule = firewall_rule['firewall_rule'] self._ensure_update_firewall_rule(context, id) - fwr = super(FirewallPluginV2, - self).update_firewall_rule(context, id, firewall_rule) - fwp_ids = self._get_policies_with_rule(context, id) - for fwp_id in fwp_ids: - self._rpc_update_firewall_policy(context, fwp_id) - return fwr + return self.driver.update_firewall_rule(context, id, firewall_rule) - def insert_rule(self, context, id, rule_info): - LOG.debug("insert_rule() called") - self._ensure_update_firewall_policy(context, id) - fwp = super(FirewallPluginV2, self).insert_rule(context, id, rule_info) - self._rpc_update_firewall_policy(context, id) - return fwp + @log_helpers.log_method_call + def insert_rule(self, context, policy_id, rule_info): + self._ensure_update_firewall_policy(context, policy_id) + self._validate_insert_remove_rule_request(rule_info) + return self.driver.insert_rule(context, policy_id, rule_info) - def remove_rule(self, context, id, rule_info): - LOG.debug("remove_rule() called") - self._ensure_update_firewall_policy(context, id) - fwp = super(FirewallPluginV2, self).remove_rule(context, id, rule_info) - self._rpc_update_firewall_policy(context, id) - return fwp + @log_helpers.log_method_call + def remove_rule(self, context, policy_id, rule_info): + self._ensure_update_firewall_policy(context, policy_id) + self._validate_insert_remove_rule_request(rule_info) + return self.driver.remove_rule(context, policy_id, rule_info) diff --git a/neutron_fwaas/services/firewall/agents/__init__.py b/neutron_fwaas/services/firewall/service_drivers/__init__.py similarity index 100% rename from neutron_fwaas/services/firewall/agents/__init__.py rename to neutron_fwaas/services/firewall/service_drivers/__init__.py diff --git a/neutron_fwaas/services/firewall/agents/l2/__init__.py b/neutron_fwaas/services/firewall/service_drivers/agents/__init__.py similarity index 100% rename from neutron_fwaas/services/firewall/agents/l2/__init__.py rename to neutron_fwaas/services/firewall/service_drivers/agents/__init__.py diff --git a/neutron_fwaas/services/firewall/service_drivers/agents/agents.py b/neutron_fwaas/services/firewall/service_drivers/agents/agents.py new file mode 100644 index 000000000..be9a3d48e --- /dev/null +++ b/neutron_fwaas/services/firewall/service_drivers/agents/agents.py @@ -0,0 +1,355 @@ +# Copyright (c) 2013 OpenStack Foundation +# 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. + +from neutron.common import rpc as neutron_rpc +from neutron_lib.api.definitions import portbindings as pb_def +from neutron_lib import constants as nl_constants +from neutron_lib import context as neutron_context +from neutron_lib.exceptions import firewall_v2 as f_exc +from oslo_config import cfg +from oslo_log import helpers as log_helpers +from oslo_log import log as logging +import oslo_messaging + +from neutron_fwaas.common import fwaas_constants as constants +from neutron_fwaas.services.firewall.service_drivers import driver_api + + +LOG = logging.getLogger(__name__) + + +class FirewallAgentCallbacks(object): + target = oslo_messaging.Target(version='1.0') + + def __init__(self, firewall_db): + self.firewall_db = firewall_db + + @log_helpers.log_method_call + def set_firewall_group_status(self, context, fwg_id, status, **kwargs): + """Agent uses this to set a firewall_group's status.""" + # Sanitize status first + if status in (nl_constants.ACTIVE, nl_constants.DOWN, + nl_constants.INACTIVE): + to_update = status + else: + to_update = nl_constants.ERROR + # ignore changing status if firewall_group expects to be deleted + # That case means that while some pending operation has been + # performed on the backend, neutron server received delete request + # and changed firewall status to PENDING_DELETE + updated = self.firewall_db.update_firewall_group_status( + context, fwg_id, to_update, not_in=(nl_constants.PENDING_DELETE,)) + if updated: + LOG.debug("firewall %s status set: %s", fwg_id, to_update) + return updated and to_update != nl_constants.ERROR + + @log_helpers.log_method_call + def firewall_group_deleted(self, context, fwg_id, **kwargs): + """Agent uses this to indicate firewall is deleted.""" + try: + fwg = self.firewall_db.get_firewall_group(context, fwg_id) + # allow to delete firewalls in ERROR state + if fwg['status'] in (nl_constants.PENDING_DELETE, + nl_constants.ERROR): + self.firewall_db.delete_firewall_group(context, fwg_id) + return True + LOG.warning('Firewall %(fwg)s unexpectedly deleted by agent, ' + 'status was %(status)s', + {'fwg': fwg_id, 'status': fwg['status']}) + fwg['status'] = nl_constants.ERROR + self.firewall_db.update_firewall_group(context, fwg_id, fwg) + return False + except f_exc.FirewallGroupNotFound: + LOG.info('Firewall group %s already deleted', fwg_id) + return True + + @log_helpers.log_method_call + def get_firewall_groups_for_project(self, context, **kwargs): + """Gets all firewall_groups and rules on a project.""" + fwg_list = [] + for fwg in self.firewall_db.get_firewall_groups(context): + fwg_with_rules =\ + self.firewall_db.make_firewall_group_dict_with_rules( + context, fwg['id']) + if fwg['status'] == nl_constants.PENDING_DELETE: + fwg_with_rules['add-port-ids'] = [] + fwg_with_rules['del-port-ids'] = ( + self.firewall_db.get_ports_in_firewall_group( + context, fwg['id'])) + else: + fwg_with_rules['add-port-ids'] = ( + self.firewall_db.get_ports_in_firewall_group( + context, fwg['id'])) + fwg_with_rules['del-port-ids'] = [] + fwg_list.append(fwg_with_rules) + return fwg_list + + @log_helpers.log_method_call + def get_projects_with_firewall_groups(self, context, **kwargs): + """Get all projects that have firewall_groups.""" + ctx = neutron_context.get_admin_context() + fwg_list = self.firewall_db.get_firewall_groups(ctx) + fwg_project_list = list(set(fwg['tenant_id'] for fwg in fwg_list)) + return fwg_project_list + + @log_helpers.log_method_call + def get_firewall_group_for_port(self, context, **kwargs): + """Get firewall_group is associated with a port.""" + ctx = context.elevated() + # Only one Firewall Group can be associated to a port at a time + fwg_port_binding = self.firewall_db.get_firewall_groups( + ctx, filters={'ports': [kwargs.get('port_id')]}) + if len(fwg_port_binding) != 1: + return + fwg = fwg_port_binding[0] + + fwg['ingress_rule_list'] = [] + for rule_id in self.firewall_db.get_firewall_policy( + context, fwg['ingress_firewall_policy_id'], + fields=['firewall_rules'])['firewall_rules']: + fwg['ingress_rule_list'].append( + self.firewall_db.get_firewall_rule(context, rule_id)) + fwg['egress_rule_list'] = [] + for rule_id in self.firewall_db.get_firewall_policy( + context, fwg['egress_firewall_policy_id'], + fields=['firewall_rules'])['firewall_rules']: + fwg['egress_rule_list'].append( + self.firewall_db.get_firewall_rule(context, rule_id)) + return fwg + + +class FirewallAgentApi(object): + """Plugin side of plugin to agent RPC API""" + + def __init__(self, topic, host): + self.host = host + target = oslo_messaging.Target(topic=topic, version='1.0') + self.client = neutron_rpc.get_client(target) + + def create_firewall_group(self, context, firewall_group): + cctxt = self.client.prepare(fanout=True) + cctxt.cast(context, 'create_firewall_group', + firewall_group=firewall_group, host=self.host) + + def update_firewall_group(self, context, firewall_group): + cctxt = self.client.prepare(fanout=True) + cctxt.cast(context, 'update_firewall_group', + firewall_group=firewall_group, host=self.host) + + def delete_firewall_group(self, context, firewall_group): + cctxt = self.client.prepare(fanout=True) + cctxt.cast(context, 'delete_firewall_group', + firewall_group=firewall_group, host=self.host) + + +class FirewallAgentDriver(driver_api.FirewallDriverDB): + """Firewall driver to implement agent messages and callback methods + + Implement RPC Firewall v2 API and callback methods for agents based on + Neutron DB model. + """ + + def __init__(self, service_plugin): + super(FirewallAgentDriver, self).__init__(service_plugin) + self.agent_rpc = FirewallAgentApi(constants.FW_AGENT, cfg.CONF.host) + self.start_rpc_listeners() + + def start_rpc_listeners(self): + self.endpoints = [FirewallAgentCallbacks(self.firewall_db)] + + self.rpc_connection = neutron_rpc.Connection() + self.rpc_connection.create_consumer(constants.FIREWALL_PLUGIN, + self.endpoints, fanout=False) + self.rpc_connection.consume_in_threads() + + def _rpc_update_firewall_group(self, context, fwg_id): + status_update = {"status": nl_constants.PENDING_UPDATE} + self.update_firewall_group(context, fwg_id, status_update) + fwg_with_rules = self.firewall_db.make_firewall_group_dict_with_rules( + context, fwg_id) + # this is triggered on an update to fwg rule or policy, no + # change in associated ports. + fwg_with_rules['add-port-ids'] = \ + self.firewall_db.get_ports_in_firewall_group(context, fwg_id) + fwg_with_rules['del-port-ids'] = [] + fwg_with_rules['port_details'] = self._get_fwg_port_details( + context, fwg_with_rules['add-port-ids']) + self.agent_rpc.update_firewall_group(context, fwg_with_rules) + + def _rpc_update_firewall_policy(self, context, firewall_policy_id): + firewall_policy = self.get_firewall_policy(context, firewall_policy_id) + if firewall_policy: + ing_fwg_ids, eg_fwg_ids = self.firewall_db.get_fwgs_with_policy( + context, firewall_policy_id) + for fwg_id in list(set(ing_fwg_ids + eg_fwg_ids)): + self._rpc_update_firewall_group(context, fwg_id) + + def _get_fwg_port_details(self, context, fwg_ports): + """Returns a dictionary list of port details. """ + port_details = {} + for port_id in fwg_ports: + port_db = self._core_plugin.get_port(context, port_id) + # Add more parameters below based on requirement. + device_owner = port_db['device_owner'] + port_details[port_id] = { + 'device_owner': device_owner, + 'device': port_db['id'], + 'network_id': port_db['network_id'], + 'fixed_ips': port_db['fixed_ips'], + 'allowed_address_pairs': + port_db.get('allowed_address_pairs', []), + 'port_security_enabled': + port_db.get('port_security_enabled', True), + 'id': port_db['id'], + 'status': port_db['status'], + } + if device_owner.startswith( + nl_constants.DEVICE_OWNER_COMPUTE_PREFIX): + port_details[port_id].update( + {'host': port_db[pb_def.HOST_ID]}) + return port_details + + def create_firewall_group_precommit(self, context, firewall_group): + ports = firewall_group['ports'] + + if (not ports or (not firewall_group['ingress_firewall_policy_id'] and + not firewall_group['egress_firewall_policy_id'])): + # no messaging to agent needed and fw needs to go to INACTIVE state + # as no associated ports and/or no policy configured. + status = nl_constants.INACTIVE + else: + status = (nl_constants.CREATED if cfg.CONF.router_distributed + else nl_constants.PENDING_CREATE) + firewall_group['status'] = status + + def create_firewall_group_postcommit(self, context, firewall_group): + if firewall_group['status'] != nl_constants.INACTIVE: + fwg_with_rules =\ + self.firewall_db.make_firewall_group_dict_with_rules( + context, firewall_group['id']) + fwg_with_rules['add-port-ids'] = firewall_group['ports'] + fwg_with_rules['del-ports-id'] = [] + fwg_with_rules['port_details'] = self._get_fwg_port_details( + context, firewall_group['ports']) + self.agent_rpc.create_firewall_group(context, fwg_with_rules) + + def delete_firewall_group_precommit(self, context, firewall_group): + if firewall_group['status'] == nl_constants.ACTIVE: + raise f_exc.FirewallGroupInUse(firewall_id=firewall_group['id']) + elif firewall_group['status'] != nl_constants.INACTIVE: + # Firewall group is in inconsistent state, remove it + return + if not firewall_group['ports']: + # No associated port, can safety remove it + return + + # Need to prevent agent to delete the firewall group before delete it + self.firewall_db.update_firewall_group_status( + context, firewall_group['id'], nl_constants.PENDING_DELETE) + firewall_group['status'] = nl_constants.PENDING_DELETE + + fwg_with_rules = self.firewall_db.make_firewall_group_dict_with_rules( + context, firewall_group['id']) + fwg_with_rules['del-port-ids'] = firewall_group['ports'] + fwg_with_rules['add-port-ids'] = [] + # Reflect state change in fwg_with_rules + fwg_with_rules['status'] = nl_constants.PENDING_DELETE + fwg_with_rules['port_details'] = self._get_fwg_port_details( + context, fwg_with_rules['del-port-ids']) + self.agent_rpc.delete_firewall_group(context, fwg_with_rules) + + def _need_pending_update(self, old_firewall_group, new_firewall_group): + port_updated = (set(new_firewall_group['ports']) != + set(old_firewall_group['ports'])) + policies_updated = ( + new_firewall_group['ingress_firewall_policy_id'] != + old_firewall_group['ingress_firewall_policy_id'] or + new_firewall_group['egress_firewall_policy_id'] != + old_firewall_group['egress_firewall_policy_id'] + ) + if (port_updated and + (new_firewall_group['ingress_firewall_policy_id'] or + new_firewall_group['egress_firewall_policy_id'])): + return True + if policies_updated and new_firewall_group['ports']: + return True + return False + + def update_firewall_group_precommit(self, context, old_firewall_group, + new_firewall_group): + if self._need_pending_update(old_firewall_group, new_firewall_group): + new_firewall_group['status'] = nl_constants.PENDING_UPDATE + + def update_firewall_group_postcommit(self, context, old_firewall_group, + new_firewall_group): + if not self._need_pending_update(old_firewall_group, + new_firewall_group): + return + + fwg_with_rules = self.firewall_db.make_firewall_group_dict_with_rules( + context, new_firewall_group['id']) + + # determine ports to add fw to and del from + fwg_with_rules['add-port-ids'] = list( + set(new_firewall_group['ports']) - set(old_firewall_group['ports']) + ) + fwg_with_rules['del-port-ids'] = list( + set(old_firewall_group['ports']) - set(new_firewall_group['ports']) + ) + + # last-port drives agent to ack with status to set state to INACTIVE + fwg_with_rules['last-port'] = not ( + set(new_firewall_group['ports']) - set(old_firewall_group['ports']) + ) + + LOG.debug("update_firewall_group %s: Add Ports: %s, Del Ports: %s", + new_firewall_group['id'], + fwg_with_rules['add-port-ids'], + fwg_with_rules['del-port-ids']) + + fwg_with_rules['port_details'] = self._get_fwg_port_details( + context, fwg_with_rules['del-port-ids']) + fwg_with_rules['port_details'].update(self._get_fwg_port_details( + context, fwg_with_rules['add-port-ids'])) + + if (new_firewall_group['name'] == constants.DEFAULT_FWG and + len(fwg_with_rules['add-port-ids']) == 1 and + not fwg_with_rules['del-port-ids']): + port_id = fwg_with_rules['add-port-ids'][0] + if (fwg_with_rules['port_details'][port_id].get('status') != + nl_constants.ACTIVE): + # If port not yet active, just associate to default firewall + # group. When agent will set it to UP, it'll found FG + # association and enforce default policies + return + # Warn agents Firewall Group port list updated + self.agent_rpc.update_firewall_group(context, fwg_with_rules) + + def update_firewall_policy_postcommit(self, context, old_firewall_policy, + new_firewall_group): + self._rpc_update_firewall_policy(context, new_firewall_group['id']) + + def update_firewall_rule_postcommit(self, context, old_firewall_rule, + new_firewall_rule): + firewall_policy_ids = self.firewall_db.get_policies_with_rule( + context, new_firewall_rule['id']) + for firewall_policy_id in firewall_policy_ids: + self._rpc_update_firewall_policy(context, firewall_policy_id) + + def insert_rule_postcommit(self, context, policy_id, rule_info): + self._rpc_update_firewall_policy(context, policy_id) + + def remove_rule_postcommit(self, context, policy_id, rule_info): + self._rpc_update_firewall_policy(context, policy_id) diff --git a/neutron_fwaas/services/firewall/agents/l3reference/__init__.py b/neutron_fwaas/services/firewall/service_drivers/agents/drivers/__init__.py similarity index 100% rename from neutron_fwaas/services/firewall/agents/l3reference/__init__.py rename to neutron_fwaas/services/firewall/service_drivers/agents/drivers/__init__.py diff --git a/neutron_fwaas/services/firewall/drivers/conntrack_base.py b/neutron_fwaas/services/firewall/service_drivers/agents/drivers/conntrack_base.py similarity index 96% rename from neutron_fwaas/services/firewall/drivers/conntrack_base.py rename to neutron_fwaas/services/firewall/service_drivers/agents/drivers/conntrack_base.py index 36145b348..536869632 100644 --- a/neutron_fwaas/services/firewall/drivers/conntrack_base.py +++ b/neutron_fwaas/services/firewall/service_drivers/agents/drivers/conntrack_base.py @@ -29,7 +29,7 @@ def load_and_init_conntrack_driver(*args, **kwargs): driver = cfg.CONF.fwaas.conntrack_driver try: conntrack_driver_cls = runtime.load_class_by_alias_or_classname( - 'neutron_fwaas.services.firewall.drivers.linux', driver) + 'neutron.agent.l3.firewall_drivers', driver) except ImportError: with excutils.save_and_reraise_exception(): LOG.exception("Driver '%s' not found.", driver) diff --git a/neutron_fwaas/services/firewall/drivers/fwaas_base.py b/neutron_fwaas/services/firewall/service_drivers/agents/drivers/fwaas_base.py similarity index 100% rename from neutron_fwaas/services/firewall/drivers/fwaas_base.py rename to neutron_fwaas/services/firewall/service_drivers/agents/drivers/fwaas_base.py diff --git a/neutron_fwaas/services/firewall/drivers/fwaas_base_v2.py b/neutron_fwaas/services/firewall/service_drivers/agents/drivers/fwaas_base_v2.py similarity index 100% rename from neutron_fwaas/services/firewall/drivers/fwaas_base_v2.py rename to neutron_fwaas/services/firewall/service_drivers/agents/drivers/fwaas_base_v2.py diff --git a/neutron_fwaas/services/firewall/drivers/__init__.py b/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/__init__.py similarity index 100% rename from neutron_fwaas/services/firewall/drivers/__init__.py rename to neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/__init__.py diff --git a/neutron_fwaas/services/firewall/drivers/linux/iptables_fwaas.py b/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/iptables_fwaas.py similarity index 99% rename from neutron_fwaas/services/firewall/drivers/linux/iptables_fwaas.py rename to neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/iptables_fwaas.py index 5860f3763..523837af7 100644 --- a/neutron_fwaas/services/firewall/drivers/linux/iptables_fwaas.py +++ b/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/iptables_fwaas.py @@ -20,8 +20,10 @@ from neutron_lib.exceptions import firewall_v1 as f_exc from oslo_log import log as logging from neutron_fwaas.common import fwaas_constants as f_const -from neutron_fwaas.services.firewall.drivers import conntrack_base -from neutron_fwaas.services.firewall.drivers import fwaas_base +from neutron_fwaas.services.firewall.service_drivers.agents.drivers import\ + conntrack_base +from neutron_fwaas.services.firewall.service_drivers.agents.drivers import\ + fwaas_base LOG = logging.getLogger(__name__) diff --git a/neutron_fwaas/services/firewall/drivers/linux/iptables_fwaas_v2.py b/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/iptables_fwaas_v2.py similarity index 99% rename from neutron_fwaas/services/firewall/drivers/linux/iptables_fwaas_v2.py rename to neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/iptables_fwaas_v2.py index 48f640519..4f2bf578c 100644 --- a/neutron_fwaas/services/firewall/drivers/linux/iptables_fwaas_v2.py +++ b/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/iptables_fwaas_v2.py @@ -19,8 +19,10 @@ from neutron_lib import constants from neutron_lib.exceptions import firewall_v2 as fw_ext from oslo_log import log as logging -from neutron_fwaas.services.firewall.drivers import conntrack_base -from neutron_fwaas.services.firewall.drivers import fwaas_base_v2 +from neutron_fwaas.services.firewall.service_drivers.agents.drivers import\ + conntrack_base +from neutron_fwaas.services.firewall.service_drivers.agents.drivers import\ + fwaas_base_v2 LOG = logging.getLogger(__name__) FWAAS_DRIVER_NAME = 'Fwaas iptables driver' diff --git a/neutron_fwaas/services/firewall/drivers/linux/__init__.py b/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/__init__.py similarity index 100% rename from neutron_fwaas/services/firewall/drivers/linux/__init__.py rename to neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/__init__.py diff --git a/neutron_fwaas/services/firewall/drivers/linux/l2/driver_base.py b/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/driver_base.py similarity index 100% rename from neutron_fwaas/services/firewall/drivers/linux/l2/driver_base.py rename to neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/driver_base.py diff --git a/neutron_fwaas/services/firewall/drivers/linux/l2/__init__.py b/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/noop/__init__.py similarity index 100% rename from neutron_fwaas/services/firewall/drivers/linux/l2/__init__.py rename to neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/noop/__init__.py diff --git a/neutron_fwaas/services/firewall/drivers/linux/l2/noop/noop_driver.py b/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/noop/noop_driver.py similarity index 92% rename from neutron_fwaas/services/firewall/drivers/linux/l2/noop/noop_driver.py rename to neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/noop/noop_driver.py index ff40c0e2c..41f581540 100644 --- a/neutron_fwaas/services/firewall/drivers/linux/l2/noop/noop_driver.py +++ b/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/noop/noop_driver.py @@ -14,7 +14,8 @@ from oslo_log import helpers as log_helpers -from neutron_fwaas.services.firewall.drivers.linux.l2 import driver_base +from neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.l2\ + import driver_base class NoopFirewallL2Driver(driver_base.FirewallL2DriverBase): diff --git a/neutron_fwaas/services/firewall/drivers/linux/l2/openvswitch_firewall/__init__.py b/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/openvswitch_firewall/__init__.py similarity index 84% rename from neutron_fwaas/services/firewall/drivers/linux/l2/openvswitch_firewall/__init__.py rename to neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/openvswitch_firewall/__init__.py index 299d82632..892e565fb 100644 --- a/neutron_fwaas/services/firewall/drivers/linux/l2/openvswitch_firewall/__init__.py +++ b/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/openvswitch_firewall/__init__.py @@ -13,7 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. -from neutron_fwaas.services.firewall.drivers.linux.l2.openvswitch_firewall \ - import firewall +from neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.l2.\ + openvswitch_firewall import firewall OVSFirewallDriver = firewall.OVSFirewallDriver diff --git a/neutron_fwaas/services/firewall/drivers/linux/l2/openvswitch_firewall/constants.py b/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/openvswitch_firewall/constants.py similarity index 100% rename from neutron_fwaas/services/firewall/drivers/linux/l2/openvswitch_firewall/constants.py rename to neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/openvswitch_firewall/constants.py diff --git a/neutron_fwaas/services/firewall/drivers/linux/l2/openvswitch_firewall/exceptions.py b/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/openvswitch_firewall/exceptions.py similarity index 100% rename from neutron_fwaas/services/firewall/drivers/linux/l2/openvswitch_firewall/exceptions.py rename to neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/openvswitch_firewall/exceptions.py diff --git a/neutron_fwaas/services/firewall/drivers/linux/l2/openvswitch_firewall/firewall.py b/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/openvswitch_firewall/firewall.py similarity index 98% rename from neutron_fwaas/services/firewall/drivers/linux/l2/openvswitch_firewall/firewall.py rename to neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/openvswitch_firewall/firewall.py index 1f1b6c402..ab828d1a1 100644 --- a/neutron_fwaas/services/firewall/drivers/linux/l2/openvswitch_firewall/firewall.py +++ b/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/openvswitch_firewall/firewall.py @@ -24,13 +24,14 @@ from neutron.common import constants from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants \ as ovs_consts -from neutron_fwaas.services.firewall.drivers.linux.l2 import driver_base -from neutron_fwaas.services.firewall.drivers.linux.l2.openvswitch_firewall \ - import constants as fwaas_ovs_consts -from neutron_fwaas.services.firewall.drivers.linux.l2.openvswitch_firewall \ - import exceptions -from neutron_fwaas.services.firewall.drivers.linux.l2.openvswitch_firewall \ - import rules +from neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.l2\ + import driver_base +from neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.l2.\ + openvswitch_firewall import constants as fwaas_ovs_consts +from neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.l2.\ + openvswitch_firewall import exceptions +from neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.l2.\ + openvswitch_firewall import rules LOG = logging.getLogger(__name__) diff --git a/neutron_fwaas/services/firewall/drivers/linux/l2/openvswitch_firewall/rules.py b/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/openvswitch_firewall/rules.py similarity index 98% rename from neutron_fwaas/services/firewall/drivers/linux/l2/openvswitch_firewall/rules.py rename to neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/openvswitch_firewall/rules.py index 7fcfe1636..529112f57 100644 --- a/neutron_fwaas/services/firewall/drivers/linux/l2/openvswitch_firewall/rules.py +++ b/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/openvswitch_firewall/rules.py @@ -14,14 +14,15 @@ # under the License. import netaddr -from neutron_lib import constants as n_consts -from oslo_log import log as logging from neutron.common import utils from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants \ as ovs_consts -from neutron_fwaas.services.firewall.drivers.linux.l2.openvswitch_firewall \ - import constants as fwaas_ovs_consts +from neutron_lib import constants as n_consts +from oslo_log import log as logging + +from neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.l2.\ + openvswitch_firewall import constants as fwaas_ovs_consts LOG = logging.getLogger(__name__) diff --git a/neutron_fwaas/services/firewall/drivers/linux/legacy_conntrack.py b/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/legacy_conntrack.py similarity index 98% rename from neutron_fwaas/services/firewall/drivers/linux/legacy_conntrack.py rename to neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/legacy_conntrack.py index c63051143..eefb5d407 100644 --- a/neutron_fwaas/services/firewall/drivers/linux/legacy_conntrack.py +++ b/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/legacy_conntrack.py @@ -17,8 +17,8 @@ from neutron.agent.linux import utils as linux_utils from neutron_lib import constants from oslo_log import log as logging -from neutron_fwaas.services.firewall.drivers import conntrack_base - +from neutron_fwaas.services.firewall.service_drivers.agents.drivers import\ + conntrack_base LOG = logging.getLogger(__name__) diff --git a/neutron_fwaas/services/firewall/drivers/linux/netlink_conntrack.py b/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/netlink_conntrack.py similarity index 98% rename from neutron_fwaas/services/firewall/drivers/linux/netlink_conntrack.py rename to neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/netlink_conntrack.py index 7058a9e01..a43cc5e39 100644 --- a/neutron_fwaas/services/firewall/drivers/linux/netlink_conntrack.py +++ b/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/netlink_conntrack.py @@ -17,7 +17,8 @@ from neutron_lib import constants from oslo_log import log as logging from neutron_fwaas.privileged import netlink_lib as nl_lib -from neutron_fwaas.services.firewall.drivers import conntrack_base +from neutron_fwaas.services.firewall.service_drivers.agents.drivers import\ + conntrack_base LOG = logging.getLogger(__name__) diff --git a/neutron_fwaas/services/firewall/agents/firewall_agent_api.py b/neutron_fwaas/services/firewall/service_drivers/agents/firewall_agent_api.py similarity index 100% rename from neutron_fwaas/services/firewall/agents/firewall_agent_api.py rename to neutron_fwaas/services/firewall/service_drivers/agents/firewall_agent_api.py diff --git a/neutron_fwaas/services/firewall/agents/firewall_service.py b/neutron_fwaas/services/firewall/service_drivers/agents/firewall_service.py similarity index 100% rename from neutron_fwaas/services/firewall/agents/firewall_service.py rename to neutron_fwaas/services/firewall/service_drivers/agents/firewall_service.py diff --git a/neutron_fwaas/services/firewall/drivers/linux/l2/noop/__init__.py b/neutron_fwaas/services/firewall/service_drivers/agents/l2/__init__.py similarity index 100% rename from neutron_fwaas/services/firewall/drivers/linux/l2/noop/__init__.py rename to neutron_fwaas/services/firewall/service_drivers/agents/l2/__init__.py diff --git a/neutron_fwaas/services/firewall/agents/l2/fwaas_v2.py b/neutron_fwaas/services/firewall/service_drivers/agents/l2/fwaas_v2.py similarity index 99% rename from neutron_fwaas/services/firewall/agents/l2/fwaas_v2.py rename to neutron_fwaas/services/firewall/service_drivers/agents/l2/fwaas_v2.py index 304e060b3..ea95f8c2e 100644 --- a/neutron_fwaas/services/firewall/agents/l2/fwaas_v2.py +++ b/neutron_fwaas/services/firewall/service_drivers/agents/l2/fwaas_v2.py @@ -28,7 +28,8 @@ from neutron_lib.utils import net as nl_net from neutron_fwaas._i18n import _ from neutron_fwaas.common import fwaas_constants as consts -from neutron_fwaas.services.firewall.agents import firewall_agent_api as api +from neutron_fwaas.services.firewall.service_drivers.agents import\ + firewall_agent_api as api LOG = logging.getLogger(__name__) diff --git a/neutron_fwaas/tests/unit/services/firewall/agents/__init__.py b/neutron_fwaas/services/firewall/service_drivers/agents/l3reference/__init__.py similarity index 100% rename from neutron_fwaas/tests/unit/services/firewall/agents/__init__.py rename to neutron_fwaas/services/firewall/service_drivers/agents/l3reference/__init__.py diff --git a/neutron_fwaas/services/firewall/agents/l3reference/firewall_l3_agent.py b/neutron_fwaas/services/firewall/service_drivers/agents/l3reference/firewall_l3_agent.py similarity index 98% rename from neutron_fwaas/services/firewall/agents/l3reference/firewall_l3_agent.py rename to neutron_fwaas/services/firewall/service_drivers/agents/l3reference/firewall_l3_agent.py index eda5f9a77..909b697fd 100644 --- a/neutron_fwaas/services/firewall/agents/l3reference/firewall_l3_agent.py +++ b/neutron_fwaas/services/firewall/service_drivers/agents/l3reference/firewall_l3_agent.py @@ -24,8 +24,10 @@ from oslo_log import log as logging from neutron_fwaas.common import fwaas_constants from neutron_fwaas.common import resources as f_resources -from neutron_fwaas.services.firewall.agents import firewall_agent_api as api -from neutron_fwaas.services.firewall.agents import firewall_service +from neutron_fwaas.services.firewall.service_drivers.agents \ + import firewall_agent_api as api +from neutron_fwaas.services.firewall.service_drivers.agents \ + import firewall_service LOG = logging.getLogger(__name__) diff --git a/neutron_fwaas/services/firewall/agents/l3reference/firewall_l3_agent_v2.py b/neutron_fwaas/services/firewall/service_drivers/agents/l3reference/firewall_l3_agent_v2.py similarity index 99% rename from neutron_fwaas/services/firewall/agents/l3reference/firewall_l3_agent_v2.py rename to neutron_fwaas/services/firewall/service_drivers/agents/l3reference/firewall_l3_agent_v2.py index 6e4b00951..4024678e4 100644 --- a/neutron_fwaas/services/firewall/agents/l3reference/firewall_l3_agent_v2.py +++ b/neutron_fwaas/services/firewall/service_drivers/agents/l3reference/firewall_l3_agent_v2.py @@ -25,8 +25,10 @@ from oslo_log import log as logging from neutron_fwaas.common import fwaas_constants from neutron_fwaas.common import resources as f_resources -from neutron_fwaas.services.firewall.agents import firewall_agent_api as api -from neutron_fwaas.services.firewall.agents import firewall_service +from neutron_fwaas.services.firewall.service_drivers.agents import\ + firewall_agent_api as api +from neutron_fwaas.services.firewall.service_drivers.agents import\ + firewall_service LOG = logging.getLogger(__name__) diff --git a/neutron_fwaas/services/firewall/service_drivers/driver_api.py b/neutron_fwaas/services/firewall/service_drivers/driver_api.py new file mode 100644 index 000000000..aa2a9f282 --- /dev/null +++ b/neutron_fwaas/services/firewall/service_drivers/driver_api.py @@ -0,0 +1,435 @@ +# Copyright (c) 2017 Juniper Networks, Inc. All rights reserved. +# 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. + +import abc +import copy + +import six + +from neutron_lib import constants as nl_constants +from neutron_lib.plugins import directory +from oslo_log import log as logging + +from neutron_fwaas.db.firewall.v2 import firewall_db_v2 + + +LOG = logging.getLogger(__name__) + + +@six.add_metaclass(abc.ABCMeta) +class FirewallDriver(object): + """Firewall v2 interface for driver + + That driver interface does not persist Firewall v2 data in any database. + The driver needs to do it by itself. + """ + + def __init__(self, service_plugin): + self.service_plugin = service_plugin + + @property + def _core_plugin(self): + return directory.get_plugin() + + # Firewall Group + @abc.abstractmethod + def create_firewall_group(self, context, firewall_group): + pass + + @abc.abstractmethod + def delete_firewall_group(self, context, id): + pass + + @abc.abstractmethod + def get_firewall_group(self, context, id, fields=None): + pass + + @abc.abstractmethod + def get_firewall_groups(self, context, filters=None, fields=None): + pass + + @abc.abstractmethod + def update_firewall_group(self, context, id, firewall_group): + pass + + # Firewall Policy + @abc.abstractmethod + def create_firewall_policy(self, context, firewall_policy): + pass + + @abc.abstractmethod + def delete_firewall_policy(self, context, id): + pass + + @abc.abstractmethod + def get_firewall_policy(self, context, id, fields=None): + pass + + @abc.abstractmethod + def get_firewall_policies(self, context, filters=None, fields=None): + pass + + @abc.abstractmethod + def update_firewall_policy(self, context, id, firewall_policy): + pass + + # Firewall Rule + @abc.abstractmethod + def create_firewall_rule(self, context, firewall_rule): + pass + + @abc.abstractmethod + def delete_firewall_rule(self, context, id): + pass + + @abc.abstractmethod + def get_firewall_rule(self, context, id, fields=None): + pass + + @abc.abstractmethod + def get_firewall_rules(self, context, filters=None, fields=None): + pass + + @abc.abstractmethod + def update_firewall_rule(self, context, id, firewall_rule): + pass + + @abc.abstractmethod + def insert_rule(self, context, policy_id, rule_info): + pass + + @abc.abstractmethod + def remove_rule(self, context, policy_id, rule_info): + pass + + +@six.add_metaclass(abc.ABCMeta) +class FirewallDriverDBMixin(FirewallDriver): + """FirewallDriverDB mixin to provision the database on behalf of the driver + + That driver interface persists Firewall data in its database and forwards + the result to pre and post commit methods. + """ + + def __init__(self, *args, **kwargs): + super(FirewallDriverDBMixin, self).__init__(*args, **kwargs) + self.firewall_db = firewall_db_v2.FirewallPluginDb() + + @staticmethod + def _update_resource_status(context, resource_type, resource_dict): + with context.session.begin(subtransactions=True): + context.session.query(resource_type).\ + filter_by(id=resource_dict['id']).\ + update({'status': resource_dict['status']}) + + # Firewall Group + def create_firewall_group(self, context, firewall_group): + with context.session.begin(subtransactions=True): + firewall_group = self.firewall_db.create_firewall_group( + context, firewall_group) + self.create_firewall_group_precommit(context, firewall_group) + self._update_resource_status(context, firewall_db_v2.FirewallGroup, + firewall_group) + self.create_firewall_group_postcommit(context, firewall_group) + return firewall_group + + @abc.abstractmethod + def create_firewall_group_precommit(self, context, firewall_group): + pass + + @abc.abstractmethod + def create_firewall_group_postcommit(self, context, firewall_group): + pass + + def delete_firewall_group(self, context, id): + firewall_group = self.firewall_db.get_firewall_group(context, id) + if firewall_group['status'] == nl_constants.PENDING_DELETE: + firewall_group['status'] = nl_constants.ERROR + self.delete_firewall_group_precommit(context, firewall_group) + if firewall_group['status'] != nl_constants.PENDING_DELETE: + # lets driver deleting firewall group later + self.firewall_db.delete_firewall_group(context, id) + self.delete_firewall_group_postcommit(context, firewall_group) + + @abc.abstractmethod + def delete_firewall_group_precommit(self, context, firewall_group): + pass + + @abc.abstractmethod + def delete_firewall_group_postcommit(self, context, firewall_group): + pass + + def get_firewall_group(self, context, id, fields=None): + return self.firewall_db.get_firewall_group(context, id, fields=fields) + + def get_firewall_groups(self, context, filters=None, fields=None): + return self.firewall_db.get_firewall_groups(context, filters, fields) + + def update_firewall_group(self, context, id, firewall_group_delta): + old_firewall_group = self.firewall_db.get_firewall_group(context, id) + new_firewall_group = copy.deepcopy(old_firewall_group) + new_firewall_group.update(firewall_group_delta) + self.update_firewall_group_precommit(context, old_firewall_group, + new_firewall_group) + firewall_group_delta['status'] = new_firewall_group['status'] + firewall_group = self.firewall_db.update_firewall_group( + context, id, firewall_group_delta) + self.update_firewall_group_postcommit(context, old_firewall_group, + firewall_group) + return firewall_group + + @abc.abstractmethod + def update_firewall_group_precommit(self, context, old_firewall_group, + new_firewall_group): + pass + + @abc.abstractmethod + def update_firewall_group_postcommit(self, context, old_firewall_group, + new_firewall_group): + pass + + # Firewall Policy + def create_firewall_policy(self, context, firewall_policy): + with context.session.begin(subtransactions=True): + firewall_policy = self.firewall_db.create_firewall_policy( + context, firewall_policy) + self.create_firewall_policy_precommit(context, firewall_policy) + self.create_firewall_policy_postcommit(context, firewall_policy) + return firewall_policy + + @abc.abstractmethod + def create_firewall_policy_precommit(self, context, firewall_policy): + pass + + @abc.abstractmethod + def create_firewall_policy_postcommit(self, context, firewall_policy): + pass + + def delete_firewall_policy(self, context, id): + firewall_policy = self.firewall_db.get_firewall_policy(context, id) + self.delete_firewall_policy_precommit(context, firewall_policy) + self.firewall_db.delete_firewall_policy(context, id) + self.delete_firewall_policy_postcommit(context, firewall_policy) + + @abc.abstractmethod + def delete_firewall_policy_precommit(self, context, firewall_policy): + pass + + @abc.abstractmethod + def delete_firewall_policy_postcommit(self, context, firewall_policy): + pass + + def get_firewall_policy(self, context, id, fields=None): + return self.firewall_db.get_firewall_policy(context, id, fields) + + def get_firewall_policies(self, context, filters=None, fields=None): + return self.firewall_db.get_firewall_policies(context, filters, fields) + + def update_firewall_policy(self, context, id, firewall_policy_delta): + old_firewall_policy = self.firewall_db.get_firewall_policy(context, id) + new_firewall_policy = copy.deepcopy(old_firewall_policy) + new_firewall_policy.update(firewall_policy_delta) + self.update_firewall_policy_precommit(context, old_firewall_policy, + new_firewall_policy) + firewall_policy = self.firewall_db.update_firewall_policy( + context, id, firewall_policy_delta) + self.update_firewall_policy_postcommit(context, old_firewall_policy, + firewall_policy) + return firewall_policy + + @abc.abstractmethod + def update_firewall_policy_precommit(self, context, old_firewall_policy, + new_firewall_policy): + pass + + @abc.abstractmethod + def update_firewall_policy_postcommit(self, context, old_firewall_policy, + new_firewall_policy): + pass + + # Firewall Rule + def create_firewall_rule(self, context, firewall_rule): + with context.session.begin(subtransactions=True): + firewall_rule = self.firewall_db.create_firewall_rule( + context, firewall_rule) + self.create_firewall_rule_precommit(context, firewall_rule) + self.create_firewall_rule_postcommit(context, firewall_rule) + return firewall_rule + + @abc.abstractmethod + def create_firewall_rule_precommit(self, context, firewall_rule): + pass + + @abc.abstractmethod + def create_firewall_rule_postcommit(self, context, firewall_rule): + pass + + def delete_firewall_rule(self, context, id): + firewall_rule = self.firewall_db.get_firewall_rule(context, id) + self.delete_firewall_rule_precommit(context, firewall_rule) + self.firewall_db.delete_firewall_rule(context, id) + self.delete_firewall_rule_postcommit(context, firewall_rule) + + @abc.abstractmethod + def delete_firewall_rule_precommit(self, context, firewall_rule): + pass + + @abc.abstractmethod + def delete_firewall_rule_postcommit(self, context, firewall_rule): + pass + + def get_firewall_rule(self, context, id, fields=None): + return self.firewall_db.get_firewall_rule(context, id, fields) + + def get_firewall_rules(self, context, filters=None, fields=None): + return self.firewall_db.get_firewall_rules(context, filters, fields) + + def update_firewall_rule(self, context, id, firewall_rule_delta): + old_firewall_rule = self.firewall_db.get_firewall_rule(context, id) + new_firewall_rule = copy.deepcopy(old_firewall_rule) + new_firewall_rule.update(firewall_rule_delta) + self.update_firewall_rule_precommit(context, old_firewall_rule, + new_firewall_rule) + firewall_rule = self.firewall_db.update_firewall_rule( + context, id, firewall_rule_delta) + self.update_firewall_rule_postcommit(context, old_firewall_rule, + firewall_rule) + return firewall_rule + + @abc.abstractmethod + def update_firewall_rule_precommit(self, context, old_firewall_rule, + new_firewall_rule): + pass + + @abc.abstractmethod + def update_firewall_rule_postcommit(self, context, old_firewall_rule, + new_firewall_rule): + pass + + def insert_rule(self, context, policy_id, rule_info): + self.insert_rule_precommit(context, policy_id, rule_info) + firewall_policy = self.firewall_db.insert_rule(context, policy_id, + rule_info) + self.insert_rule_postcommit(context, policy_id, rule_info) + return firewall_policy + + @abc.abstractmethod + def insert_rule_precommit(self, context, policy_id, rule_info): + pass + + @abc.abstractmethod + def insert_rule_postcommit(self, context, policy_id, rule_info): + pass + + def remove_rule(self, context, policy_id, rule_info): + self.remove_rule_precommit(context, policy_id, rule_info) + firewall_policy = self.firewall_db.remove_rule(context, policy_id, + rule_info) + self.remove_rule_postcommit(context, policy_id, rule_info) + return firewall_policy + + @abc.abstractmethod + def remove_rule_precommit(self, context, policy_id, rule_info): + pass + + @abc.abstractmethod + def remove_rule_postcommit(self, context, policy_id, rule_info): + pass + + +class FirewallDriverDB(FirewallDriverDBMixin): + """FirewallDriverDBMixin interface for driver with database. + + Each firewall backend driver that needs a database persistency should + inherit from this driver. + It can overload needed methods from the following pre/postcommit methods. + Any exception raised during a precommit method will result in not having + related records in the databases. + """ + + #Firewal Group + def create_firewall_group_precommit(self, context, firewall_group): + pass + + def create_firewall_group_postcommit(self, context, firewall_group): + pass + + def update_firewall_group_precommit(self, context, old_firewall_group, + new_firewall_group): + pass + + def update_firewall_group_postcommit(self, context, old_firewall_group, + new_firewall_group): + pass + + def delete_firewall_group_precommit(self, context, firewall_group): + pass + + def delete_firewall_group_postcommit(self, context, firewall_group): + pass + + #Firewall Policy + def create_firewall_policy_precommit(self, context, firewall_policy): + pass + + def create_firewall_policy_postcommit(self, context, firewall_policy): + pass + + def update_firewall_policy_precommit(self, context, old_firewall_policy, + new_firewall_policy): + pass + + def update_firewall_policy_postcommit(self, context, old_firewall_policy, + new_firewall_policy): + pass + + def delete_firewall_policy_precommit(self, context, firewall_policy): + pass + + def delete_firewall_policy_postcommit(self, context, firewall_policy): + pass + + #Firewall Rule + def create_firewall_rule_precommit(self, context, firewall_rule): + pass + + def create_firewall_rule_postcommit(self, context, firewall_rule): + pass + + def update_firewall_rule_precommit(self, context, old_firewall_rule, + new_firewall_rule): + pass + + def update_firewall_rule_postcommit(self, context, old_firewall_rule, + new_firewall_rule): + pass + + def delete_firewall_rule_precommit(self, context, firewall_rule): + pass + + def delete_firewall_rule_postcommit(self, context, firewall_rule): + pass + + def insert_rule_precommit(self, context, policy_id, rule_info): + pass + + def insert_rule_postcommit(self, context, policy_id, rule_info): + pass + + def remove_rule_precommit(self, context, policy_id, rule_info): + pass + + def remove_rule_postcommit(self, context, policy_id, rule_info): + pass diff --git a/neutron_fwaas/tests/unit/db/firewall/v2/test_firewall_db_v2.py b/neutron_fwaas/tests/unit/db/firewall/v2/test_firewall_db_v2.py index 34038f044..913aca6e3 100644 --- a/neutron_fwaas/tests/unit/db/firewall/v2/test_firewall_db_v2.py +++ b/neutron_fwaas/tests/unit/db/firewall/v2/test_firewall_db_v2.py @@ -13,387 +13,26 @@ # License for the specific language governing permissions and limitations # under the License. -import contextlib - import mock -from neutron.api import extensions as api_ext -from neutron.common import config -from neutron_lib.api.definitions import firewall_v2 -from neutron_lib import constants as nl_constants -from neutron_lib import context -from neutron_lib.exceptions import firewall_v2 as f_exc -from neutron_lib.plugins import directory -from oslo_config import cfg -from oslo_utils import importutils -from oslo_utils import uuidutils import six import testtools import webob.exc +from neutron_lib import constants as nl_constants +from neutron_lib.exceptions import firewall_v2 as f_exc +from oslo_utils import uuidutils + from neutron_fwaas.common import fwaas_constants as constants -from neutron_fwaas.db.firewall.v2 import firewall_db_v2 as fdb -from neutron_fwaas import extensions -from neutron_fwaas.services.firewall import fwaas_plugin_v2 -from neutron_fwaas.tests import base +from neutron_fwaas.tests.unit.services.firewall import test_fwaas_plugin_v2 -DB_FW_PLUGIN_KLASS = ( - "neutron_fwaas.db.firewall.v2.firewall_db_v2.Firewall_db_mixin_v2" -) -FWAAS_PLUGIN = 'neutron_fwaas.services.firewall.fwaas_plugin_v2' -DELETEFW_PATH = FWAAS_PLUGIN + '.FirewallAgentApi.delete_firewall_group' -extensions_path = ':'.join(extensions.__path__) -DESCRIPTION = 'default description' -PROTOCOL = 'tcp' -IP_VERSION = 4 -SOURCE_IP_ADDRESS_RAW = '1.1.1.1' -DESTINATION_IP_ADDRESS_RAW = '2.2.2.2' -SOURCE_PORT = '55000:56000' -DESTINATION_PORT = '56000:57000' -ACTION = 'allow' -AUDITED = True -ENABLED = True -ADMIN_STATE_UP = True -SHARED = True +class TestFirewallDBPluginV2(test_fwaas_plugin_v2.FirewallPluginV2TestCase): - -class FakeAgentApi(fwaas_plugin_v2.FirewallCallbacks): - """ - This class used to mock the AgentAPI delete method inherits from - FirewallCallbacks because it needs access to the firewall_deleted method. - The delete_firewall method belongs to the FirewallAgentApi, which has - no access to the firewall_deleted method normally because it's not - responsible for deleting the firewall from the DB. However, it needs - to in the unit tests since there is no agent to call back. - """ - def __init__(self): - pass - - def delete_firewall_group(self, context, firewall_group, **kwargs): - self.plugin = directory.get_plugin('FIREWALL_V2') - self.firewall_group_deleted(context, firewall_group['id'], **kwargs) - - -class FirewallPluginV2DbTestCase(base.NeutronDbPluginV2TestCase): - resource_prefix_map = dict( - (k, firewall_v2.API_PREFIX) - for k in firewall_v2.RESOURCE_ATTRIBUTE_MAP.keys() - ) - - def setUp(self, core_plugin=None, fw_plugin=None, ext_mgr=None): - self.agentapi_delf_p = mock.patch( - DELETEFW_PATH, create=True, - new=FakeAgentApi().delete_firewall_group) - self.agentapi_delf_p.start() - if not fw_plugin: - fw_plugin = DB_FW_PLUGIN_KLASS - service_plugins = {'fw_plugin_name': fw_plugin} - - fdb.Firewall_db_mixin_v2.supported_extension_aliases = ["fwaas_v2"] - fdb.Firewall_db_mixin_v2.path_prefix = firewall_v2.API_PREFIX - super(FirewallPluginV2DbTestCase, self).setUp( - ext_mgr=ext_mgr, - service_plugins=service_plugins - ) - - if not ext_mgr: - self.plugin = importutils.import_object(fw_plugin) - ext_mgr = api_ext.PluginAwareExtensionManager( - extensions_path, - {'FIREWALL': self.plugin} - ) - app = config.load_paste_app('extensions_test_app') - self.ext_api = api_ext.ExtensionMiddleware(app, ext_mgr=ext_mgr) - router_distributed_opts = [ - cfg.BoolOpt( - 'router_distributed', - default=False, - help=("System-wide flag to determine the type of router " - "that tenants can create. Only admin can override.")), - ] - cfg.CONF.register_opts(router_distributed_opts) - - def _get_admin_context(self): - # FIXME NOTE(ivasilevskaya) seems that test framework treats context - # with user_id=None/tenant_id=None (return value of - # context._get_admin_context() method) in a somewhat special way. - # So as a workaround to have the framework behave properly right now - # let's implement our own _get_admin_context method and look into the - # matter some other time. - return context.Context(user_id='admin', - tenant_id='admin-tenant', - is_admin=True) - - def _get_nonadmin_context(self, user_id=None, tenant_id=None): - return context.Context(user_id=user_id or 'non-admin', - tenant_id=tenant_id or 'tenant1') - - def _test_list_resources(self, resource, items, - neutron_context=None, - query_params=None): - if resource.endswith('y'): - resource_plural = resource.replace('y', 'ies') - else: - resource_plural = resource + 's' - - res = self._list(resource_plural, - neutron_context=neutron_context, - query_params=query_params) - resource = resource.replace('-', '_') - self.assertEqual( - sorted([i[resource]['id'] for i in items]), - sorted([i['id'] for i in res[resource_plural]])) - - def _list_req(self, resource_plural, ctx=None): - if not ctx: - ctx = self._get_admin_context() - req = self.new_list_request(resource_plural) - req.environ['neutron.context'] = ctx - return self.deserialize( - self.fmt, req.get_response(self.ext_api))[resource_plural] - - def _show_req(self, resource_plural, obj_id, ctx=None): - req = self.new_show_request(resource_plural, obj_id, fmt=self.fmt) - if not ctx: - ctx = self._get_admin_context() - req.environ['neutron.context'] = ctx - res = self.deserialize( - self.fmt, req.get_response(self.ext_api)) - return res - - def _build_default_fwg(self, ctx=None, is_one=True): - res = self._list_req('firewall_groups', ctx=ctx) - if is_one: - self.assertEqual(1, len(res)) - return res[0] - return res - - def _get_test_firewall_rule_attrs(self, name='firewall_rule1'): - attrs = {'name': name, - 'tenant_id': self._tenant_id, - 'project_id': self._tenant_id, - 'protocol': PROTOCOL, - 'ip_version': IP_VERSION, - 'source_ip_address': SOURCE_IP_ADDRESS_RAW, - 'destination_ip_address': DESTINATION_IP_ADDRESS_RAW, - 'source_port': SOURCE_PORT, - 'destination_port': DESTINATION_PORT, - 'action': ACTION, - 'enabled': ENABLED, - 'shared': SHARED} - return attrs - - def _get_test_firewall_policy_attrs(self, name='firewall_policy1', - audited=AUDITED): - attrs = {'name': name, - 'description': DESCRIPTION, - 'tenant_id': self._tenant_id, - 'project_id': self._tenant_id, - 'firewall_rules': [], - 'audited': audited, - 'shared': SHARED} - return attrs - - def _get_test_firewall_group_attrs(self, name='firewall_1', - status='PENDING_CREATE'): - attrs = {'name': name, - 'tenant_id': self._tenant_id, - 'project_id': self._tenant_id, - 'admin_state_up': ADMIN_STATE_UP, - 'status': status} - - return attrs - - def _create_firewall_policy(self, fmt, name, description, shared, - firewall_rules, audited, - expected_res_status=None, **kwargs): - tenant_id = kwargs.get('tenant_id', self._tenant_id) - data = {'firewall_policy': {'name': name, - 'description': description, - 'tenant_id': tenant_id, - 'project_id': tenant_id, - 'firewall_rules': firewall_rules, - 'audited': audited, - 'shared': shared}} - - fw_policy_req = self.new_create_request('firewall_policies', data, fmt) - fw_policy_res = fw_policy_req.get_response(self.ext_api) - if expected_res_status: - self.assertEqual(expected_res_status, fw_policy_res.status_int) - - return fw_policy_res - - def _replace_firewall_status(self, attrs, old_status, new_status): - if attrs['status'] is old_status: - attrs['status'] = new_status - return attrs - - @contextlib.contextmanager - def firewall_policy(self, fmt=None, name='firewall_policy1', - description=DESCRIPTION, shared=SHARED, - firewall_rules=None, audited=True, - do_delete=True, **kwargs): - if firewall_rules is None: - firewall_rules = [] - if not fmt: - fmt = self.fmt - res = self._create_firewall_policy(fmt, name, description, shared, - firewall_rules, audited, **kwargs) - if res.status_int >= 400: - raise webob.exc.HTTPClientError(code=res.status_int) - firewall_policy = self.deserialize(fmt or self.fmt, res) - yield firewall_policy - if do_delete: - self._delete('firewall_policies', - firewall_policy['firewall_policy']['id']) - - def _create_firewall_rule(self, fmt, name, shared, protocol, - ip_version, source_ip_address, - destination_ip_address, source_port, - destination_port, action, enabled, - expected_res_status=None, **kwargs): - tenant_id = kwargs.get('tenant_id', self._tenant_id) - data = {'firewall_rule': {'name': name, - 'tenant_id': tenant_id, - 'project_id': tenant_id, - 'protocol': protocol, - 'ip_version': ip_version, - 'source_ip_address': source_ip_address, - 'destination_ip_address': - destination_ip_address, - 'source_port': source_port, - 'destination_port': destination_port, - 'action': action, - 'enabled': enabled, - 'shared': shared}} - - fw_rule_req = self.new_create_request('firewall_rules', data, fmt) - fw_rule_res = fw_rule_req.get_response(self.ext_api) - if expected_res_status: - self.assertEqual(expected_res_status, fw_rule_res.status_int) - - return fw_rule_res - - @contextlib.contextmanager - def firewall_rule(self, fmt=None, name='firewall_rule1', - shared=SHARED, protocol=PROTOCOL, ip_version=IP_VERSION, - source_ip_address=SOURCE_IP_ADDRESS_RAW, - destination_ip_address=DESTINATION_IP_ADDRESS_RAW, - source_port=SOURCE_PORT, - destination_port=DESTINATION_PORT, - action=ACTION, enabled=ENABLED, - do_delete=True, **kwargs): - if not fmt: - fmt = self.fmt - res = self._create_firewall_rule(fmt, name, shared, protocol, - ip_version, source_ip_address, - destination_ip_address, - source_port, destination_port, - action, enabled, **kwargs) - if res.status_int >= 400: - raise webob.exc.HTTPClientError(code=res.status_int) - firewall_rule = self.deserialize(fmt or self.fmt, res) - yield firewall_rule - if do_delete: - self._delete('firewall_rules', - firewall_rule['firewall_rule']['id']) - - def _create_firewall_group(self, fmt, name, description, - ingress_firewall_policy_id, - egress_firewall_policy_id, - ports=None, admin_state_up=True, - expected_res_status=None, **kwargs): - tenant_id = kwargs.get('tenant_id', self._tenant_id) - if ingress_firewall_policy_id is None: - default_policy = kwargs.get('default_policy', True) - if default_policy: - res = self._create_firewall_policy(fmt, 'fwp', - description=DESCRIPTION, - shared=SHARED, - firewall_rules=[], - audited=AUDITED) - firewall_policy = self.deserialize(fmt or self.fmt, res) - fwp_id = firewall_policy["firewall_policy"]["id"] - ingress_firewall_policy_id = fwp_id - data = {'firewall_group': {'name': name, - 'description': description, - 'ingress_firewall_policy_id': ingress_firewall_policy_id, - 'egress_firewall_policy_id': egress_firewall_policy_id, - 'admin_state_up': admin_state_up}} - ctx = kwargs.get('context', None) - if ctx is None or ctx.is_admin: - data['firewall_group'].update({'tenant_id': tenant_id}) - data['firewall_group'].update({'project_id': tenant_id}) - if ports is not None: - data['firewall_group'].update({'ports': ports}) - - firewall_req = self.new_create_request('firewall_groups', data, fmt, - context=ctx) - firewall_res = firewall_req.get_response(self.ext_api) - if expected_res_status: - self.assertEqual(expected_res_status, firewall_res.status_int) - - return firewall_res - - @contextlib.contextmanager - def firewall_group(self, fmt=None, name='firewall_1', - description=DESCRIPTION, - ingress_firewall_policy_id=None, - egress_firewall_policy_id=None, - ports=None, admin_state_up=True, - do_delete=True, **kwargs): - if not fmt: - fmt = self.fmt - res = self._create_firewall_group(fmt, name, description, - ingress_firewall_policy_id, - egress_firewall_policy_id, - ports=ports, - admin_state_up=admin_state_up, - **kwargs) - if res.status_int >= 400: - raise webob.exc.HTTPClientError(code=res.status_int) - firewall_group = self.deserialize(fmt or self.fmt, res) - yield firewall_group - if do_delete: - self._delete('firewall_groups', - firewall_group['firewall_group']['id']) - - def _rule_action(self, action, id, firewall_rule_id, insert_before=None, - insert_after=None, expected_code=webob.exc.HTTPOk.code, - expected_body=None, body_data=None): - # We intentionally do this check for None since we want to distinguish - # from empty dictionary - if body_data is None: - if action == 'insert': - body_data = {'firewall_rule_id': firewall_rule_id, - 'insert_before': insert_before, - 'insert_after': insert_after} - else: - body_data = {'firewall_rule_id': firewall_rule_id} - - req = self.new_action_request('firewall_policies', - body_data, id, - "%s_rule" % action) - res = req.get_response(self.ext_api) - self.assertEqual(expected_code, res.status_int) - response = self.deserialize(self.fmt, res) - if expected_body: - self.assertEqual(expected_body, response) - return response - - def _compare_firewall_rule_lists(self, firewall_policy_id, - observed_list, expected_list): - position = 0 - for r1, r2 in zip(observed_list, expected_list): - rule = r1['firewall_rule'] - rule['firewall_policy_id'] = firewall_policy_id - position += 1 - rule['position'] = position - for k in rule: - self.assertEqual(r2[k], rule[k]) - - -class TestFirewallDBPluginV2(FirewallPluginV2DbTestCase): + def setUp(self): + provider = ('neutron_fwaas.services.firewall.service_drivers.' + 'driver_api.FirewallDriverDB') + super(TestFirewallDBPluginV2, self).setUp(service_provider=provider) + self.db = self.plugin.driver.firewall_db def test_get_policy_ordered_rules(self): with self.firewall_rule(name='alone'), \ @@ -405,7 +44,7 @@ class TestFirewallDBPluginV2(FirewallPluginV2DbTestCase): with self.firewall_policy(firewall_rules=expected_ids) as fwp: ctx = self._get_admin_context() fwp_id = fwp['firewall_policy']['id'] - observeds = self.plugin._get_policy_ordered_rules(ctx, fwp_id) + observeds = self.db._get_policy_ordered_rules(ctx, fwp_id) observed_ids = [r['id'] for r in observeds] self.assertEqual(expected_ids, observed_ids) @@ -413,8 +52,8 @@ class TestFirewallDBPluginV2(FirewallPluginV2DbTestCase): name = "firewall_policy1" attrs = self._get_test_firewall_policy_attrs(name) - with self.firewall_policy(name=name, shared=SHARED, - firewall_rules=None, audited=AUDITED + with self.firewall_policy(name=name, shared=self.SHARED, + firewall_rules=None, audited=self.AUDITED ) as firewall_policy: for k, v in six.iteritems(attrs): self.assertEqual(v, firewall_policy['firewall_policy'][k]) @@ -429,49 +68,29 @@ class TestFirewallDBPluginV2(FirewallPluginV2DbTestCase): fr = [fwr1, fwr2, fwr3] fw_rule_ids = [r['firewall_rule']['id'] for r in fr] attrs['firewall_rules'] = fw_rule_ids - with self.firewall_policy(name=name, shared=SHARED, + with self.firewall_policy(name=name, shared=self.SHARED, firewall_rules=fw_rule_ids, - audited=AUDITED) as fwp: + audited=self.AUDITED) as fwp: for k, v in six.iteritems(attrs): self.assertEqual(v, fwp['firewall_policy'][k]) - def test_create_admin_firewall_policy_with_other_tenant_rules(self): - with self.firewall_rule(shared=False) as fr: - fw_rule_ids = [fr['firewall_rule']['id']] - res = self._create_firewall_policy(None, 'firewall_policy1', - description=DESCRIPTION, - shared=SHARED, - firewall_rules=fw_rule_ids, - audited=AUDITED, - tenant_id='admin-tenant') - self.assertEqual(webob.exc.HTTPNotFound.code, res.status_int) - def test_create_firewall_policy_with_previously_associated_rule(self): with self.firewall_rule() as fwr: fw_rule_ids = [fwr['firewall_rule']['id']] with self.firewall_policy(firewall_rules=fw_rule_ids): - with self.firewall_policy(shared=SHARED, + with self.firewall_policy(shared=self.SHARED, firewall_rules=fw_rule_ids) as fwp2: self.assertEqual( fwr['firewall_rule']['id'], fwp2['firewall_policy']['firewall_rules'][0]) - def test_create_shared_firewall_policy_with_nonshared_rule(self): - with self.firewall_rule(shared=False) as fwr: - fw_rule_ids = [fwr['firewall_rule']['id']] - res = self._create_firewall_policy(None, 'firewall_policy1', - description=DESCRIPTION, - shared=SHARED, - firewall_rules=fw_rule_ids, - audited=AUDITED) - self.assertEqual(webob.exc.HTTPNotFound.code, res.status_int) - def test_show_firewall_policy(self): name = "firewall_policy1" attrs = self._get_test_firewall_policy_attrs(name) - with self.firewall_policy(name=name, shared=SHARED, - firewall_rules=None, audited=AUDITED) as fwp: + with self.firewall_policy(name=name, shared=self.SHARED, + firewall_rules=None, + audited=self.AUDITED) as fwp: res = self._show_req('firewall_policies', fwp['firewall_policy']['id']) for k, v in six.iteritems(attrs): @@ -490,8 +109,8 @@ class TestFirewallDBPluginV2(FirewallPluginV2DbTestCase): name = "new_firewall_policy1" attrs = self._get_test_firewall_policy_attrs(name, audited=False) - with self.firewall_policy(shared=SHARED, firewall_rules=None, - audited=AUDITED) as fwp: + with self.firewall_policy(shared=self.SHARED, firewall_rules=None, + audited=self.AUDITED) as fwp: data = {'firewall_policy': {'name': name}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) @@ -501,7 +120,7 @@ class TestFirewallDBPluginV2(FirewallPluginV2DbTestCase): def _test_update_firewall_policy(self, with_audited): with self.firewall_policy(name='firewall_policy1', description='fwp', - audited=AUDITED) as fwp: + audited=self.AUDITED) as fwp: attrs = self._get_test_firewall_policy_attrs(audited=with_audited) data = {'firewall_policy': {'description': 'fw_p1'}} @@ -658,7 +277,7 @@ class TestFirewallDBPluginV2(FirewallPluginV2DbTestCase): with self.firewall_policy(shared=False) as fwp: fw_rule_ids = [fr['firewall_rule']['id']] # update shared policy with shared attr and nonshared rule - data = {'firewall_policy': {'shared': SHARED, + data = {'firewall_policy': {'shared': self.SHARED, 'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) @@ -671,14 +290,15 @@ class TestFirewallDBPluginV2(FirewallPluginV2DbTestCase): with self.firewall_policy(shared=False, firewall_rules=fw_rule_ids) as fwp: # update policy with shared attr - data = {'firewall_policy': {'shared': SHARED}} + data = {'firewall_policy': {'shared': self.SHARED}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) res = req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_update_firewall_policy_assoc_with_other_tenant_firewall(self): - with self.firewall_policy(shared=SHARED, tenant_id='tenant1') as fwp: + with self.firewall_policy(shared=self.SHARED, + tenant_id='tenant1') as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall_group(ingress_firewall_policy_id=fwp_id, egress_firewall_policy_id=fwp_id): @@ -790,13 +410,11 @@ class TestFirewallDBPluginV2(FirewallPluginV2DbTestCase): self.assertIsNone(fw_rule['ingress_firewall_policy_id']) def test_delete_firewall_policy_with_firewall_group_association(self): - attrs = self._get_test_firewall_group_attrs() with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] - attrs['firewall_policy_id'] = fwp_id with self.firewall_group( ingress_firewall_policy_id=fwp_id, - admin_state_up=ADMIN_STATE_UP): + admin_state_up=self.ADMIN_STATE_UP): req = self.new_delete_request('firewall_policies', fwp_id) res = req.get_response(self.ext_api) self.assertEqual(409, res.status_int) @@ -832,20 +450,20 @@ class TestFirewallDBPluginV2(FirewallPluginV2DbTestCase): def test_create_firewall_src_port_illegal_range(self): attrs = self._get_test_firewall_rule_attrs() attrs['source_port'] = '65535:1024' - res = self._create_firewall_rule(self.fmt, **attrs) - self.assertEqual(400, res.status_int) + attrs['expected_res_status'] = 400 + self._create_firewall_rule(self.fmt, **attrs) def test_create_firewall_dest_port_illegal_range(self): attrs = self._get_test_firewall_rule_attrs() attrs['destination_port'] = '65535:1024' - res = self._create_firewall_rule(self.fmt, **attrs) - self.assertEqual(400, res.status_int) + attrs['expected_res_status'] = 400 + self._create_firewall_rule(self.fmt, **attrs) def test_create_firewall_rule_icmp_with_port(self): attrs = self._get_test_firewall_rule_attrs() attrs['protocol'] = 'icmp' - res = self._create_firewall_rule(self.fmt, **attrs) - self.assertEqual(400, res.status_int) + attrs['expected_res_status'] = 400 + self._create_firewall_rule(self.fmt, **attrs) def test_create_firewall_rule_icmp_without_port(self): attrs = self._get_test_firewall_rule_attrs() @@ -862,28 +480,28 @@ class TestFirewallDBPluginV2(FirewallPluginV2DbTestCase): def test_create_firewall_without_source(self): attrs = self._get_test_firewall_rule_attrs() attrs['source_ip_address'] = None - res = self._create_firewall_rule(self.fmt, **attrs) - self.assertEqual(201, res.status_int) + attrs['expected_res_status'] = 201 + self._create_firewall_rule(self.fmt, **attrs) def test_create_firewall_rule_without_destination(self): attrs = self._get_test_firewall_rule_attrs() attrs['destination_ip_address'] = None - res = self._create_firewall_rule(self.fmt, **attrs) - self.assertEqual(201, res.status_int) + attrs['expected_res_status'] = 201 + self._create_firewall_rule(self.fmt, **attrs) def test_create_firewall_rule_without_protocol_with_dport(self): attrs = self._get_test_firewall_rule_attrs() attrs['protocol'] = None attrs['source_port'] = None - res = self._create_firewall_rule(self.fmt, **attrs) - self.assertEqual(400, res.status_int) + attrs['expected_res_status'] = 400 + self._create_firewall_rule(self.fmt, **attrs) def test_create_firewall_rule_without_protocol_with_sport(self): attrs = self._get_test_firewall_rule_attrs() attrs['protocol'] = None attrs['destination_port'] = None - res = self._create_firewall_rule(self.fmt, **attrs) - self.assertEqual(400, res.status_int) + attrs['expected_res_status'] = 400 + self._create_firewall_rule(self.fmt, **attrs) def test_show_firewall_rule_with_fw_policy_not_associated(self): attrs = self._get_test_firewall_rule_attrs() @@ -916,22 +534,22 @@ class TestFirewallDBPluginV2(FirewallPluginV2DbTestCase): attrs['source_ip_address'] = '::/0' attrs['destination_ip_address'] = '2001:db8:3::/64' attrs['ip_version'] = 4 - res = self._create_firewall_rule(self.fmt, **attrs) - self.assertEqual(400, res.status_int) + attrs['expected_res_status'] = 400 + self._create_firewall_rule(self.fmt, **attrs) attrs = self._get_test_firewall_rule_attrs() attrs['source_ip_address'] = None attrs['destination_ip_address'] = '2001:db8:3::/64' attrs['ip_version'] = 4 - res = self._create_firewall_rule(self.fmt, **attrs) - self.assertEqual(400, res.status_int) + attrs['expected_res_status'] = 400 + self._create_firewall_rule(self.fmt, **attrs) attrs = self._get_test_firewall_rule_attrs() attrs['source_ip_address'] = '::/0' attrs['destination_ip_address'] = None attrs['ip_version'] = 4 - res = self._create_firewall_rule(self.fmt, **attrs) - self.assertEqual(400, res.status_int) + attrs['expected_res_status'] = 400 + self._create_firewall_rule(self.fmt, **attrs) def test_list_firewall_rules(self): with self.firewall_rule(name='fwr1') as fwr1, \ @@ -950,7 +568,7 @@ class TestFirewallDBPluginV2(FirewallPluginV2DbTestCase): attrs['destination_port'] = '30:40' with self.firewall_rule() as fwr: data = {'firewall_rule': {'name': name, - 'protocol': PROTOCOL, + 'protocol': self.PROTOCOL, 'source_port': '10:20', 'destination_port': '30:40'}} req = self.new_update_request('firewall_rules', data, @@ -964,7 +582,7 @@ class TestFirewallDBPluginV2(FirewallPluginV2DbTestCase): attrs['destination_port'] = '80' with self.firewall_rule() as fwr: data = {'firewall_rule': {'name': name, - 'protocol': PROTOCOL, + 'protocol': self.PROTOCOL, 'source_port': 10000, 'destination_port': 80}} req = self.new_update_request('firewall_rules', data, @@ -978,7 +596,7 @@ class TestFirewallDBPluginV2(FirewallPluginV2DbTestCase): attrs['destination_port'] = '80' with self.firewall_rule() as fwr: data = {'firewall_rule': {'name': name, - 'protocol': PROTOCOL, + 'protocol': self.PROTOCOL, 'source_port': '10000', 'destination_port': '80'}} req = self.new_update_request('firewall_rules', data, @@ -1095,7 +713,7 @@ class TestFirewallDBPluginV2(FirewallPluginV2DbTestCase): @testtools.skip('bug/1614680') def test_update_firewall_rule_associated_with_other_tenant_policy(self): - with self.firewall_rule(shared=SHARED, tenant_id='tenant1') as fwr: + with self.firewall_rule(shared=self, tenant_id='tenant1') as fwr: fwr_id = [fwr['firewall_rule']['id']] with self.firewall_policy(shared=False, firewall_rules=fwr_id): data = {'firewall_rule': {'shared': False}} @@ -1138,21 +756,6 @@ class TestFirewallDBPluginV2(FirewallPluginV2DbTestCase): res = req.get_response(self.ext_api) self.assertEqual(409, res.status_int) - def _test_create_firewall_group(self, attrs): - with self.firewall_policy() as fwp: - fwp_id = fwp['firewall_policy']['id'] - attrs['ingress_firewall_policy_id'] = fwp_id - attrs['egress_firewall_policy_id'] = fwp_id - with self.firewall_group( - name=attrs['name'], - ingress_firewall_policy_id=fwp_id, - egress_firewall_policy_id=fwp_id, - admin_state_up=ADMIN_STATE_UP, - ports=attrs['ports'] if 'ports' in attrs else None, - ) as firewall_group: - for k, v in six.iteritems(attrs): - self.assertEqual(v, firewall_group['firewall_group'][k]) - def test_create_firewall_group(self): attrs = self._get_test_firewall_group_attrs("firewall1") self._test_create_firewall_group(attrs) @@ -1247,11 +850,6 @@ class TestFirewallDBPluginV2(FirewallPluginV2DbTestCase): attrs = self._get_test_firewall_group_attrs("firewall1") self._test_create_firewall_group(attrs) - def test_create_firewall_group_with_dvr(self): - cfg.CONF.set_override('router_distributed', True) - attrs = self._get_test_firewall_group_attrs("firewall1", "CREATED") - self._test_create_firewall_group(attrs) - def test_create_firewall_group_with_fwp_does_not_exist(self): fmt = self.fmt fwg_name = "firewall1" @@ -1260,7 +858,7 @@ class TestFirewallDBPluginV2(FirewallPluginV2DbTestCase): self._create_firewall_group(fmt, fwg_name, description, not_found_fwp_id, not_found_fwp_id, ports=None, - admin_state_up=ADMIN_STATE_UP, + admin_state_up=self.ADMIN_STATE_UP, expected_res_status=404) def test_create_firewall_group_with_fwp_on_different_tenant(self): @@ -1296,10 +894,12 @@ class TestFirewallDBPluginV2(FirewallPluginV2DbTestCase): fwp_id = fwp['firewall_policy']['id'] ctx = self._get_admin_context() target_tenant = 'tenant1' - with self.firewall_group(name=fwg_name, - ingress_firewall_policy_id=fwp_id, - tenant_id=target_tenant, context=ctx, - admin_state_up=ADMIN_STATE_UP) as fwg: + with self.firewall_group( + name=fwg_name, + ingress_firewall_policy_id=fwp_id, + tenant_id=target_tenant, + context=ctx, + admin_state_up=self.ADMIN_STATE_UP) as fwg: self.assertEqual(target_tenant, fwg['firewall_group']['tenant_id']) @@ -1308,13 +908,12 @@ class TestFirewallDBPluginV2(FirewallPluginV2DbTestCase): fwp_id = fwp['firewall_policy']['id'] attrs['ingress_firewall_policy_id'] = fwp_id attrs['egress_firewall_policy_id'] = fwp_id - attrs['status'] = 'PENDING_CREATE' with self.firewall_group( name=attrs['name'], ports=attrs['ports'] if 'ports' in attrs else None, ingress_firewall_policy_id=fwp_id, egress_firewall_policy_id=fwp_id, - admin_state_up=ADMIN_STATE_UP) as firewall_group: + admin_state_up=self.ADMIN_STATE_UP) as firewall_group: res = self._show_req('firewall_groups', firewall_group['firewall_group']['id']) for k, v in six.iteritems(attrs): @@ -1362,7 +961,7 @@ class TestFirewallDBPluginV2(FirewallPluginV2DbTestCase): fwp_id = fwp['firewall_policy']['id'] with self.firewall_group( ingress_firewall_policy_id=fwp_id, - admin_state_up=ADMIN_STATE_UP) as firewall: + admin_state_up=self.ADMIN_STATE_UP) as firewall: data = {'firewall_group': {'name': name}} req = self.new_update_request('firewall_groups', data, firewall['firewall_group']['id']) @@ -1385,7 +984,7 @@ class TestFirewallDBPluginV2(FirewallPluginV2DbTestCase): def_fwg_id = self._build_default_fwg(ctx=ctx)['id'] with self.port( device_owner=nl_constants.DEVICE_OWNER_ROUTER_INTF, - ctx=ctx) as dummy_port: + tenant_id=ctx.project_id) as dummy_port: port_id = dummy_port['port']['id'] success_cases = [ {'ports': [port_id]}, @@ -1406,7 +1005,7 @@ class TestFirewallDBPluginV2(FirewallPluginV2DbTestCase): def_fwg_id = self._build_default_fwg(ctx=ctx)['id'] with self.port( device_owner=nl_constants.DEVICE_OWNER_ROUTER_INTF, - ctx=ctx) as dummy_port: + tenant_id=ctx.project_id) as dummy_port: port_id = dummy_port['port']['id'] conflict_cases = [ {'name': ''}, @@ -1432,7 +1031,7 @@ class TestFirewallDBPluginV2(FirewallPluginV2DbTestCase): ctx = self._get_admin_context() with self.port( device_owner=nl_constants.DEVICE_OWNER_ROUTER_INTF, - ctx=ctx) as dummy_port: + tenant_id=ctx.project_id) as dummy_port: port_id = dummy_port['port']['id'] def_fwg_id = self._build_default_fwg(ctx=ctx)['id'] success_cases = [ @@ -1457,7 +1056,7 @@ class TestFirewallDBPluginV2(FirewallPluginV2DbTestCase): ctx = self._get_admin_context() with self.port( device_owner=nl_constants.DEVICE_OWNER_ROUTER_INTF, - ctx=ctx) as dummy_port: + tenant_id=ctx.project_id) as dummy_port: port_id = dummy_port['port']['id'] def_fwg_id = self._build_default_fwg(ctx=ctx)['id'] conflict_cases = [ @@ -1547,26 +1146,29 @@ class TestFirewallDBPluginV2(FirewallPluginV2DbTestCase): self.assertEqual(404, res.status_int) def test_update_firewall_group_fwp_not_found_on_different_tenant(self): - with self.firewall_policy(name='fwp1', tenant_id='tenant1', - do_delete=False) as fwp1, \ - self.firewall_policy(name='fwp2', shared=False, - tenant_id='tenant2') as fwp2: + ctx_tenant1 = self._get_nonadmin_context(tenant_id='tenant1') + ctx_tenant2 = self._get_nonadmin_context(tenant_id='tenant2') - fwps = [fwp1, fwp2] - # create firewall using fwp1 exists the same tenant. - fwp1_id = fwps[0]['firewall_policy']['id'] - fwp2_id = fwps[1]['firewall_policy']['id'] - ctx = self._get_nonadmin_context() - with self.firewall_group(ingress_firewall_policy_id=fwp1_id, - context=ctx) as firewall: - fw_id = firewall['firewall_group']['id'] - fw_db = self.plugin._get_firewall_group(ctx, fw_id) - fw_db['status'] = nl_constants.ACTIVE - # update firewall from fwp1 to fwp2(different tenant) - data = {'firewall_group': - {'ingress_firewall_policy_id': fwp2_id}} - req = self.new_update_request('firewall_groups', data, fw_id, - context=ctx) + with self.firewall_policy(name='fwp1', context=ctx_tenant1, + shared=False, do_delete=False) as fwp1, \ + self.firewall_group( + ingress_firewall_policy_id=fwp1['firewall_policy']['id'], + context=ctx_tenant1, do_delete=False) as fwg: + fwg_id = fwg['firewall_group']['id'] + # fw_db = self.db._get_firewall_group(ctx_tenant1, fwg_id) + # fw_db['status'] = nl_constants.ACTIVE + + # update firewall from fwp1 to fwp2 (different tenant) + with self.firewall_policy(name='fwp2', context=ctx_tenant2, + shared=False) as fwp2: + data = { + 'firewall_group': { + 'ingress_firewall_policy_id': + fwp2['firewall_policy']['id'], + }, + } + req = self.new_update_request('firewall_groups', data, fwg_id, + context=ctx_tenant1) res = req.get_response(self.ext_api) self.assertEqual(404, res.status_int) @@ -1586,11 +1188,11 @@ class TestFirewallDBPluginV2(FirewallPluginV2DbTestCase): def test_delete_firewall_group_already_deleted(self): ctx = self._get_admin_context() - deleted_id = uuidutils.generate_uuid() - with self.firewall_group(do_delete=False) as fwg: + with self.firewall_group(do_delete=False, context=ctx) as fwg: fwg_id = fwg['firewall_group']['id'] self.assertIsNone(self.plugin.delete_firewall_group(ctx, fwg_id)) - self.assertIsNone(self.plugin.delete_firewall_group(ctx, deleted_id)) + # No error raise is fwg not found on delete + self.assertIsNone(self.plugin.delete_firewall_group(ctx, fwg_id)) def test_delete_default_firewall_group_with_admin(self): ctx_a = self._get_admin_context() @@ -1980,45 +1582,36 @@ class TestFirewallDBPluginV2(FirewallPluginV2DbTestCase): def test_set_port_in_use_for_firewall_group(self): fwg_db = {'id': 'fake_id'} new_ports = {'ports': ['fake_port1', 'fake_port2']} - m_context = context.get_admin_context() + m_context = self._get_admin_context() with mock.patch.object(m_context.session, 'add', side_effect=[None, f_exc.FirewallGroupPortInUse( port_ids=['fake_port2'])]): self.assertRaises(f_exc.FirewallGroupPortInUse, - self.plugin._set_ports_for_firewall_group, + self.db._set_ports_for_firewall_group, m_context, fwg_db, new_ports) def test_set_port_for_default_firewall_group(self): ctx = self._get_nonadmin_context() - self._build_default_fwg(ctx=ctx) - with self.port(project_id=ctx.tenant_id) as port1, \ - self.port(project_id=ctx.tenant_id) as port2: + default_fwg = self._build_default_fwg(ctx=ctx) + port_args = { + 'tenant_id': ctx.tenant_id, + 'device_owner': 'compute:nova', + 'binding:vif_type': 'ovs', + } + self.plugin._is_supported_by_fw_l2_driver = mock.Mock( + return_value=True) + with self.port(**port_args) as port1, self.port(**port_args) as port2: port1_id = port1['port']['id'] port2_id = port2['port']['id'] port_ids = [port1_id, port2_id] - project_id = ctx.tenant_id - self.plugin.set_port_for_default_firewall_group( - ctx, port1_id, project_id) - self.plugin.set_port_for_default_firewall_group( - ctx, port2_id, project_id) - def_fwg_db = self.plugin._get_default_fwg(ctx, project_id) - self.assertEqual('PENDING_UPDATE', def_fwg_db['status']) - self.assertEqual(sorted(port_ids), sorted(def_fwg_db['ports'])) - - def test_set_port_for_default_firewall_group_raised_port_in_use(self): - ctx = self._get_nonadmin_context() - self._build_default_fwg(ctx=ctx) - self.plugin.update_firewall_group_status = mock.Mock() - with self.port(project_id=ctx.tenant_id) as port1: - port1_id = port1['port']['id'] - port_ids = [port1_id] - self.plugin._set_ports_for_firewall_group = mock.Mock( - side_effect=f_exc.FirewallGroupPortInUse(port_ids=port_ids)) - project_id = ctx.tenant_id - - self.plugin.set_port_for_default_firewall_group( - ctx, port1_id, project_id) - self.plugin.update_firewall_group_status.assert_not_called() + self.plugin.update_firewall_group( + ctx, + default_fwg['id'], + {'firewall_group': {'ports': port_ids}}, + ) + default_fwg = self.plugin.get_firewall_group(ctx, + default_fwg['id']) + self.assertEqual(sorted(port_ids), sorted(default_fwg['ports'])) diff --git a/neutron_fwaas/tests/unit/services/firewall/agents/l2/__init__.py b/neutron_fwaas/tests/unit/services/firewall/service_drivers/__init__.py similarity index 100% rename from neutron_fwaas/tests/unit/services/firewall/agents/l2/__init__.py rename to neutron_fwaas/tests/unit/services/firewall/service_drivers/__init__.py diff --git a/neutron_fwaas/tests/unit/services/firewall/agents/l3reference/__init__.py b/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/__init__.py similarity index 100% rename from neutron_fwaas/tests/unit/services/firewall/agents/l3reference/__init__.py rename to neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/__init__.py diff --git a/neutron_fwaas/tests/unit/services/firewall/drivers/__init__.py b/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/__init__.py similarity index 100% rename from neutron_fwaas/tests/unit/services/firewall/drivers/__init__.py rename to neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/__init__.py diff --git a/neutron_fwaas/tests/unit/services/firewall/drivers/linux/__init__.py b/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/__init__.py similarity index 100% rename from neutron_fwaas/tests/unit/services/firewall/drivers/linux/__init__.py rename to neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/__init__.py diff --git a/neutron_fwaas/tests/unit/services/firewall/drivers/linux/l2/__init__.py b/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/l2/__init__.py similarity index 100% rename from neutron_fwaas/tests/unit/services/firewall/drivers/linux/l2/__init__.py rename to neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/l2/__init__.py diff --git a/neutron_fwaas/tests/unit/services/firewall/drivers/linux/l2/noop/__init__.py b/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/l2/noop/__init__.py similarity index 100% rename from neutron_fwaas/tests/unit/services/firewall/drivers/linux/l2/noop/__init__.py rename to neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/l2/noop/__init__.py diff --git a/neutron_fwaas/tests/unit/services/firewall/drivers/linux/l2/noop/test_noop_driver.py b/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/l2/noop/test_noop_driver.py similarity index 93% rename from neutron_fwaas/tests/unit/services/firewall/drivers/linux/l2/noop/test_noop_driver.py rename to neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/l2/noop/test_noop_driver.py index 2c23e40f7..fea3c29f3 100644 --- a/neutron_fwaas/tests/unit/services/firewall/drivers/linux/l2/noop/test_noop_driver.py +++ b/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/l2/noop/test_noop_driver.py @@ -16,7 +16,8 @@ import mock from neutron import manager -from neutron_fwaas.services.firewall.drivers.linux.l2.noop import noop_driver +from neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.l2.\ + noop import noop_driver from neutron_fwaas.tests import base diff --git a/neutron_fwaas/tests/unit/services/firewall/drivers/linux/l2/openvswitch_firewall/__init__.py b/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/l2/openvswitch_firewall/__init__.py similarity index 100% rename from neutron_fwaas/tests/unit/services/firewall/drivers/linux/l2/openvswitch_firewall/__init__.py rename to neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/l2/openvswitch_firewall/__init__.py diff --git a/neutron_fwaas/tests/unit/services/firewall/drivers/linux/l2/openvswitch_firewall/test_firewall.py b/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/l2/openvswitch_firewall/test_firewall.py similarity index 98% rename from neutron_fwaas/tests/unit/services/firewall/drivers/linux/l2/openvswitch_firewall/test_firewall.py rename to neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/l2/openvswitch_firewall/test_firewall.py index f4fe762ae..86d96b493 100644 --- a/neutron_fwaas/tests/unit/services/firewall/drivers/linux/l2/openvswitch_firewall/test_firewall.py +++ b/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/l2/openvswitch_firewall/test_firewall.py @@ -24,12 +24,12 @@ from neutron.plugins.ml2.drivers.openvswitch.agent import \ ovs_agent_extension_api as ovs_ext_api from neutron.tests import base -from neutron_fwaas.services.firewall.drivers.linux.l2.openvswitch_firewall \ - import constants as fwaas_ovs_consts -from neutron_fwaas.services.firewall.drivers.linux.l2.openvswitch_firewall \ - import exceptions -from neutron_fwaas.services.firewall.drivers.linux.l2.openvswitch_firewall \ - import firewall as ovsfw +from neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.l2.\ + openvswitch_firewall import constants as fwaas_ovs_consts +from neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.l2.\ + openvswitch_firewall import exceptions +from neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.l2.\ + openvswitch_firewall import firewall as ovsfw TESTING_VLAN_TAG = 1 diff --git a/neutron_fwaas/tests/unit/services/firewall/drivers/linux/l2/openvswitch_firewall/test_rules.py b/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/l2/openvswitch_firewall/test_rules.py similarity index 97% rename from neutron_fwaas/tests/unit/services/firewall/drivers/linux/l2/openvswitch_firewall/test_rules.py rename to neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/l2/openvswitch_firewall/test_rules.py index 7d36ef1b8..5c776827b 100644 --- a/neutron_fwaas/tests/unit/services/firewall/drivers/linux/l2/openvswitch_firewall/test_rules.py +++ b/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/l2/openvswitch_firewall/test_rules.py @@ -20,12 +20,12 @@ from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants \ as ovs_consts from neutron.tests import base -from neutron_fwaas.services.firewall.drivers.linux.l2.openvswitch_firewall \ - import constants as fwaas_ovs_consts -from neutron_fwaas.services.firewall.drivers.linux.l2.openvswitch_firewall \ - import firewall as ovsfw -from neutron_fwaas.services.firewall.drivers.linux.l2.openvswitch_firewall \ - import rules +from neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.l2.\ + openvswitch_firewall import constants as fwaas_ovs_consts +from neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.l2.\ + openvswitch_firewall import firewall as ovsfw +from neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.l2.\ + openvswitch_firewall import rules TESTING_VLAN_TAG = 1 diff --git a/neutron_fwaas/tests/unit/services/firewall/drivers/linux/test_iptables_fwaas.py b/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/test_iptables_fwaas.py similarity index 99% rename from neutron_fwaas/tests/unit/services/firewall/drivers/linux/test_iptables_fwaas.py rename to neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/test_iptables_fwaas.py index eef0264c3..3b5bb59a6 100644 --- a/neutron_fwaas/tests/unit/services/firewall/drivers/linux/test_iptables_fwaas.py +++ b/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/test_iptables_fwaas.py @@ -19,7 +19,8 @@ import mock from neutron.tests import base from neutron.tests.unit.api.v2 import test_base as test_api_v2 -import neutron_fwaas.services.firewall.drivers.linux.iptables_fwaas as fwaas +import neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.\ + iptables_fwaas as fwaas _uuid = test_api_v2._uuid diff --git a/neutron_fwaas/tests/unit/services/firewall/drivers/linux/test_iptables_fwaas_v2.py b/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/test_iptables_fwaas_v2.py similarity index 99% rename from neutron_fwaas/tests/unit/services/firewall/drivers/linux/test_iptables_fwaas_v2.py rename to neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/test_iptables_fwaas_v2.py index e3eb640e0..61caae69c 100644 --- a/neutron_fwaas/tests/unit/services/firewall/drivers/linux/test_iptables_fwaas_v2.py +++ b/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/test_iptables_fwaas_v2.py @@ -19,7 +19,8 @@ import mock from neutron.tests import base from neutron.tests.unit.api.v2 import test_base as test_api_v2 -import neutron_fwaas.services.firewall.drivers.linux.iptables_fwaas_v2 as fwaas +import neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.\ + iptables_fwaas_v2 as fwaas _uuid = test_api_v2._uuid diff --git a/neutron_fwaas/tests/unit/services/firewall/drivers/linux/test_legacy_conntrack.py b/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/test_legacy_conntrack.py similarity index 95% rename from neutron_fwaas/tests/unit/services/firewall/drivers/linux/test_legacy_conntrack.py rename to neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/test_legacy_conntrack.py index d23f8c305..a04f2935a 100644 --- a/neutron_fwaas/tests/unit/services/firewall/drivers/linux/test_legacy_conntrack.py +++ b/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/test_legacy_conntrack.py @@ -17,7 +17,8 @@ import mock import testtools from neutron.tests import base -from neutron_fwaas.services.firewall.drivers.linux import legacy_conntrack +from neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux \ + import legacy_conntrack from neutron_lib import constants @@ -128,8 +129,8 @@ class ConntrackLegacyTestCase(base.BaseTestCase): def test_delete_entries(self): list_entries_mock = mock.patch( - 'neutron_fwaas.services.firewall.drivers.linux' - '.legacy_conntrack.ConntrackLegacy.list_entries') + 'neutron_fwaas.services.firewall.service_drivers.agents.drivers.' + 'linux.legacy_conntrack.ConntrackLegacy.list_entries') self.list_entries = list_entries_mock.start() self.conntrack_driver.list_entries.return_value = [ diff --git a/neutron_fwaas/tests/unit/services/firewall/drivers/linux/test_netlink_conntrack.py b/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/test_netlink_conntrack.py similarity index 98% rename from neutron_fwaas/tests/unit/services/firewall/drivers/linux/test_netlink_conntrack.py rename to neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/test_netlink_conntrack.py index 33258690e..0637459df 100644 --- a/neutron_fwaas/tests/unit/services/firewall/drivers/linux/test_netlink_conntrack.py +++ b/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/test_netlink_conntrack.py @@ -15,7 +15,8 @@ import mock -from neutron_fwaas.services.firewall.drivers.linux import netlink_conntrack +from neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux \ + import netlink_conntrack from neutron_fwaas.tests import base FW_RULES = [ diff --git a/neutron_fwaas/tests/unit/services/firewall/plugins/__init__.py b/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/l2/__init__.py similarity index 100% rename from neutron_fwaas/tests/unit/services/firewall/plugins/__init__.py rename to neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/l2/__init__.py diff --git a/neutron_fwaas/tests/unit/services/firewall/agents/l2/fake_data.py b/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/l2/fake_data.py similarity index 100% rename from neutron_fwaas/tests/unit/services/firewall/agents/l2/fake_data.py rename to neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/l2/fake_data.py diff --git a/neutron_fwaas/tests/unit/services/firewall/agents/l2/test_fwaas_v2.py b/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/l2/test_fwaas_v2.py similarity index 99% rename from neutron_fwaas/tests/unit/services/firewall/agents/l2/test_fwaas_v2.py rename to neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/l2/test_fwaas_v2.py index cc1c5c20d..f3e850a18 100644 --- a/neutron_fwaas/tests/unit/services/firewall/agents/l2/test_fwaas_v2.py +++ b/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/l2/test_fwaas_v2.py @@ -21,9 +21,10 @@ from neutron_lib.exceptions import firewall_v2 as f_exc from oslo_config import cfg from neutron_fwaas.common import fwaas_constants as consts -from neutron_fwaas.services.firewall.agents.l2 import fwaas_v2 +from neutron_fwaas.services.firewall.service_drivers.agents.l2 import fwaas_v2 from neutron_fwaas.tests import base -from neutron_fwaas.tests.unit.services.firewall.agents.l2 import fake_data +from neutron_fwaas.tests.unit.services.firewall.service_drivers.agents.l2\ + import fake_data class TestFWaasV2AgentExtensionBase(base.BaseTestCase): diff --git a/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/l3reference/__init__.py b/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/l3reference/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutron_fwaas/tests/unit/services/firewall/agents/l3reference/test_firewall_l3_agent.py b/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/l3reference/test_firewall_l3_agent.py similarity index 98% rename from neutron_fwaas/tests/unit/services/firewall/agents/l3reference/test_firewall_l3_agent.py rename to neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/l3reference/test_firewall_l3_agent.py index 9c68aac10..5e29d6aa8 100644 --- a/neutron_fwaas/tests/unit/services/firewall/agents/l3reference/test_firewall_l3_agent.py +++ b/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/l3reference/test_firewall_l3_agent.py @@ -24,11 +24,12 @@ from oslo_utils import uuidutils import testtools from neutron_fwaas.common import fwaas_constants -from neutron_fwaas.services.firewall.agents import firewall_agent_api -from neutron_fwaas.services.firewall.agents.l3reference \ +from neutron_fwaas.services.firewall.service_drivers.agents \ + import firewall_agent_api +from neutron_fwaas.services.firewall.service_drivers.agents.l3reference \ import firewall_l3_agent from neutron_fwaas.tests import base -from neutron_fwaas.tests.unit.services.firewall.agents \ +from neutron_fwaas.tests.unit.services.firewall.service_drivers.agents \ import test_firewall_agent_api diff --git a/neutron_fwaas/tests/unit/services/firewall/agents/l3reference/test_firewall_l3_agent_v2.py b/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/l3reference/test_firewall_l3_agent_v2.py similarity index 98% rename from neutron_fwaas/tests/unit/services/firewall/agents/l3reference/test_firewall_l3_agent_v2.py rename to neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/l3reference/test_firewall_l3_agent_v2.py index 58f980389..c1b7646e1 100644 --- a/neutron_fwaas/tests/unit/services/firewall/agents/l3reference/test_firewall_l3_agent_v2.py +++ b/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/l3reference/test_firewall_l3_agent_v2.py @@ -23,13 +23,14 @@ from oslo_config import cfg from oslo_utils import uuidutils from neutron_fwaas.common import fwaas_constants -from neutron_fwaas.services.firewall.agents import firewall_agent_api -from neutron_fwaas.services.firewall.agents.l3reference \ - import firewall_l3_agent_v2 -from neutron_fwaas.services.firewall.drivers.linux \ +from neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux \ import iptables_fwaas_v2 +from neutron_fwaas.services.firewall.service_drivers.agents \ + import firewall_agent_api +from neutron_fwaas.services.firewall.service_drivers.agents.l3reference \ + import firewall_l3_agent_v2 from neutron_fwaas.tests import base -from neutron_fwaas.tests.unit.services.firewall.agents \ +from neutron_fwaas.tests.unit.services.firewall.service_drivers.agents \ import test_firewall_agent_api diff --git a/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/test_agents.py b/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/test_agents.py new file mode 100644 index 000000000..b24e6338f --- /dev/null +++ b/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/test_agents.py @@ -0,0 +1,654 @@ +# Copyright 2016 +# 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. + +import mock +import six + +from neutron import extensions as neutron_extensions +from neutron.tests.unit.extensions import test_l3 +from neutron_lib import constants as nl_constants +from neutron_lib import context +from neutron_lib.exceptions import firewall_v2 as f_exc +from neutron_lib.plugins import directory +from oslo_config import cfg + +from neutron_fwaas._i18n import _ +from neutron_fwaas.db.firewall.v2.firewall_db_v2 import FirewallGroup +from neutron_fwaas.services.firewall.service_drivers.agents import agents +from neutron_fwaas.tests import base +from neutron_fwaas.tests.unit.services.firewall import test_fwaas_plugin_v2 + + +FIREWALL_AGENT_PLUGIN = ('neutron_fwaas.services.firewall.service_drivers.' + 'agents.agents') +FIREWALL_AGENT_PLUGIN_KLASS = FIREWALL_AGENT_PLUGIN + '.FirewallAgentDriver' +DELETEFW_PATH = (FIREWALL_AGENT_PLUGIN + '.FirewallAgentApi.' + 'delete_firewall_group') + + +class FakeAgentApi(agents.FirewallAgentCallbacks): + """ + This class used to mock the AgentAPI delete method inherits from + FirewallCallbacks because it needs access to the firewall_deleted method. + The delete_firewall method belongs to the FirewallAgentApi, which has + no access to the firewall_deleted method normally because it's not + responsible for deleting the firewall from the DB. However, it needs + to in the unit tests since there is no agent to call back. + """ + def __init__(self): + return + + def delete_firewall_group(self, context, firewall_group, **kwargs): + self.plugin = directory.get_plugin('FIREWALL_V2') + self.firewall_db = self.plugin.driver.firewall_db + self.firewall_group_deleted(context, firewall_group['id'], **kwargs) + + +class TestFirewallAgentApi(base.BaseTestCase): + def setUp(self): + super(TestFirewallAgentApi, self).setUp() + + self.api = agents.FirewallAgentApi('topic', 'host') + + def test_init(self): + self.assertEqual('topic', self.api.client.target.topic) + self.assertEqual('host', self.api.host) + + def _call_test_helper(self, method_name): + with mock.patch.object(self.api.client, 'cast') as rpc_mock, \ + mock.patch.object(self.api.client, 'prepare') as prepare_mock: + prepare_mock.return_value = self.api.client + getattr(self.api, method_name)(mock.sentinel.context, 'test') + + prepare_args = {'fanout': True} + prepare_mock.assert_called_once_with(**prepare_args) + + rpc_mock.assert_called_once_with(mock.sentinel.context, method_name, + firewall_group='test', host='host') + + def test_create_firewall_group(self): + self._call_test_helper('create_firewall_group') + + def test_update_firewall_group(self): + self._call_test_helper('update_firewall_group') + + def test_delete_firewall_group(self): + self._call_test_helper('delete_firewall_group') + + +class TestAgentDriver(test_fwaas_plugin_v2.FirewallPluginV2TestCase, + test_l3.L3NatTestCaseMixin): + + def setUp(self): + self.agentapi_del_fw_p = mock.patch( + DELETEFW_PATH, create=True, + new=FakeAgentApi().delete_firewall_group, + ).start() + mock.patch.object(agents.neutron_rpc, 'get_client').start() + mock.patch.object(agents.neutron_rpc, 'Connection').start() + + l3_plugin_str = ('neutron.tests.unit.extensions.test_l3.' + 'TestL3NatServicePlugin') + l3_plugin = {'l3_plugin_name': l3_plugin_str} + super(TestAgentDriver, self).setUp( + service_provider=FIREWALL_AGENT_PLUGIN_KLASS, + extra_service_plugins=l3_plugin, + extra_extension_paths=neutron_extensions.__path__) + + self.callbacks = self.plugin.driver.endpoints[0] + self.db = self.plugin.driver.firewall_db + + router_distributed_opts = [ + cfg.BoolOpt( + 'router_distributed', + default=False, + help=_("System-wide flag to determine the type of router " + "that tenants can create. Only admin can override.")), + ] + cfg.CONF.register_opts(router_distributed_opts) + + @property + def _self_context(self): + return context.Context('', self._tenant_id) + + def _get_test_firewall_group_attrs(self, name, + status=nl_constants.INACTIVE): + return super(TestAgentDriver, self)._get_test_firewall_group_attrs( + name, status=status) + + def test_set_firewall_group_status(self): + ctx = context.get_admin_context() + with self.firewall_policy() as fwp: + fwp_id = fwp['firewall_policy']['id'] + with self.firewall_group( + ingress_firewall_policy_id=fwp_id, + admin_state_up=self.ADMIN_STATE_UP + ) as fwg: + fwg_id = fwg['firewall_group']['id'] + res = self.callbacks.set_firewall_group_status(ctx, fwg_id, + nl_constants.ACTIVE) + fwg_db = self.plugin.get_firewall_group(ctx, fwg_id) + self.assertEqual(nl_constants.ACTIVE, fwg_db['status']) + self.assertTrue(res) + res = self.callbacks.set_firewall_group_status(ctx, fwg_id, + nl_constants.ERROR) + fwg_db = self.plugin.get_firewall_group(ctx, fwg_id) + self.assertEqual(nl_constants.ERROR, fwg_db['status']) + self.assertFalse(res) + + def test_firewall_group_deleted(self): + ctx = context.get_admin_context() + with self.firewall_policy() as fwp: + fwp_id = fwp['firewall_policy']['id'] + with self.firewall_group( + ingress_firewall_policy_id=fwp_id, + admin_state_up=self.ADMIN_STATE_UP, + do_delete=False + ) as fwg: + fwg_id = fwg['firewall_group']['id'] + with ctx.session.begin(subtransactions=True): + fwg_db = self.db._get_firewall_group(ctx, fwg_id) + fwg_db['status'] = nl_constants.PENDING_DELETE + + observed = self.callbacks.firewall_group_deleted(ctx, fwg_id) + self.assertTrue(observed) + + self.assertRaises(f_exc.FirewallGroupNotFound, + self.plugin.get_firewall_group, + ctx, fwg_id) + + def test_firewall_group_deleted_concurrently(self): + ctx = context.get_admin_context() + alt_ctx = context.get_admin_context() + + _get_firewall_group = self.db._get_firewall_group + + def getdelete(context, fwg_id): + fwg_db = _get_firewall_group(context, fwg_id) + # NOTE(cby): Use a different session to simulate a concurrent del + with alt_ctx.session.begin(subtransactions=True): + alt_ctx.session.query(FirewallGroup).filter_by( + id=fwg_id).delete() + return fwg_db + + with self.firewall_policy() as fwp: + fwp_id = fwp['firewall_policy']['id'] + with self.firewall_group( + firewall_policy_id=fwp_id, + admin_state_up=self.ADMIN_STATE_UP, + do_delete=False + ) as fwg: + fwg_id = fwg['firewall_group']['id'] + with ctx.session.begin(subtransactions=True): + fwg_db = self.db._get_firewall_group(ctx, fwg_id) + fwg_db['status'] = nl_constants.PENDING_DELETE + ctx.session.flush() + + with mock.patch.object( + self.db, '_get_firewall_group', side_effect=getdelete + ): + observed = self.callbacks.firewall_group_deleted( + ctx, fwg_id) + self.assertTrue(observed) + + self.assertRaises(f_exc.FirewallGroupNotFound, + self.plugin.get_firewall_group, + ctx, fwg_id) + + def test_firewall_group_deleted_not_found(self): + ctx = context.get_admin_context() + observed = self.callbacks.firewall_group_deleted( + ctx, 'notfound') + self.assertTrue(observed) + + def test_firewall_group_deleted_error(self): + ctx = context.get_admin_context() + with self.firewall_policy() as fwp: + fwp_id = fwp['firewall_policy']['id'] + with self.firewall_group( + firewall_policy_id=fwp_id, + admin_state_up=self.ADMIN_STATE_UP, + ) as fwg: + fwg_id = fwg['firewall_group']['id'] + observed = self.callbacks.firewall_group_deleted( + ctx, fwg_id) + self.assertFalse(observed) + fwg_db = self.db._get_firewall_group(ctx, fwg_id) + self.assertEqual(nl_constants.ERROR, fwg_db['status']) + + def test_create_firewall_group_ports_not_specified(self): + """neutron firewall-create test-policy """ + with self.firewall_policy() as fwp: + fwp_id = fwp['firewall_policy']['id'] + with self.firewall_group( + name='test', + ingress_firewall_policy_id=fwp_id, + egress_firewall_policy_id=fwp_id, + admin_state_up=True) as fwg1: + self.assertEqual(nl_constants.INACTIVE, + fwg1['firewall_group']['status']) + + def test_create_firewall_group_with_ports(self): + """neutron firewall_group create test-policy """ + with self.router(name='router1', admin_state_up=True, + tenant_id=self._tenant_id) as r, \ + self.subnet() as s1, \ + self.subnet(cidr='20.0.0.0/24') as s2: + + body = self._router_interface_action( + 'add', + r['router']['id'], + s1['subnet']['id'], + None) + port_id1 = body['port_id'] + + body = self._router_interface_action( + 'add', + r['router']['id'], + s2['subnet']['id'], + None) + port_id2 = body['port_id'] + fwg_ports = [port_id1, port_id2] + with self.firewall_policy() as fwp: + fwp_id = fwp['firewall_policy']['id'] + with self.firewall_group( + name='test', + ingress_firewall_policy_id=fwp_id, + egress_firewall_policy_id=fwp_id, ports=fwg_ports, + admin_state_up=True) as fwg1: + self.assertEqual(nl_constants.PENDING_CREATE, + fwg1['firewall_group']['status']) + + def test_create_firewall_group_with_ports_on_diff_routers(self): + """neutron firewall_group create test-policy """ + with self.router(name='router1', admin_state_up=True, + tenant_id=self._tenant_id) as r, \ + self.subnet() as s1, \ + self.subnet(cidr='20.0.0.0/24') as s2: + body = self._router_interface_action( + 'add', + r['router']['id'], + s1['subnet']['id'], + None) + port_id1 = body['port_id'] + body = self._router_interface_action( + 'add', + r['router']['id'], + s2['subnet']['id'], + None) + port_id2 = body['port_id'] + + with self.router(name='router1', admin_state_up=True, + tenant_id=self._tenant_id) as r2, \ + self.subnet() as s3: + + body = self._router_interface_action( + 'add', + r2['router']['id'], + s3['subnet']['id'], + None) + port_id3 = body['port_id'] + + fwg_ports = [port_id1, port_id2, port_id3] + with self.firewall_policy() as fwp: + fwp_id = fwp['firewall_policy']['id'] + with self.firewall_group( + name='test', + ingress_firewall_policy_id=fwp_id, + egress_firewall_policy_id=fwp_id, + ports=fwg_ports, + admin_state_up=True) as fwg1: + self.assertEqual(nl_constants.PENDING_CREATE, + fwg1['firewall_group']['status']) + + def test_create_firewall_group_with_ports_no_policy(self): + """neutron firewall_group create test-policy """ + with self.router(name='router1', admin_state_up=True, + tenant_id=self._tenant_id) as r, \ + self.subnet() as s1, \ + self.subnet(cidr='20.0.0.0/24') as s2: + + body = self._router_interface_action( + 'add', + r['router']['id'], + s1['subnet']['id'], + None) + port_id1 = body['port_id'] + + body = self._router_interface_action( + 'add', + r['router']['id'], + s2['subnet']['id'], + None) + port_id2 = body['port_id'] + fwg_ports = [port_id1, port_id2] + with self.firewall_group( + name='test', + default_policy=False, + ports=fwg_ports, + admin_state_up=True) as fwg1: + self.assertEqual(nl_constants.INACTIVE, + fwg1['firewall_group']['status']) + + def test_update_firewall_group_with_new_ports_no_policy(self): + """neutron firewall_group create test-policy """ + with self.router(name='router1', admin_state_up=True, + tenant_id=self._tenant_id) as r, \ + self.subnet() as s1, \ + self.subnet(cidr='20.0.0.0/24') as s2, \ + self.subnet(cidr='30.0.0.0/24') as s3: + + body = self._router_interface_action( + 'add', + r['router']['id'], + s1['subnet']['id'], + None) + port_id1 = body['port_id'] + + body = self._router_interface_action( + 'add', + r['router']['id'], + s2['subnet']['id'], + None) + port_id2 = body['port_id'] + + body = self._router_interface_action( + 'add', + r['router']['id'], + s3['subnet']['id'], + None) + port_id3 = body['port_id'] + + fwg_ports = [port_id1, port_id2] + with self.firewall_group( + name='test', + default_policy=False, + ports=fwg_ports, + admin_state_up=True) as fwg1: + self.assertEqual(nl_constants.INACTIVE, + fwg1['firewall_group']['status']) + data = {'firewall_group': {'ports': [port_id2, port_id3]}} + req = self.new_update_request('firewall_groups', data, + fwg1['firewall_group']['id'], + context=self._self_context) + res = self.deserialize(self.fmt, + req.get_response(self.ext_api)) + + self.assertEqual(sorted([port_id2, port_id3]), + sorted(res['firewall_group']['ports'])) + + self.assertEqual(nl_constants.INACTIVE, + res['firewall_group']['status']) + + def test_update_firewall_group_with_new_ports_status_pending(self): + """neutron firewall_group create test-policy """ + with self.router(name='router1', admin_state_up=True, + tenant_id=self._tenant_id) as r, \ + self.subnet() as s1, \ + self.subnet(cidr='20.0.0.0/24') as s2, \ + self.subnet(cidr='30.0.0.0/24') as s3: + + body = self._router_interface_action( + 'add', + r['router']['id'], + s1['subnet']['id'], + None) + port_id1 = body['port_id'] + + body = self._router_interface_action( + 'add', + r['router']['id'], + s2['subnet']['id'], + None) + port_id2 = body['port_id'] + fwg_ports = [port_id1, port_id2] + + body = self._router_interface_action( + 'add', + r['router']['id'], + s3['subnet']['id'], + None) + port_id3 = body['port_id'] + + with self.firewall_policy() as fwp: + fwp_id = fwp['firewall_policy']['id'] + with self.firewall_group( + name='test', + ingress_firewall_policy_id=fwp_id, + egress_firewall_policy_id=fwp_id, ports=fwg_ports, + admin_state_up=True) as fwg1: + self.assertEqual(nl_constants.PENDING_CREATE, + fwg1['firewall_group']['status']) + data = {'firewall_group': {'ports': [port_id2, port_id3]}} + req = self.new_update_request('firewall_groups', data, + fwg1['firewall_group']['id']) + res = req.get_response(self.ext_api) + self.assertEqual(409, res.status_int) + + def test_update_firewall_group_with_new_ports_status_active(self): + """neutron firewall_group create test-policy """ + with self.router(name='router1', admin_state_up=True, + tenant_id=self._tenant_id) as r, \ + self.subnet() as s1, \ + self.subnet(cidr='20.0.0.0/24') as s2, \ + self.subnet(cidr='30.0.0.0/24') as s3: + + body = self._router_interface_action( + 'add', + r['router']['id'], + s1['subnet']['id'], + None) + port_id1 = body['port_id'] + + body = self._router_interface_action( + 'add', + r['router']['id'], + s2['subnet']['id'], + None) + port_id2 = body['port_id'] + fwg_ports = [port_id1, port_id2] + + body = self._router_interface_action( + 'add', + r['router']['id'], + s3['subnet']['id'], + None) + port_id3 = body['port_id'] + + with self.firewall_policy() as fwp: + fwp_id = fwp['firewall_policy']['id'] + with self.firewall_group( + name='test', + ingress_firewall_policy_id=fwp_id, + egress_firewall_policy_id=fwp_id, ports=fwg_ports, + admin_state_up=True) as fwg1: + self.assertEqual(nl_constants.PENDING_CREATE, + fwg1['firewall_group']['status']) + + ctx = context.get_admin_context() + self.callbacks.set_firewall_group_status(ctx, + fwg1['firewall_group']['id'], nl_constants.ACTIVE) + data = {'firewall_group': {'ports': [port_id2, port_id3]}} + req = self.new_update_request('firewall_groups', data, + fwg1['firewall_group']['id'], + context=self._self_context) + res = self.deserialize(self.fmt, + req.get_response(self.ext_api)) + self.assertEqual(sorted([port_id2, port_id3]), + sorted(res['firewall_group']['ports'])) + + def test_update_firewall_rule_on_active_fwg(self): + name = "new_firewall_rule1" + attrs = self._get_test_firewall_rule_attrs(name) + with self.router(name='router1', admin_state_up=True, + tenant_id=self._tenant_id) as r, \ + self.subnet() as s1: + + body = self._router_interface_action( + 'add', + r['router']['id'], + s1['subnet']['id'], + None) + port_id1 = body['port_id'] + with self.firewall_rule() as fwr: + with self.firewall_policy( + firewall_rules=[fwr['firewall_rule']['id']]) as fwp: + fwp_id = fwp['firewall_policy']['id'] + with self.firewall_group( + name='test', + ingress_firewall_policy_id=fwp_id, + egress_firewall_policy_id=fwp_id, ports=[port_id1], + admin_state_up=True) as fwg1: + self.assertEqual(nl_constants.PENDING_CREATE, + fwg1['firewall_group']['status']) + + ctx = context.get_admin_context() + self.callbacks.set_firewall_group_status(ctx, + fwg1['firewall_group']['id'], nl_constants.ACTIVE) + data = {'firewall_rule': {'name': name}} + req = self.new_update_request('firewall_rules', data, + fwr['firewall_rule']['id']) + res = self.deserialize(self.fmt, + req.get_response(self.ext_api)) + for k, v in six.iteritems(attrs): + self.assertEqual(v, res['firewall_rule'][k]) + + def test_update_firewall_rule_on_pending_create_fwg(self): + """update should fail""" + name = "new_firewall_rule1" + with self.router(name='router1', admin_state_up=True, + tenant_id=self._tenant_id) as r, \ + self.subnet() as s1: + + body = self._router_interface_action( + 'add', + r['router']['id'], + s1['subnet']['id'], + None) + port_id1 = body['port_id'] + with self.firewall_rule() as fwr: + with self.firewall_policy( + firewall_rules=[fwr['firewall_rule']['id']]) as fwp: + fwp_id = fwp['firewall_policy']['id'] + with self.firewall_group( + name='test', + ingress_firewall_policy_id=fwp_id, + egress_firewall_policy_id=fwp_id, ports=[port_id1], + admin_state_up=True) as fwg1: + self.assertEqual(nl_constants.PENDING_CREATE, + fwg1['firewall_group']['status']) + + data = {'firewall_rule': {'name': name}} + req = self.new_update_request('firewall_rules', data, + fwr['firewall_rule']['id']) + res = req.get_response(self.ext_api) + self.assertEqual(409, res.status_int) + + def test_update_firewall_group_with_non_exist_ports(self): + """neutron firewall_group create test-policy """ + with self.router(name='router1', admin_state_up=True, + tenant_id=self._tenant_id) as r, \ + self.subnet(cidr='30.0.0.0/24') as s: + body = self._router_interface_action( + 'add', + r['router']['id'], + s['subnet']['id'], + None) + port_id1 = body['port_id'] + foo_port_id = 'caef152d-b118-4b9b-bc77-800661bf082d' + fwg_ports = [port_id1] + with self.firewall_group( + name='test', + default_policy=False, + ports=fwg_ports, + admin_state_up=True) as fwg1: + self.assertEqual(nl_constants.INACTIVE, + fwg1['firewall_group']['status']) + data = {'firewall_group': {'ports': [foo_port_id]}} + req = self.new_update_request('firewall_groups', data, + fwg1['firewall_group']['id']) + res = self.deserialize(self.fmt, + req.get_response(self.ext_api)) + self.assertEqual('PortNotFound', + res['NeutronError']['type']) + + def test_update_firewall_group_with_ports_and_policy(self): + """neutron firewall_group create test-policy """ + with self.router(name='router1', admin_state_up=True, + tenant_id=self._tenant_id) as r,\ + self.subnet() as s1,\ + self.subnet(cidr='20.0.0.0/24') as s2: + + body = self._router_interface_action( + 'add', + r['router']['id'], + s1['subnet']['id'], + None) + port_id1 = body['port_id'] + + body = self._router_interface_action( + 'add', + r['router']['id'], + s2['subnet']['id'], + None) + port_id2 = body['port_id'] + + fwg_ports = [port_id1, port_id2] + with self.firewall_rule() as fwr: + with self.firewall_policy( + firewall_rules=[fwr['firewall_rule']['id']]) as fwp: + with self.firewall_group( + name='test', + default_policy=False, + ports=fwg_ports, + admin_state_up=True) as fwg1: + self.assertEqual(nl_constants.INACTIVE, + fwg1['firewall_group']['status']) + fwp_id = fwp["firewall_policy"]["id"] + + data = {'firewall_group': {'ports': fwg_ports}} + req = (self. + new_update_request('firewall_groups', data, + fwg1['firewall_group']['id'], + context=self._self_context)) + res = self.deserialize(self.fmt, + req.get_response(self.ext_api)) + self.assertEqual(nl_constants.INACTIVE, + res['firewall_group']['status']) + + data = {'firewall_group': { + 'ingress_firewall_policy_id': fwp_id}} + req = (self. + new_update_request('firewall_groups', data, + fwg1['firewall_group']['id'], + context=self._self_context)) + res = self.deserialize(self.fmt, + req.get_response(self.ext_api)) + self.assertEqual(nl_constants.PENDING_UPDATE, + res['firewall_group']['status']) + + def test_create_firewall_group_with_dvr(self): + cfg.CONF.set_override('router_distributed', True) + attrs = self._get_test_firewall_group_attrs("firewall1") + self._test_create_firewall_group(attrs) + + def test_create_firewall_group(self): + attrs = self._get_test_firewall_group_attrs("firewall1") + self._test_create_firewall_group(attrs) + + def test_create_firewall_group_with_empty_ports(self): + attrs = self._get_test_firewall_group_attrs("fwg1") + attrs['ports'] = [] + self._test_create_firewall_group(attrs) diff --git a/neutron_fwaas/tests/unit/services/firewall/agents/test_firewall_agent_api.py b/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/test_firewall_agent_api.py similarity index 91% rename from neutron_fwaas/tests/unit/services/firewall/agents/test_firewall_agent_api.py rename to neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/test_firewall_agent_api.py index b82f48c15..3115b09ab 100644 --- a/neutron_fwaas/tests/unit/services/firewall/agents/test_firewall_agent_api.py +++ b/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/test_firewall_agent_api.py @@ -15,9 +15,12 @@ import mock -from neutron_fwaas.services.firewall.agents import firewall_agent_api as api -from neutron_fwaas.services.firewall.drivers import fwaas_base -from neutron_fwaas.services.firewall.drivers import fwaas_base_v2 +from neutron_fwaas.services.firewall.service_drivers.agents.drivers \ + import fwaas_base +from neutron_fwaas.services.firewall.service_drivers.agents.drivers \ + import fwaas_base_v2 +from neutron_fwaas.services.firewall.service_drivers.agents \ + import firewall_agent_api as api from neutron_fwaas.tests import base diff --git a/neutron_fwaas/tests/unit/services/firewall/agents/test_firewall_service.py b/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/test_firewall_service.py similarity index 91% rename from neutron_fwaas/tests/unit/services/firewall/agents/test_firewall_service.py rename to neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/test_firewall_service.py index d3dc83f0e..950cd4ed1 100644 --- a/neutron_fwaas/tests/unit/services/firewall/agents/test_firewall_service.py +++ b/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/test_firewall_service.py @@ -16,10 +16,12 @@ from neutron.tests import base from oslo_config import cfg -from neutron_fwaas.services.firewall.agents import firewall_service +from neutron_fwaas.services.firewall.service_drivers.agents import\ + firewall_service -FWAAS_NOP_DEVICE = ('neutron_fwaas.tests.unit.services.firewall.agents.' - 'test_firewall_agent_api.NoopFwaasDriver') +FWAAS_NOP_DEVICE = ('neutron_fwaas.tests.unit.services.firewall.' + 'service_drivers.agents.test_firewall_agent_api.' + 'NoopFwaasDriver') class TestFirewallDeviceDriverLoading(base.BaseTestCase): diff --git a/neutron_fwaas/tests/unit/services/firewall/test_fwaas_plugin_v2.py b/neutron_fwaas/tests/unit/services/firewall/test_fwaas_plugin_v2.py index 3431dda98..2bde6541d 100644 --- a/neutron_fwaas/tests/unit/services/firewall/test_fwaas_plugin_v2.py +++ b/neutron_fwaas/tests/unit/services/firewall/test_fwaas_plugin_v2.py @@ -13,798 +13,618 @@ # License for the specific language governing permissions and limitations # under the License. +import contextlib + import mock -from neutron.tests import fake_notifier -from neutron.tests.unit.extensions import test_l3 as test_l3_plugin +import six +import webob.exc + +from neutron.api import extensions as api_ext +from neutron.db import servicetype_db as sdb +from neutron_lib.api.definitions import firewall_v2 from neutron_lib import constants as nl_constants from neutron_lib import context from neutron_lib.exceptions import firewall_v2 as f_exc -from neutron_lib.plugins import constants as plugin_constants from neutron_lib.plugins import directory -from oslo_config import cfg -import six +from oslo_utils import importutils -from neutron_fwaas.db.firewall.v2 import firewall_db_v2 -import neutron_fwaas.extensions -from neutron_fwaas.extensions import firewall_v2 +from neutron_fwaas.common import fwaas_constants +from neutron_fwaas import extensions from neutron_fwaas.services.firewall import fwaas_plugin_v2 from neutron_fwaas.tests import base -from neutron_fwaas.tests.unit.db.firewall.v2 import ( - test_firewall_db_v2 as test_db_firewall) -extensions_path = neutron_fwaas.extensions.__path__[0] - -FW_PLUGIN_KLASS = ( - "neutron_fwaas.services.firewall.fwaas_plugin_v2.FirewallPluginV2" -) +def http_client_error(req, res): + explanation = "Request '%s %s %s' failed: %s" % (req.method, req.url, + req.body, res.body) + return webob.exc.HTTPClientError(code=res.status_int, + explanation=explanation) -class FirewallTestExtensionManager(test_l3_plugin.L3TestExtensionManager): +class FirewallPluginV2TestCase(base.NeutronDbPluginV2TestCase): + DESCRIPTION = 'default description' + PROTOCOL = 'tcp' + IP_VERSION = 4 + SOURCE_IP_ADDRESS_RAW = '1.1.1.1' + DESTINATION_IP_ADDRESS_RAW = '2.2.2.2' + SOURCE_PORT = '55000:56000' + DESTINATION_PORT = '56000:57000' + ACTION = 'allow' + AUDITED = True + ENABLED = True + ADMIN_STATE_UP = True + SHARED = True - def get_resources(self): - res = super(FirewallTestExtensionManager, self).get_resources() - res = res + firewall_v2.Firewall_v2.get_resources() + resource_prefix_map = dict( + (k, firewall_v2.API_PREFIX) + for k in firewall_v2.RESOURCE_ATTRIBUTE_MAP.keys() + ) + + def setUp(self, service_provider=None, core_plugin=None, + extra_service_plugins=None, extra_extension_paths=None): + provider = fwaas_constants.FIREWALL_V2 + if not service_provider: + provider += (':dummy:neutron_fwaas.services.firewall.' + 'service_drivers.driver_api.FirewallDriverDB:default') + else: + provider += ':test:' + service_provider + ':default' + + bits = provider.split(':') + provider = { + 'service_type': bits[0], + 'name': bits[1], + 'driver': bits[2], + 'default': True, + } + # override the default service provider + self.service_providers = ( + mock.patch.object(sdb.ServiceTypeManager, + 'get_service_providers').start()) + self.service_providers.return_value = [provider] + + plugin_str = ('neutron_fwaas.services.firewall.fwaas_plugin_v2.' + 'FirewallPluginV2') + service_plugins = {'fw_plugin_name': plugin_str} + service_plugins.update(extra_service_plugins or {}) + + # we need to provide a plugin instance, although the extension manager + # will create a new instance of the plugin + plugins = { + fwaas_constants.FIREWALL_V2: fwaas_plugin_v2.FirewallPluginV2(), + } + for plugin_name, plugin_str in (extra_service_plugins or {}).items(): + plugins[plugin_name] = importutils.import_object(plugin_str) + ext_mgr = api_ext.PluginAwareExtensionManager( + ':'.join(extensions.__path__ + (extra_extension_paths or [])), + plugins, + ) + + super(FirewallPluginV2TestCase, self).setUp( + plugin=core_plugin, + service_plugins=service_plugins, + ext_mgr=ext_mgr, + ) + + # find the Firewall plugin that was instantiated by the extension + # manager + self.plugin = directory.get_plugin(fwaas_constants.FIREWALL_V2) + + def _get_admin_context(self): + # FIXME NOTE(ivasilevskaya) seems that test framework treats context + # with user_id=None/tenant_id=None (return value of + # context._get_admin_context() method) in a somewhat special way. + # So as a workaround to have the framework behave properly right now + # let's implement our own _get_admin_context method and look into the + # matter some other time. + return context.Context(user_id='admin', + tenant_id='admin-tenant', + is_admin=True) + + def _get_nonadmin_context(self, user_id='non-admin', tenant_id='tenant1'): + return context.Context(user_id=user_id, tenant_id=tenant_id) + + def _test_list_resources(self, resource, items, + neutron_context=None, + query_params=None): + if resource.endswith('y'): + resource_plural = resource.replace('y', 'ies') + else: + resource_plural = resource + 's' + + res = self._list(resource_plural, + neutron_context=neutron_context, + query_params=query_params) + resource = resource.replace('-', '_') + self.assertEqual( + sorted([i[resource]['id'] for i in items]), + sorted([i['id'] for i in res[resource_plural]])) + + def _list_req(self, resource_plural, ctx=None): + if not ctx: + ctx = self._get_admin_context() + req = self.new_list_request(resource_plural) + req.environ['neutron.context'] = ctx + return self.deserialize( + self.fmt, req.get_response(self.ext_api))[resource_plural] + + def _show_req(self, resource_plural, obj_id, ctx=None): + req = self.new_show_request(resource_plural, obj_id, fmt=self.fmt) + if not ctx: + ctx = self._get_admin_context() + req.environ['neutron.context'] = ctx + res = self.deserialize( + self.fmt, req.get_response(self.ext_api)) return res - def get_actions(self): - return [] + def _build_default_fwg(self, ctx=None, is_one=True): + res = self._list_req('firewall_groups', ctx=ctx) + if is_one: + self.assertEqual(1, len(res)) + return res[0] + return res - def get_request_extensions(self): - return [] + def _get_test_firewall_rule_attrs(self, name='firewall_rule1'): + attrs = {'name': name, + 'tenant_id': self._tenant_id, + 'project_id': self._tenant_id, + 'protocol': self.PROTOCOL, + 'ip_version': self.IP_VERSION, + 'source_ip_address': self.SOURCE_IP_ADDRESS_RAW, + 'destination_ip_address': self.DESTINATION_IP_ADDRESS_RAW, + 'source_port': self.SOURCE_PORT, + 'destination_port': self.DESTINATION_PORT, + 'action': self.ACTION, + 'enabled': self.ENABLED, + 'shared': self.SHARED} + return attrs + def _get_test_firewall_policy_attrs(self, name='firewall_policy1', + audited=AUDITED): + attrs = {'name': name, + 'description': self.DESCRIPTION, + 'tenant_id': self._tenant_id, + 'project_id': self._tenant_id, + 'firewall_rules': [], + 'audited': audited, + 'shared': self.SHARED} + return attrs -class TestFirewallAgentApi(base.BaseTestCase): - def setUp(self): - super(TestFirewallAgentApi, self).setUp() + def _get_test_firewall_group_attrs(self, name='firewall_1', + status=nl_constants.CREATED): + attrs = {'name': name, + 'tenant_id': self._tenant_id, + 'project_id': self._tenant_id, + 'admin_state_up': self.ADMIN_STATE_UP, + 'status': status} - self.api = fwaas_plugin_v2.FirewallAgentApi('topic', 'host') + return attrs - def test_init(self): - self.assertEqual('topic', self.api.client.target.topic) - self.assertEqual('host', self.api.host) + def _create_firewall_policy(self, fmt, name, description, shared, + firewall_rules, audited, + expected_res_status=None, **kwargs): + data = {'firewall_policy': {'name': name, + 'description': description, + 'firewall_rules': firewall_rules, + 'audited': audited, + 'shared': shared}} + ctx = kwargs.get('context', None) + if ctx is None or ctx.is_admin: + tenant_id = kwargs.get('tenant_id', self._tenant_id) + data['firewall_policy'].update({'tenant_id': tenant_id}) + data['firewall_policy'].update({'project_id': tenant_id}) - def _call_test_helper(self, method_name): - with mock.patch.object(self.api.client, 'cast') as rpc_mock, \ - mock.patch.object(self.api.client, 'prepare') as prepare_mock: - prepare_mock.return_value = self.api.client - getattr(self.api, method_name)(mock.sentinel.context, 'test') + req = self.new_create_request('firewall_policies', data, fmt, + context=ctx) + res = req.get_response(self.ext_api) + if expected_res_status: + self.assertEqual(expected_res_status, res.status_int) + elif res.status_int >= 400: + raise http_client_error(req, res) - prepare_args = {'fanout': True} - prepare_mock.assert_called_once_with(**prepare_args) + return res - rpc_mock.assert_called_once_with(mock.sentinel.context, method_name, - firewall_group='test', host='host') + def _replace_firewall_status(self, attrs, old_status, new_status): + if attrs['status'] is old_status: + attrs['status'] = new_status + return attrs - def test_create_firewall_group(self): - self._call_test_helper('create_firewall_group') + @contextlib.contextmanager + def firewall_policy(self, fmt=None, name='firewall_policy1', + description=DESCRIPTION, shared=SHARED, + firewall_rules=None, audited=True, + do_delete=True, **kwargs): + if firewall_rules is None: + firewall_rules = [] + if not fmt: + fmt = self.fmt + res = self._create_firewall_policy(fmt, name, description, shared, + firewall_rules, audited, **kwargs) + if res.status_int >= 400: + raise webob.exc.HTTPClientError(code=res.status_int) + firewall_policy = self.deserialize(fmt or self.fmt, res) + yield firewall_policy + if do_delete: + self._delete('firewall_policies', + firewall_policy['firewall_policy']['id']) - def test_update_firewall_group(self): - self._call_test_helper('update_firewall_group') + def _create_firewall_rule(self, fmt, name, shared, protocol, + ip_version, source_ip_address, + destination_ip_address, source_port, + destination_port, action, enabled, + expected_res_status=None, **kwargs): + tenant_id = kwargs.get('tenant_id', self._tenant_id) + data = {'firewall_rule': {'name': name, + 'protocol': protocol, + 'ip_version': ip_version, + 'source_ip_address': source_ip_address, + 'destination_ip_address': + destination_ip_address, + 'source_port': source_port, + 'destination_port': destination_port, + 'action': action, + 'enabled': enabled, + 'shared': shared}} + ctx = kwargs.get('context', None) + if ctx is None or ctx.is_admin: + tenant_id = kwargs.get('tenant_id', self._tenant_id) + data['firewall_rule'].update({'tenant_id': tenant_id}) + data['firewall_rule'].update({'project_id': tenant_id}) - def test_delete_firewall_group(self): - self._call_test_helper('delete_firewall_group') + req = self.new_create_request('firewall_rules', data, fmt, context=ctx) + res = req.get_response(self.ext_api) + if expected_res_status: + self.assertEqual(expected_res_status, res.status_int) + elif res.status_int >= 400: + raise http_client_error(req, res) + return res -class TestFirewallRouterPortBase( - test_db_firewall.FirewallPluginV2DbTestCase): + @contextlib.contextmanager + def firewall_rule(self, fmt=None, name='firewall_rule1', + shared=SHARED, protocol=PROTOCOL, ip_version=IP_VERSION, + source_ip_address=SOURCE_IP_ADDRESS_RAW, + destination_ip_address=DESTINATION_IP_ADDRESS_RAW, + source_port=SOURCE_PORT, + destination_port=DESTINATION_PORT, + action=ACTION, enabled=ENABLED, + do_delete=True, **kwargs): + if not fmt: + fmt = self.fmt + res = self._create_firewall_rule(fmt, name, shared, protocol, + ip_version, source_ip_address, + destination_ip_address, + source_port, destination_port, + action, enabled, **kwargs) + if res.status_int >= 400: + raise webob.exc.HTTPClientError(code=res.status_int) + firewall_rule = self.deserialize(fmt or self.fmt, res) + yield firewall_rule + if do_delete: + self._delete('firewall_rules', + firewall_rule['firewall_rule']['id']) - def setUp(self, core_plugin=None, fw_plugin=None, ext_mgr=None): - self.agentapi_del_fw_p = mock.patch(test_db_firewall.DELETEFW_PATH, - create=True, - new=test_db_firewall.FakeAgentApi().delete_firewall_group) - self.agentapi_del_fw_p.start() + def _create_firewall_group(self, fmt, name, description, + ingress_firewall_policy_id=None, + egress_firewall_policy_id=None, + ports=None, admin_state_up=True, + expected_res_status=None, **kwargs): + if ingress_firewall_policy_id is None: + default_policy = kwargs.get('default_policy', True) + if default_policy: + res = self._create_firewall_policy( + fmt, + 'fwp', + description=self.DESCRIPTION, + shared=self.SHARED, + firewall_rules=[], + audited=self.AUDITED, + ) + firewall_policy = self.deserialize(fmt or self.fmt, res) + fwp_id = firewall_policy["firewall_policy"]["id"] + ingress_firewall_policy_id = fwp_id + data = {'firewall_group': {'name': name, + 'description': description, + 'ingress_firewall_policy_id': ingress_firewall_policy_id, + 'egress_firewall_policy_id': egress_firewall_policy_id, + 'admin_state_up': admin_state_up}} + ctx = kwargs.get('context', None) + if ctx is None or ctx.is_admin: + tenant_id = kwargs.get('tenant_id', self._tenant_id) + data['firewall_group'].update({'tenant_id': tenant_id}) + data['firewall_group'].update({'project_id': tenant_id}) + if ports is not None: + data['firewall_group'].update({'ports': ports}) - # the plugin without L3 support - plugin = 'neutron.tests.unit.extensions.test_l3.TestNoL3NatPlugin' - # the L3 service plugin - l3_plugin = ('neutron.tests.unit.extensions.test_l3.' - 'TestL3NatServicePlugin') + req = self.new_create_request('firewall_groups', data, fmt, + context=ctx) + res = req.get_response(self.ext_api) + if expected_res_status: + self.assertEqual(expected_res_status, res.status_int) + elif res.status_int >= 400: + raise http_client_error(req, res) + return res - cfg.CONF.set_override('api_extensions_path', extensions_path) - if not fw_plugin: - fw_plugin = FW_PLUGIN_KLASS - service_plugins = {'l3_plugin_name': l3_plugin, - 'fw_plugin_name': fw_plugin} + @contextlib.contextmanager + def firewall_group(self, fmt=None, name='firewall_1', + description=DESCRIPTION, + ingress_firewall_policy_id=None, + egress_firewall_policy_id=None, + ports=None, admin_state_up=True, + do_delete=True, **kwargs): + if not fmt: + fmt = self.fmt + res = self._create_firewall_group(fmt, name, description, + ingress_firewall_policy_id, + egress_firewall_policy_id, + ports=ports, + admin_state_up=admin_state_up, + **kwargs) + if res.status_int >= 400: + raise webob.exc.HTTPClientError(code=res.status_int) + firewall_group = self.deserialize(fmt or self.fmt, res) + yield firewall_group + if do_delete: + self._delete('firewall_groups', + firewall_group['firewall_group']['id']) - if not ext_mgr: - ext_mgr = FirewallTestExtensionManager() - super(test_db_firewall.FirewallPluginV2DbTestCase, self).setUp( - plugin=plugin, service_plugins=service_plugins, ext_mgr=ext_mgr) + def _rule_action(self, action, id, firewall_rule_id, insert_before=None, + insert_after=None, expected_code=webob.exc.HTTPOk.code, + expected_body=None, body_data=None): + # We intentionally do this check for None since we want to distinguish + # from empty dictionary + if body_data is None: + if action == 'insert': + body_data = {'firewall_rule_id': firewall_rule_id, + 'insert_before': insert_before, + 'insert_after': insert_after} + else: + body_data = {'firewall_rule_id': firewall_rule_id} - self.setup_notification_driver() + req = self.new_action_request('firewall_policies', + body_data, id, + "%s_rule" % action) + res = req.get_response(self.ext_api) + self.assertEqual(expected_code, res.status_int) + response = self.deserialize(self.fmt, res) + if expected_body: + self.assertEqual(expected_body, response) + return response - self.l3_plugin = directory.get_plugin(plugin_constants.L3) - self.plugin = directory.get_plugin('FIREWALL_V2') - self.callbacks = self.plugin.endpoints[0] + def _compare_firewall_rule_lists(self, firewall_policy_id, + observed_list, expected_list): + position = 0 + for r1, r2 in zip(observed_list, expected_list): + rule = r1['firewall_rule'] + rule['firewall_policy_id'] = firewall_policy_id + position += 1 + rule['position'] = position + for k in rule: + self.assertEqual(r2[k], rule[k]) - -class TestFirewallCallbacks(TestFirewallRouterPortBase): - - def setUp(self): - super(TestFirewallCallbacks, self).setUp(fw_plugin=FW_PLUGIN_KLASS) - self.callbacks = self.plugin.endpoints[0] - - def test_set_firewall_group_status(self): - ctx = context.get_admin_context() + def _test_create_firewall_group(self, attrs): with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] + attrs['ingress_firewall_policy_id'] = fwp_id + attrs['egress_firewall_policy_id'] = fwp_id with self.firewall_group( - ingress_firewall_policy_id=fwp_id, - admin_state_up=test_db_firewall.ADMIN_STATE_UP - ) as fwg: - fwg_id = fwg['firewall_group']['id'] - res = self.callbacks.set_firewall_group_status(ctx, fwg_id, - nl_constants.ACTIVE) - fwg_db = self.plugin.get_firewall_group(ctx, fwg_id) - self.assertEqual(nl_constants.ACTIVE, fwg_db['status']) - self.assertTrue(res) - res = self.callbacks.set_firewall_group_status(ctx, fwg_id, - nl_constants.ERROR) - fwg_db = self.plugin.get_firewall_group(ctx, fwg_id) - self.assertEqual(nl_constants.ERROR, fwg_db['status']) - self.assertFalse(res) - - def test_firewall_group_deleted(self): - ctx = context.get_admin_context() - with self.firewall_policy() as fwp: - fwp_id = fwp['firewall_policy']['id'] - with self.firewall_group( - ingress_firewall_policy_id=fwp_id, - admin_state_up=test_db_firewall.ADMIN_STATE_UP, - do_delete=False - ) as fwg: - fwg_id = fwg['firewall_group']['id'] - with ctx.session.begin(subtransactions=True): - fwg_db = self.plugin._get_firewall_group(ctx, fwg_id) - fwg_db['status'] = nl_constants.PENDING_DELETE - - observed = self.callbacks.firewall_group_deleted(ctx, fwg_id) - self.assertTrue(observed) - - self.assertRaises(f_exc.FirewallGroupNotFound, - self.plugin.get_firewall_group, - ctx, fwg_id) - - def test_firewall_group_deleted_concurrently(self): - ctx = context.get_admin_context() - alt_ctx = context.get_admin_context() - - _get_firewall_group = self.plugin._get_firewall_group - - def getdelete(context, fwg_id): - fwg_db = _get_firewall_group(context, fwg_id) - # NOTE(cby): Use a different session to simulate a concurrent del - with mock.patch.object( - firewall_db_v2.Firewall_db_mixin_v2, - 'delete_firewall_group', - return_value=None): - self.plugin.delete_db_firewall_group_object(alt_ctx, fwg_id) - return fwg_db - - with self.firewall_policy() as fwp: - fwp_id = fwp['firewall_policy']['id'] - with self.firewall_group( - firewall_policy_id=fwp_id, - admin_state_up=test_db_firewall.ADMIN_STATE_UP, - do_delete=False - ) as fwg: - fwg_id = fwg['firewall_group']['id'] - with ctx.session.begin(subtransactions=True): - fwg_db = self.plugin._get_firewall_group(ctx, fwg_id) - fwg_db['status'] = nl_constants.PENDING_DELETE - ctx.session.flush() - with mock.patch.object( - self.plugin, '_get_firewall_group', side_effect=getdelete - ): - observed = self.callbacks.firewall_group_deleted( - ctx, fwg_id) - self.assertTrue(observed) - - self.assertRaises(f_exc.FirewallGroupNotFound, - self.plugin.get_firewall_group, - ctx, fwg_id) - - def test_firewall_group_deleted_not_found(self): - ctx = context.get_admin_context() - observed = self.callbacks.firewall_group_deleted( - ctx, 'notfound') - self.assertTrue(observed) - - def test_firewall_group_deleted_error(self): - ctx = context.get_admin_context() - with self.firewall_policy() as fwp: - fwp_id = fwp['firewall_policy']['id'] - with self.firewall_group( - firewall_policy_id=fwp_id, - admin_state_up=test_db_firewall.ADMIN_STATE_UP, - ) as fwg: - fwg_id = fwg['firewall_group']['id'] - observed = self.callbacks.firewall_group_deleted( - ctx, fwg_id) - self.assertFalse(observed) - fwg_db = self.plugin._get_firewall_group(ctx, fwg_id) - self.assertEqual(nl_constants.ERROR, fwg_db['status']) - - -class TestFirewallPluginBasev2(TestFirewallRouterPortBase, - test_l3_plugin.L3NatTestCaseMixin): - - def setUp(self): - super(TestFirewallPluginBasev2, self).setUp(fw_plugin=FW_PLUGIN_KLASS) - fake_notifier.reset() - mock.patch.object(self.plugin, '_is_supported_by_fw_l2_driver', - return_value=True).start() - - @property - def _self_context(self): - return context.Context('', self._tenant_id) - - def test_create_firewall_group_ports_not_specified(self): - """neutron firewall-create test-policy """ - with self.firewall_policy() as fwp: - fwp_id = fwp['firewall_policy']['id'] - with self.firewall_group( - name='test', + name=attrs['name'], ingress_firewall_policy_id=fwp_id, egress_firewall_policy_id=fwp_id, - admin_state_up=True) as fwg1: - self.assertEqual(nl_constants.INACTIVE, - fwg1['firewall_group']['status']) + admin_state_up=self.ADMIN_STATE_UP, + ports=attrs['ports'] if 'ports' in attrs else None, + ) as firewall_group: + for k, v in six.iteritems(attrs): + self.assertEqual(v, firewall_group['firewall_group'][k]) - def test_create_firewall_group_with_ports(self): - """neutron firewall_group create test-policy """ - with self.router(name='router1', admin_state_up=True, - tenant_id=self._tenant_id) as r, \ - self.subnet() as s1, \ - self.subnet(cidr='20.0.0.0/24') as s2: - body = self._router_interface_action( - 'add', - r['router']['id'], - s1['subnet']['id'], - None) - port_id1 = body['port_id'] +class TestFirewallPluginBasev2(FirewallPluginV2TestCase): + def test_create_firewall_group_with_port_on_different_project(self): + with self.port(tenant_id='fake_project_id_1') as port: + admin_ctx = context.get_admin_context() + self._create_firewall_group( + self.fmt, + "firewall_group1", + self.DESCRIPTION, + context=admin_ctx, + ports=[port['port']['id']], + expected_res_status=webob.exc.HTTPConflict.code, + ) - body = self._router_interface_action( - 'add', - r['router']['id'], - s2['subnet']['id'], - None) - port_id2 = body['port_id'] - fwg_ports = [port_id1, port_id2] - with self.firewall_policy() as fwp: - fwp_id = fwp['firewall_policy']['id'] - with self.firewall_group( - name='test', - ingress_firewall_policy_id=fwp_id, - egress_firewall_policy_id=fwp_id, ports=fwg_ports, - admin_state_up=True) as fwg1: - self.assertEqual(nl_constants.PENDING_CREATE, - fwg1['firewall_group']['status']) - - self._router_interface_action('remove', - r['router']['id'], - s1['subnet']['id'], - None) - self._router_interface_action( - 'remove', - r['router']['id'], - s2['subnet']['id'], - None) - - def test_create_firewall_group_with_ports_on_diff_routers(self): - """neutron firewall_group create test-policy """ - with self.router(name='router1', admin_state_up=True, - tenant_id=self._tenant_id) as r, \ - self.subnet() as s1, \ - self.subnet(cidr='20.0.0.0/24') as s2: - body = self._router_interface_action( - 'add', - r['router']['id'], - s1['subnet']['id'], - None) - port_id1 = body['port_id'] - body = self._router_interface_action( - 'add', - r['router']['id'], - s2['subnet']['id'], - None) - port_id2 = body['port_id'] - - with self.router(name='router1', admin_state_up=True, - tenant_id=self._tenant_id) as r2, \ - self.subnet() as s3: - - body = self._router_interface_action( - 'add', - r2['router']['id'], - s3['subnet']['id'], - None) - port_id3 = body['port_id'] - - fwg_ports = [port_id1, port_id2, port_id3] - with self.firewall_policy() as fwp: - fwp_id = fwp['firewall_policy']['id'] - with self.firewall_group( - name='test', - ingress_firewall_policy_id=fwp_id, - egress_firewall_policy_id=fwp_id, - ports=fwg_ports, - admin_state_up=True) as fwg1: - self.assertEqual(nl_constants.PENDING_CREATE, - fwg1['firewall_group']['status']) - - self._router_interface_action('remove', - r2['router']['id'], - s3['subnet']['id'], - None) - - self._router_interface_action('remove', - r['router']['id'], - s1['subnet']['id'], - None) - self._router_interface_action( - 'remove', - r['router']['id'], - s2['subnet']['id'], - None) - - def test_create_firewall_group_with_ports_no_policy(self): - """neutron firewall_group create test-policy """ - with self.router(name='router1', admin_state_up=True, - tenant_id=self._tenant_id) as r, \ - self.subnet() as s1, \ - self.subnet(cidr='20.0.0.0/24') as s2: - - body = self._router_interface_action( - 'add', - r['router']['id'], - s1['subnet']['id'], - None) - port_id1 = body['port_id'] - - body = self._router_interface_action( - 'add', - r['router']['id'], - s2['subnet']['id'], - None) - port_id2 = body['port_id'] - fwg_ports = [port_id1, port_id2] - with self.firewall_group( - name='test', - default_policy=False, - ports=fwg_ports, - admin_state_up=True) as fwg1: - self.assertEqual(nl_constants.INACTIVE, - fwg1['firewall_group']['status']) - - self._router_interface_action('remove', - r['router']['id'], - s1['subnet']['id'], - None) - self._router_interface_action( - 'remove', - r['router']['id'], - s2['subnet']['id'], - None) - - def test_update_firewall_group_with_new_ports_no_polcy(self): - """neutron firewall_group create test-policy """ - with self.router(name='router1', admin_state_up=True, - tenant_id=self._tenant_id) as r, \ - self.subnet() as s1, \ - self.subnet(cidr='20.0.0.0/24') as s2, \ - self.subnet(cidr='30.0.0.0/24') as s3: - - body = self._router_interface_action( - 'add', - r['router']['id'], - s1['subnet']['id'], - None) - port_id1 = body['port_id'] - - body = self._router_interface_action( - 'add', - r['router']['id'], - s2['subnet']['id'], - None) - port_id2 = body['port_id'] - - body = self._router_interface_action( - 'add', - r['router']['id'], - s3['subnet']['id'], - None) - port_id3 = body['port_id'] - - fwg_ports = [port_id1, port_id2] - with self.firewall_group( - name='test', - default_policy=False, - ports=fwg_ports, - admin_state_up=True) as fwg1: - self.assertEqual(nl_constants.INACTIVE, - fwg1['firewall_group']['status']) - data = {'firewall_group': {'ports': [port_id2, port_id3]}} - req = self.new_update_request('firewall_groups', data, - fwg1['firewall_group']['id'], - context=self._self_context) - res = self.deserialize(self.fmt, - req.get_response(self.ext_api)) - - self.assertEqual(sorted([port_id2, port_id3]), - sorted(res['firewall_group']['ports'])) - - self.assertEqual(nl_constants.INACTIVE, - res['firewall_group']['status']) - - self._router_interface_action('remove', - r['router']['id'], - s1['subnet']['id'], - None) - self._router_interface_action( - 'remove', - r['router']['id'], - s2['subnet']['id'], - None) - - def test_update_firewall_group_with_new_ports_status_pending(self): - """neutron firewall_group create test-policy """ - with self.router(name='router1', admin_state_up=True, - tenant_id=self._tenant_id) as r, \ - self.subnet() as s1, \ - self.subnet(cidr='20.0.0.0/24') as s2, \ - self.subnet(cidr='30.0.0.0/24') as s3: - - body = self._router_interface_action( - 'add', - r['router']['id'], - s1['subnet']['id'], - None) - port_id1 = body['port_id'] - - body = self._router_interface_action( - 'add', - r['router']['id'], - s2['subnet']['id'], - None) - port_id2 = body['port_id'] - fwg_ports = [port_id1, port_id2] - - body = self._router_interface_action( - 'add', - r['router']['id'], - s3['subnet']['id'], - None) - port_id3 = body['port_id'] - - with self.firewall_policy() as fwp: - fwp_id = fwp['firewall_policy']['id'] - with self.firewall_group( - name='test', - ingress_firewall_policy_id=fwp_id, - egress_firewall_policy_id=fwp_id, ports=fwg_ports, - admin_state_up=True) as fwg1: - self.assertEqual(nl_constants.PENDING_CREATE, - fwg1['firewall_group']['status']) - data = {'firewall_group': {'ports': [port_id2, port_id3]}} - req = self.new_update_request('firewall_groups', data, - fwg1['firewall_group']['id']) - res = req.get_response(self.ext_api) - self.assertEqual(409, res.status_int) - self._router_interface_action('remove', - r['router']['id'], - s1['subnet']['id'], - None) - self._router_interface_action( - 'remove', - r['router']['id'], - s2['subnet']['id'], - None) - - def test_update_firewall_group_with_new_ports_status_active(self): - """neutron firewall_group create test-policy """ - with self.router(name='router1', admin_state_up=True, - tenant_id=self._tenant_id) as r, \ - self.subnet() as s1, \ - self.subnet(cidr='20.0.0.0/24') as s2, \ - self.subnet(cidr='30.0.0.0/24') as s3: - - body = self._router_interface_action( - 'add', - r['router']['id'], - s1['subnet']['id'], - None) - port_id1 = body['port_id'] - - body = self._router_interface_action( - 'add', - r['router']['id'], - s2['subnet']['id'], - None) - port_id2 = body['port_id'] - fwg_ports = [port_id1, port_id2] - - body = self._router_interface_action( - 'add', - r['router']['id'], - s3['subnet']['id'], - None) - port_id3 = body['port_id'] - - with self.firewall_policy() as fwp: - fwp_id = fwp['firewall_policy']['id'] - with self.firewall_group( - name='test', - ingress_firewall_policy_id=fwp_id, - egress_firewall_policy_id=fwp_id, ports=fwg_ports, - admin_state_up=True) as fwg1: - self.assertEqual(nl_constants.PENDING_CREATE, - fwg1['firewall_group']['status']) - - ctx = context.get_admin_context() - self.callbacks.set_firewall_group_status(ctx, - fwg1['firewall_group']['id'], nl_constants.ACTIVE) - data = {'firewall_group': {'ports': [port_id2, port_id3]}} - req = self.new_update_request('firewall_groups', data, - fwg1['firewall_group']['id'], - context=self._self_context) - res = self.deserialize(self.fmt, - req.get_response(self.ext_api)) - self.assertEqual(sorted([port_id2, port_id3]), - sorted(res['firewall_group']['ports'])) - - self._router_interface_action('remove', - r['router']['id'], - s1['subnet']['id'], - None) - self._router_interface_action( - 'remove', - r['router']['id'], - s2['subnet']['id'], - None) - - def test_update_firewall_rule_on_active_fwg(self): - name = "new_firewall_rule1" - attrs = self._get_test_firewall_rule_attrs(name) - with self.router(name='router1', admin_state_up=True, - tenant_id=self._tenant_id) as r, \ - self.subnet() as s1: - - body = self._router_interface_action( - 'add', - r['router']['id'], - s1['subnet']['id'], - None) - port_id1 = body['port_id'] - with self.firewall_rule() as fwr: - with self.firewall_policy( - firewall_rules=[fwr['firewall_rule']['id']]) as fwp: - fwp_id = fwp['firewall_policy']['id'] - with self.firewall_group( - name='test', - ingress_firewall_policy_id=fwp_id, - egress_firewall_policy_id=fwp_id, ports=[port_id1], - admin_state_up=True) as fwg1: - self.assertEqual(nl_constants.PENDING_CREATE, - fwg1['firewall_group']['status']) - - ctx = context.get_admin_context() - self.callbacks.set_firewall_group_status(ctx, - fwg1['firewall_group']['id'], nl_constants.ACTIVE) - data = {'firewall_rule': {'name': name}} - req = self.new_update_request('firewall_rules', data, - fwr['firewall_rule']['id']) - res = self.deserialize(self.fmt, - req.get_response(self.ext_api)) - for k, v in six.iteritems(attrs): - self.assertEqual(v, res['firewall_rule'][k]) - - self._router_interface_action('remove', - r['router']['id'], - s1['subnet']['id'], - None) - - def test_update_firewall_rule_on_pending_create_fwg(self): - """update should fail""" - name = "new_firewall_rule1" - with self.router(name='router1', admin_state_up=True, - tenant_id=self._tenant_id) as r, \ - self.subnet() as s1: - - body = self._router_interface_action( - 'add', - r['router']['id'], - s1['subnet']['id'], - None) - port_id1 = body['port_id'] - with self.firewall_rule() as fwr: - with self.firewall_policy( - firewall_rules=[fwr['firewall_rule']['id']]) as fwp: - fwp_id = fwp['firewall_policy']['id'] - with self.firewall_group( - name='test', - ingress_firewall_policy_id=fwp_id, - egress_firewall_policy_id=fwp_id, ports=[port_id1], - admin_state_up=True) as fwg1: - self.assertEqual(nl_constants.PENDING_CREATE, - fwg1['firewall_group']['status']) - - data = {'firewall_rule': {'name': name}} - req = self.new_update_request('firewall_rules', data, - fwr['firewall_rule']['id']) - res = req.get_response(self.ext_api) - self.assertEqual(409, res.status_int) - - self._router_interface_action('remove', - r['router']['id'], - s1['subnet']['id'], - None) - - def test_update_firewall_group_with_non_exist_ports(self): - """neutron firewall_group create test-policy """ - with self.router(name='router1', admin_state_up=True, - tenant_id=self._tenant_id) as r, \ - self.subnet(cidr='30.0.0.0/24') as s: - body = self._router_interface_action( - 'add', - r['router']['id'], - s['subnet']['id'], - None) - port_id1 = body['port_id'] - foo_port_id = 'caef152d-b118-4b9b-bc77-800661bf082d' - fwg_ports = [port_id1] - with self.firewall_group( - name='test', - default_policy=False, - ports=fwg_ports, - admin_state_up=True) as fwg1: - self.assertEqual(nl_constants.INACTIVE, - fwg1['firewall_group']['status']) - data = {'firewall_group': {'ports': [foo_port_id]}} - req = self.new_update_request('firewall_groups', data, - fwg1['firewall_group']['id']) - res = self.deserialize(self.fmt, - req.get_response(self.ext_api)) - self.assertEqual('PortNotFound', - res['NeutronError']['type']) - - self._router_interface_action('remove', - r['router']['id'], - s['subnet']['id'], - None) - - def _get_user_context(self, user_id="a_user", tenant_id="some_tenant", - is_admin=False): - return context.Context(user_id=user_id, tenant_id=tenant_id, - is_admin=is_admin) - - def test_update_firewall_group_with_invalid_project(self): - with self.router(name='router1', - admin_state_up=True, - tenant_id=self._tenant_id) as r, \ - self.subnet(cidr='30.0.0.0/24') as s: - body = self._router_interface_action('add', - r['router']['id'], - s['subnet']['id'], - None) - port_id = body['port_id'] - with self.firewall_group(name='test', - default_policy=False, - ports=[], - admin_state_up=True, - ctx=self._get_user_context()) as fwg1: - data = {'firewall_group': {'ports': [port_id]}} - # make sure that an exception is raised when admin tries to - # update ports of another tenant + def test_update_firewall_group_with_port_on_different_project(self): + ctx = context.Context('not_admin', 'fake_project_id_1') + with self.firewall_group(ctx=ctx) as firewall_group: + with self.port(tenant_id='fake_project_id_2') as port: + data = { + 'firewall_group': { + 'ports': [port['port']['id']], + }, + } req = self.new_update_request( - 'firewall_groups', data, fwg1['firewall_group']['id'], - context=context.get_admin_context()) - res = self.deserialize(self.fmt, - req.get_response(self.ext_api)) - self.assertEqual('FirewallGroupPortInvalidProject', - res['NeutronError']['type']) + 'firewall_groups', + data, + firewall_group['firewall_group']['id'], + ) + res = req.get_response(self.ext_api) + self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) - self._router_interface_action('remove', - r['router']['id'], - s['subnet']['id'], - None) + def test_create_firewall_group_with_with_wrong_type_port(self): + with self.port(device_owner="wrong port type") as port: + self._create_firewall_group( + self.fmt, + "firewall_group1", + self.DESCRIPTION, + ports=[port['port']['id']], + expected_res_status=webob.exc.HTTPConflict.code, + ) - def test_update_firewall_group_with_ports_and_polcy(self): - """neutron firewall_group create test-policy """ - with self.router(name='router1', admin_state_up=True, - tenant_id=self._tenant_id) as r,\ - self.subnet() as s1,\ - self.subnet(cidr='20.0.0.0/24') as s2: + def test_update_firewall_group_with_with_wrong_type_port(self): + with self.firewall_group() as firewall_group: + with self.port(device_owner="wrong port type") as port: + data = { + 'firewall_group': { + 'ports': [port['port']['id']], + }, + } + req = self.new_update_request( + 'firewall_groups', + data, + firewall_group['firewall_group']['id'], + ) + res = req.get_response(self.ext_api) + self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) - body = self._router_interface_action( - 'add', - r['router']['id'], - s1['subnet']['id'], - None) - port_id1 = body['port_id'] + def test_create_firewall_group_with_port_already_in_use(self): + with self.port( + device_owner=nl_constants.DEVICE_OWNER_ROUTER_INTF) as port: + with self.firewall_group(ports=[port['port']['id']]): + self._create_firewall_group( + self.fmt, + "firewall_group2", + self.DESCRIPTION, + ports=[port['port']['id']], + expected_res_status=webob.exc.HTTPConflict.code, + ) - body = self._router_interface_action( - 'add', - r['router']['id'], - s2['subnet']['id'], - None) - port_id2 = body['port_id'] + def test_update_firewall_group_with_port_already_in_use(self): + with self.port( + device_owner=nl_constants.DEVICE_OWNER_ROUTER_INTF) as port: + with self.firewall_group(ports=[port['port']['id']]): + with self.firewall_group() as firewall_group: + data = { + 'firewall_group': { + 'ports': [port['port']['id']], + }, + } + req = self.new_update_request( + 'firewall_groups', + data, + firewall_group['firewall_group']['id'], + ) + res = req.get_response(self.ext_api) + self.assertEqual(webob.exc.HTTPConflict.code, + res.status_int) - fwg_ports = [port_id1, port_id2] + def test_firewall_group_policy_rule_can_be_updated(self): + pending_status = [nl_constants.PENDING_CREATE, + nl_constants.PENDING_UPDATE, + nl_constants.PENDING_DELETE] + + for status in pending_status: with self.firewall_rule() as fwr: - with self.firewall_policy( - firewall_rules=[fwr['firewall_rule']['id']]) as fwp: + fwr_id = fwr['firewall_rule']['id'] + with self.firewall_policy(firewall_rules=[fwr_id]) as fwp: + fwp_id = fwp['firewall_policy']['id'] with self.firewall_group( - name='test', - default_policy=False, - ports=fwg_ports, - admin_state_up=True) as fwg1: - self.assertEqual(nl_constants.INACTIVE, - fwg1['firewall_group']['status']) - fwp_id = fwp["firewall_policy"]["id"] + ingress_firewall_policy_id=fwp_id) as fwg: + self.plugin.driver.firewall_db.\ + update_firewall_group_status( + context.get_admin_context(), + fwg['firewall_group']['id'], + status + ) + data = { + 'firewall_rule': { + 'name': 'new_name', + }, + } + req = self.new_update_request( + 'firewall_rules', + data, + fwr_id, + ) + res = req.get_response(self.ext_api) + self.assertEqual(webob.exc.HTTPConflict.code, + res.status_int) - data = {'firewall_group': {'ports': fwg_ports}} - req = (self. - new_update_request('firewall_groups', data, - fwg1['firewall_group']['id'], - context=self._self_context)) - res = self.deserialize(self.fmt, - req.get_response(self.ext_api)) - self.assertEqual(nl_constants.INACTIVE, - res['firewall_group']['status']) + def test_create_firewall_policy_with_other_project_not_shared_rule(self): + project1_context = self._get_nonadmin_context(tenant_id='project1') + project2_context = self._get_nonadmin_context(tenant_id='project2') + with self.firewall_rule(context=project1_context, shared=False) as fwr: + fwr_id = fwr['firewall_rule']['id'] + self.firewall_policy( + context=project2_context, + firewall_rules=[fwr_id], + expected_res_status=webob.exc.HTTPNotFound.code, + ) - data = {'firewall_group': { - 'ingress_firewall_policy_id': fwp_id}} - req = (self. - new_update_request('firewall_groups', data, - fwg1['firewall_group']['id'], - context=self._self_context)) - res = self.deserialize(self.fmt, - req.get_response(self.ext_api)) - self.assertEqual(nl_constants.PENDING_UPDATE, - res['firewall_group']['status']) + def test_update_firewall_policy_with_other_project_not_shared_rule(self): + project1_context = self._get_nonadmin_context(tenant_id='project1') + project2_context = self._get_nonadmin_context(tenant_id='project2') + with self.firewall_rule(context=project1_context, shared=False) as fwr: + with self.firewall_policy(context=project2_context, + shared=False) as fwp: + fwr_id = fwr['firewall_rule']['id'] + fwp_id = fwp['firewall_policy']['id'] + data = { + 'firewall_policy': { + 'firewall_rules': [fwr_id], + }, + } + req = self.new_update_request('firewall_policy', data, fwp_id) + res = req.get_response(self.ext_api) + self.assertEqual(webob.exc.HTTPNotFound.code, res.status_int) - self._router_interface_action('remove', - r['router']['id'], - s1['subnet']['id'], - None) - self._router_interface_action( - 'remove', - r['router']['id'], - s2['subnet']['id'], - None) + def test_create_firewall_policy_with_other_project_shared_rule(self): + admin_context = self._get_admin_context() + project1_context = self._get_nonadmin_context(tenant_id='project1') + with self.firewall_rule(context=admin_context, shared=True) as fwr: + fwr_id = fwr['firewall_rule']['id'] + self.firewall_policy( + context=project1_context, + firewall_rules=[fwr_id], + expected_res_status=webob.exc.HTTPOk.code, + ) class TestAutomaticAssociation(TestFirewallPluginBasev2): - def setUp(self): # TODO(yushiro): Replace constant value for this test class # Set auto association fwg super(TestAutomaticAssociation, self).setUp() - self.agent_rpc = self.plugin.agent_rpc - self.plugin.set_port_for_default_firewall_group = mock.Mock() - self.plugin._get_fwg_port_details = mock.Mock() def test_vm_port(self): - self.plugin._get_fwg_port_details = mock.Mock() + port = { + "id": "fake_port", + "device_owner": "compute:nova", + "binding:vif_type": "ovs", + "binding:vif_details": {"ovs_hybrid_plug": False}, + "project_id": "fake_project", + "port_security_enabled": True, + } + self.plugin._core_plugin.get_port = mock.Mock(return_value=port) + fake_default_fwg = { + 'id': 'fake_id', + 'name': 'default', + 'ports': ['fake_port_id1'], + } + self.plugin.get_firewall_groups = \ + mock.Mock(return_value=[fake_default_fwg]) + self.plugin.update_firewall_group = mock.Mock() kwargs = { "context": mock.ANY, - "port": {"id": "fake_port", - "device_owner": "compute:nova", - "binding:vif_type": "ovs", - "project_id": "fake_project"}, + "port": port, "original_port": {"binding:vif_type": "unbound"} } self.plugin.handle_update_port( "PORT", "after_update", "test_plugin", **kwargs) - self.plugin.set_port_for_default_firewall_group.\ - assert_called_once_with(mock.ANY, - kwargs['port']['id'], - kwargs['port']['project_id']) + self.plugin.get_firewall_groups.assert_called_once_with( + mock.ANY, + filters={ + 'tenant_id': [kwargs['port']['project_id']], + 'name': [fake_default_fwg['name']], + }, + fields=['id', 'ports'], + ) + port_ids = fake_default_fwg['ports'] + [kwargs['port']['id']] + self.plugin.update_firewall_group.assert_called_once_with( + mock.ANY, + fake_default_fwg['id'], + {'firewall_group': {'ports': port_ids}}, + ) def test_vm_port_not_newly_created(self): + self.plugin.get_firewall_group = mock.Mock() + self.plugin.update_firewall_group = mock.Mock() # Just updated for VM port(name or description...etc.) kwargs = { "context": mock.ANY, @@ -822,9 +642,12 @@ class TestAutomaticAssociation(TestFirewallPluginBasev2): } self.plugin.handle_update_port( "PORT", "after_update", "test_plugin", **kwargs) - self.plugin.set_port_for_default_firewall_group.assert_not_called() + self.plugin.get_firewall_group.assert_not_called() + self.plugin.update_firewall_group.assert_not_called() def test_not_vm_port(self): + self.plugin.get_firewall_group = mock.Mock() + self.plugin.update_firewall_group = mock.Mock() for device_owner in ["network:router_interface", "network:router_gateway", "network:dhcp"]: @@ -839,4 +662,31 @@ class TestAutomaticAssociation(TestFirewallPluginBasev2): } self.plugin.handle_update_port( "PORT", "after_update", "test_plugin", **kwargs) - self.plugin.set_port_for_default_firewall_group.assert_not_called() + self.plugin.get_firewall_group.assert_not_called() + self.plugin.update_firewall_group.assert_not_called() + + def test_set_port_for_default_firewall_group_raised_port_in_use(self): + port_id = 'fake_port_id_already_associated_to_default_fw' + port = { + "id": port_id, + "device_owner": "compute:nova", + "binding:vif_type": "ovs", + "binding:vif_details": {"ovs_hybrid_plug": False}, + "project_id": "fake_project", + "port_security_enabled": True, + } + self.plugin._core_plugin.get_port = mock.Mock(return_value=port) + self.plugin.get_firewall_groups = mock.Mock(return_value=[]) + self.plugin.update_firewall_group = mock.Mock( + side_effect=f_exc.FirewallGroupPortInUse(port_ids=[port_id])) + kwargs = { + "context": mock.ANY, + "port": port, + "original_port": {"binding:vif_type": "unbound"} + } + try: + self.plugin.handle_update_port("PORT", "after_update", + "test_plugin", **kwargs) + except f_exc.FirewallGroupPortInUse: + self.fail("Associating port to default firewall group raises " + "'FirewallGroupPortInUse' while it should ignore it") diff --git a/setup.cfg b/setup.cfg index c1fa6ad94..fedd3c157 100644 --- a/setup.cfg +++ b/setup.cfg @@ -34,9 +34,9 @@ setup-hooks = firewall_drivers = # These are for backwards compat with Juno firewall service provider # configuration values - neutron.services.firewall.drivers.linux.iptables_fwaas.IptablesFwaasDriver = neutron_fwaas.services.firewall.drivers.linux.iptables_fwaas:IptablesFwaasDriver - iptables = neutron_fwaas.services.firewall.drivers.linux.iptables_fwaas:IptablesFwaasDriver - iptables_v2 = neutron_fwaas.services.firewall.drivers.linux.iptables_fwaas_v2:IptablesFwaasDriver + neutron.services.firewall.drivers.linux.iptables_fwaas.IptablesFwaasDriver = neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.iptables_fwaas:IptablesFwaasDriver + iptables = neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.iptables_fwaas:IptablesFwaasDriver + iptables_v2 = neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.iptables_fwaas_v2:IptablesFwaasDriver neutron.service_plugins = firewall = neutron_fwaas.services.firewall.fwaas_plugin:FirewallPlugin firewall_v2 = neutron_fwaas.services.firewall.fwaas_plugin_v2:FirewallPluginV2 @@ -50,16 +50,16 @@ oslo.config.opts = neutron.fwaas = neutron_fwaas.opts:list_opts firewall.agent = neutron_fwaas.opts:list_agent_opts neutron.agent.l2.extensions = - fwaas_v2 = neutron_fwaas.services.firewall.agents.l2.fwaas_v2:FWaaSV2AgentExtension + fwaas_v2 = neutron_fwaas.services.firewall.service_drivers.agents.l2.fwaas_v2:FWaaSV2AgentExtension neutron.agent.l2.firewall_drivers = - noop = neutron_fwaas.services.firewall.drivers.linux.l2.noop.noop_driver:NoopFirewallL2Driver - ovs = neutron_fwaas.services.firewall.drivers.linux.l2.openvswitch_firewall.firewall:OVSFirewallDriver + noop = neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.l2.noop.noop_driver:NoopFirewallL2Driver + ovs = neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.l2.openvswitch_firewall.firewall:OVSFirewallDriver neutron.agent.l3.extensions = - fwaas = neutron_fwaas.services.firewall.agents.l3reference.firewall_l3_agent:L3WithFWaaS - fwaas_v2 = neutron_fwaas.services.firewall.agents.l3reference.firewall_l3_agent_v2:L3WithFWaaS -neutron_fwaas.services.firewall.drivers.linux = - conntrack = neutron_fwaas.services.firewall.drivers.linux.legacy_conntrack:ConntrackLegacy - netlink_conntrack = neutron_fwaas.services.firewall.drivers.linux.netlink_conntrack:ConntrackNetlink + fwaas = neutron_fwaas.services.firewall.service_drivers.agents.l3reference.firewall_l3_agent:L3WithFWaaS + fwaas_v2 = neutron_fwaas.services.firewall.service_drivers.agents.l3reference.firewall_l3_agent_v2:L3WithFWaaS +neutron.agent.l3.firewall_drivers = + conntrack = neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.legacy_conntrack:ConntrackLegacy + netlink_conntrack = neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.netlink_conntrack:ConntrackNetlink [extract_messages] keywords = _ gettext ngettext l_ lazy_gettext