FWaaS Insertion Model on Routers
Implementation for FWaaS insertion model to associate a Firewall with specified Routers as opposed to the current model of association with all Routers in the tenant. - extension and new table tracking associated routers - plugin changes to manage router association - changes to plugin - agent interface (adding router associations) - fw agent changes to pick up router associations from plugin - UT changes - Upgrade script APIImpact DocImpact Change-Id: I0eba4b0aa127738397e6b9c45556e207216d79c7 Implements: blueprint fwaas-router-insertion
This commit is contained in:
parent
d735611981
commit
d0704f0560
|
@ -27,7 +27,7 @@ from sqlalchemy.ext.orderinglist import ordering_list
|
|||
from sqlalchemy import orm
|
||||
from sqlalchemy.orm import exc
|
||||
|
||||
from neutron_fwaas.extensions import firewall
|
||||
from neutron_fwaas.extensions import firewall as fw_ext
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
@ -83,7 +83,7 @@ class FirewallPolicy(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant):
|
|||
firewalls = orm.relationship(Firewall, backref='firewall_policies')
|
||||
|
||||
|
||||
class Firewall_db_mixin(firewall.FirewallPluginBase, base_db.CommonDbMixin):
|
||||
class Firewall_db_mixin(fw_ext.FirewallPluginBase, base_db.CommonDbMixin):
|
||||
"""Mixin class for Firewall DB implementation."""
|
||||
|
||||
@property
|
||||
|
@ -94,19 +94,19 @@ class Firewall_db_mixin(firewall.FirewallPluginBase, base_db.CommonDbMixin):
|
|||
try:
|
||||
return self._get_by_id(context, Firewall, id)
|
||||
except exc.NoResultFound:
|
||||
raise firewall.FirewallNotFound(firewall_id=id)
|
||||
raise fw_ext.FirewallNotFound(firewall_id=id)
|
||||
|
||||
def _get_firewall_policy(self, context, id):
|
||||
try:
|
||||
return self._get_by_id(context, FirewallPolicy, id)
|
||||
except exc.NoResultFound:
|
||||
raise firewall.FirewallPolicyNotFound(firewall_policy_id=id)
|
||||
raise fw_ext.FirewallPolicyNotFound(firewall_policy_id=id)
|
||||
|
||||
def _get_firewall_rule(self, context, id):
|
||||
try:
|
||||
return self._get_by_id(context, FirewallRule, id)
|
||||
except exc.NoResultFound:
|
||||
raise firewall.FirewallRuleNotFound(firewall_rule_id=id)
|
||||
raise fw_ext.FirewallRuleNotFound(firewall_rule_id=id)
|
||||
|
||||
def _make_firewall_dict(self, fw, fields=None):
|
||||
res = {'id': fw['id'],
|
||||
|
@ -180,7 +180,7 @@ class Firewall_db_mixin(firewall.FirewallPluginBase, base_db.CommonDbMixin):
|
|||
def _check_firewall_rule_conflict(self, fwr_db, fwp_db):
|
||||
if not fwr_db['shared']:
|
||||
if fwr_db['tenant_id'] != fwp_db['tenant_id']:
|
||||
raise firewall.FirewallRuleConflict(
|
||||
raise fw_ext.FirewallRuleConflict(
|
||||
firewall_rule_id=fwr_db['id'],
|
||||
tenant_id=fwr_db['tenant_id'])
|
||||
|
||||
|
@ -202,20 +202,20 @@ class Firewall_db_mixin(firewall.FirewallPluginBase, base_db.CommonDbMixin):
|
|||
# If we find an invalid rule in the list we
|
||||
# do not perform the update since this breaks
|
||||
# the integrity of this list.
|
||||
raise firewall.FirewallRuleNotFound(
|
||||
raise fw_ext.FirewallRuleNotFound(
|
||||
firewall_rule_id=fwrule_id)
|
||||
elif rules_dict[fwrule_id]['firewall_policy_id']:
|
||||
if (rules_dict[fwrule_id]['firewall_policy_id'] !=
|
||||
fwp_db['id']):
|
||||
raise firewall.FirewallRuleInUse(
|
||||
raise fw_ext.FirewallRuleInUse(
|
||||
firewall_rule_id=fwrule_id)
|
||||
if 'shared' in fwp:
|
||||
if fwp['shared'] and not rules_dict[fwrule_id]['shared']:
|
||||
raise firewall.FirewallRuleSharingConflict(
|
||||
raise fw_ext.FirewallRuleSharingConflict(
|
||||
firewall_rule_id=fwrule_id,
|
||||
firewall_policy_id=fwp_db['id'])
|
||||
elif fwp_db['shared'] and not rules_dict[fwrule_id]['shared']:
|
||||
raise firewall.FirewallRuleSharingConflict(
|
||||
raise fw_ext.FirewallRuleSharingConflict(
|
||||
firewall_rule_id=fwrule_id,
|
||||
firewall_policy_id=fwp_db['id'])
|
||||
for fwr_db in rules_in_db:
|
||||
|
@ -235,7 +235,7 @@ class Firewall_db_mixin(firewall.FirewallPluginBase, base_db.CommonDbMixin):
|
|||
rules_in_db = fwp_db['firewall_rules']
|
||||
for fwr_db in rules_in_db:
|
||||
if not fwr_db['shared']:
|
||||
raise firewall.FirewallPolicySharingConflict(
|
||||
raise fw_ext.FirewallPolicySharingConflict(
|
||||
firewall_rule_id=fwr_db['id'],
|
||||
firewall_policy_id=fwp_db['id'])
|
||||
|
||||
|
@ -275,18 +275,19 @@ class Firewall_db_mixin(firewall.FirewallPluginBase, base_db.CommonDbMixin):
|
|||
protocol = fwr['protocol']
|
||||
if protocol not in (const.TCP, const.UDP):
|
||||
if fwr['source_port'] or fwr['destination_port']:
|
||||
raise firewall.FirewallRuleInvalidICMPParameter(
|
||||
raise fw_ext.FirewallRuleInvalidICMPParameter(
|
||||
param="Source, destination port")
|
||||
|
||||
def create_firewall(self, context, firewall):
|
||||
def create_firewall(self, context, firewall, status=None):
|
||||
LOG.debug("create_firewall() called")
|
||||
fw = firewall['firewall']
|
||||
tenant_id = self._get_tenant_id_for_create(context, fw)
|
||||
# distributed routers may required a more complex state machine;
|
||||
# the introduction of a new 'CREATED' state allows this, whilst
|
||||
# keeping a backward compatible behavior of the logical resource.
|
||||
status = (const.CREATED
|
||||
if cfg.CONF.router_distributed else const.PENDING_CREATE)
|
||||
if not status:
|
||||
status = (const.CREATED if cfg.CONF.router_distributed
|
||||
else const.PENDING_CREATE)
|
||||
with context.session.begin(subtransactions=True):
|
||||
firewall_db = Firewall(
|
||||
id=uuidutils.generate_uuid(),
|
||||
|
@ -305,7 +306,7 @@ class Firewall_db_mixin(firewall.FirewallPluginBase, base_db.CommonDbMixin):
|
|||
with context.session.begin(subtransactions=True):
|
||||
count = context.session.query(Firewall).filter_by(id=id).update(fw)
|
||||
if not count:
|
||||
raise firewall.FirewallNotFound(firewall_id=id)
|
||||
raise fw_ext.FirewallNotFound(firewall_id=id)
|
||||
return self.get_firewall(context, id)
|
||||
|
||||
def delete_firewall(self, context, id):
|
||||
|
@ -315,7 +316,7 @@ class Firewall_db_mixin(firewall.FirewallPluginBase, base_db.CommonDbMixin):
|
|||
# firewall is active
|
||||
count = context.session.query(Firewall).filter_by(id=id).delete()
|
||||
if not count:
|
||||
raise firewall.FirewallNotFound(firewall_id=id)
|
||||
raise fw_ext.FirewallNotFound(firewall_id=id)
|
||||
|
||||
def get_firewall(self, context, id, fields=None):
|
||||
LOG.debug("get_firewall() called")
|
||||
|
@ -357,7 +358,7 @@ class Firewall_db_mixin(firewall.FirewallPluginBase, base_db.CommonDbMixin):
|
|||
if not fwp.get('shared', True) and fwp_db.firewalls:
|
||||
for fw in fwp_db['firewalls']:
|
||||
if fwp_db['tenant_id'] != fw['tenant_id']:
|
||||
raise firewall.FirewallPolicyInUse(
|
||||
raise fw_ext.FirewallPolicyInUse(
|
||||
firewall_policy_id=id)
|
||||
# check any existing rules are not shared
|
||||
if 'shared' in fwp and 'firewall_rules' not in fwp:
|
||||
|
@ -378,7 +379,7 @@ class Firewall_db_mixin(firewall.FirewallPluginBase, base_db.CommonDbMixin):
|
|||
# being used
|
||||
qry = context.session.query(Firewall)
|
||||
if qry.filter_by(firewall_policy_id=id).first():
|
||||
raise firewall.FirewallPolicyInUse(firewall_policy_id=id)
|
||||
raise fw_ext.FirewallPolicyInUse(firewall_policy_id=id)
|
||||
else:
|
||||
context.session.delete(fwp)
|
||||
|
||||
|
@ -405,7 +406,7 @@ class Firewall_db_mixin(firewall.FirewallPluginBase, base_db.CommonDbMixin):
|
|||
tenant_id = self._get_tenant_id_for_create(context, fwr)
|
||||
if not fwr['protocol'] and (fwr['source_port'] or
|
||||
fwr['destination_port']):
|
||||
raise firewall.FirewallRuleWithPortWithoutProtocolInvalid()
|
||||
raise fw_ext.FirewallRuleWithPortWithoutProtocolInvalid()
|
||||
src_port_min, src_port_max = self._get_min_max_ports_from_range(
|
||||
fwr['source_port'])
|
||||
dst_port_min, dst_port_max = self._get_min_max_ports_from_range(
|
||||
|
@ -439,7 +440,7 @@ class Firewall_db_mixin(firewall.FirewallPluginBase, base_db.CommonDbMixin):
|
|||
fwr_db.firewall_policy_id)
|
||||
if 'shared' in fwr and not fwr['shared']:
|
||||
if fwr_db['tenant_id'] != fwp_db['tenant_id']:
|
||||
raise firewall.FirewallRuleInUse(firewall_rule_id=id)
|
||||
raise fw_ext.FirewallRuleInUse(firewall_rule_id=id)
|
||||
if 'source_port' in fwr:
|
||||
src_port_min, src_port_max = self._get_min_max_ports_from_range(
|
||||
fwr['source_port'])
|
||||
|
@ -460,7 +461,7 @@ class Firewall_db_mixin(firewall.FirewallPluginBase, base_db.CommonDbMixin):
|
|||
dport = fwr.get('destination_port_range_min',
|
||||
fwr_db['destination_port_range_min'])
|
||||
if sport or dport:
|
||||
raise firewall.FirewallRuleWithPortWithoutProtocolInvalid()
|
||||
raise fw_ext.FirewallRuleWithPortWithoutProtocolInvalid()
|
||||
fwr_db.update(fwr)
|
||||
if fwr_db.firewall_policy_id:
|
||||
fwp_db.audited = False
|
||||
|
@ -471,7 +472,7 @@ class Firewall_db_mixin(firewall.FirewallPluginBase, base_db.CommonDbMixin):
|
|||
with context.session.begin(subtransactions=True):
|
||||
fwr = self._get_firewall_rule(context, id)
|
||||
if fwr.firewall_policy_id:
|
||||
raise firewall.FirewallRuleInUse(firewall_rule_id=id)
|
||||
raise fw_ext.FirewallRuleInUse(firewall_rule_id=id)
|
||||
context.session.delete(fwr)
|
||||
|
||||
def get_firewall_rule(self, context, id, fields=None):
|
||||
|
@ -492,7 +493,7 @@ class Firewall_db_mixin(firewall.FirewallPluginBase, base_db.CommonDbMixin):
|
|||
|
||||
def _validate_insert_remove_rule_request(self, id, rule_info):
|
||||
if not rule_info or 'firewall_rule_id' not in rule_info:
|
||||
raise firewall.FirewallRuleInfoMissing()
|
||||
raise fw_ext.FirewallRuleInfoMissing()
|
||||
|
||||
def insert_rule(self, context, id, rule_info):
|
||||
LOG.debug("insert_rule() called")
|
||||
|
@ -501,7 +502,7 @@ class Firewall_db_mixin(firewall.FirewallPluginBase, base_db.CommonDbMixin):
|
|||
insert_before = True
|
||||
ref_firewall_rule_id = None
|
||||
if not firewall_rule_id:
|
||||
raise firewall.FirewallRuleNotFound(firewall_rule_id=None)
|
||||
raise fw_ext.FirewallRuleNotFound(firewall_rule_id=None)
|
||||
if 'insert_before' in rule_info:
|
||||
ref_firewall_rule_id = rule_info['insert_before']
|
||||
if not ref_firewall_rule_id and 'insert_after' in rule_info:
|
||||
|
@ -512,7 +513,7 @@ class Firewall_db_mixin(firewall.FirewallPluginBase, base_db.CommonDbMixin):
|
|||
fwr_db = self._get_firewall_rule(context, firewall_rule_id)
|
||||
fwp_db = self._get_firewall_policy(context, id)
|
||||
if fwr_db.firewall_policy_id:
|
||||
raise firewall.FirewallRuleInUse(firewall_rule_id=fwr_db['id'])
|
||||
raise fw_ext.FirewallRuleInUse(firewall_rule_id=fwr_db['id'])
|
||||
self._check_firewall_rule_conflict(fwr_db, fwp_db)
|
||||
if ref_firewall_rule_id:
|
||||
# If reference_firewall_rule_id is set, the new rule
|
||||
|
@ -523,7 +524,7 @@ class Firewall_db_mixin(firewall.FirewallPluginBase, base_db.CommonDbMixin):
|
|||
ref_fwr_db = self._get_firewall_rule(
|
||||
context, ref_firewall_rule_id)
|
||||
if ref_fwr_db.firewall_policy_id != id:
|
||||
raise firewall.FirewallRuleNotAssociatedWithPolicy(
|
||||
raise fw_ext.FirewallRuleNotAssociatedWithPolicy(
|
||||
firewall_rule_id=ref_fwr_db['id'],
|
||||
firewall_policy_id=id)
|
||||
if insert_before:
|
||||
|
@ -545,11 +546,11 @@ class Firewall_db_mixin(firewall.FirewallPluginBase, base_db.CommonDbMixin):
|
|||
self._validate_insert_remove_rule_request(id, rule_info)
|
||||
firewall_rule_id = rule_info['firewall_rule_id']
|
||||
if not firewall_rule_id:
|
||||
raise firewall.FirewallRuleNotFound(firewall_rule_id=None)
|
||||
raise fw_ext.FirewallRuleNotFound(firewall_rule_id=None)
|
||||
with context.session.begin(subtransactions=True):
|
||||
fwr_db = self._get_firewall_rule(context, firewall_rule_id)
|
||||
if fwr_db.firewall_policy_id != id:
|
||||
raise firewall.FirewallRuleNotAssociatedWithPolicy(
|
||||
raise fw_ext.FirewallRuleNotAssociatedWithPolicy(
|
||||
firewall_rule_id=fwr_db['id'],
|
||||
firewall_policy_id=id)
|
||||
return self._process_rule_for_policy(context, id, fwr_db, None)
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
# Copyright 2015 Cisco Systems Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from neutron.common import log
|
||||
from neutron.db import model_base
|
||||
from oslo_log import log as logging
|
||||
|
||||
from neutron_fwaas.extensions import firewallrouterinsertion as fwrtrins
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FirewallRouterAssociation(model_base.BASEV2):
|
||||
|
||||
"""Tracks FW Router Association"""
|
||||
|
||||
__tablename__ = 'firewall_router_associations'
|
||||
|
||||
fw_id = sa.Column(sa.String(36),
|
||||
sa.ForeignKey('firewalls.id', ondelete="CASCADE"),
|
||||
primary_key=True)
|
||||
router_id = sa.Column(sa.String(36),
|
||||
sa.ForeignKey('routers.id', ondelete="CASCADE"),
|
||||
primary_key=True)
|
||||
|
||||
|
||||
class FirewallRouterInsertionDbMixin(object):
|
||||
|
||||
"""Access methods for the firewall_router_associations table."""
|
||||
|
||||
@log.log
|
||||
def set_routers_for_firewall(self, context, fw):
|
||||
"""Sets the routers associated with the fw."""
|
||||
with context.session.begin(subtransactions=True):
|
||||
for r_id in fw['router_ids']:
|
||||
fw_rtr_db = FirewallRouterAssociation(fw_id=fw['fw_id'],
|
||||
router_id=r_id)
|
||||
context.session.add(fw_rtr_db)
|
||||
|
||||
@log.log
|
||||
def get_firewall_routers(self, context, fwid):
|
||||
"""Gets all routers associated with a firewall."""
|
||||
with context.session.begin(subtransactions=True):
|
||||
fw_rtr_qry = context.session.query(
|
||||
FirewallRouterAssociation.router_id)
|
||||
fw_rtr_rows = fw_rtr_qry.filter_by(fw_id=fwid)
|
||||
fw_rtrs = [entry.router_id for entry in fw_rtr_rows]
|
||||
LOG.debug("get_firewall_routers(): fw_rtrs: %s", fw_rtrs)
|
||||
return fw_rtrs
|
||||
|
||||
@log.log
|
||||
def validate_firewall_routers_not_in_use(
|
||||
self, context, router_ids, fwid=None):
|
||||
"""Validate if router-ids not associated with any firewall.
|
||||
|
||||
If any of the router-ids in the list is already associated with
|
||||
a firewall, raise an exception else just return.
|
||||
"""
|
||||
fw_rtr_qry = context.session.query(FirewallRouterAssociation.router_id)
|
||||
fw_rtrs = fw_rtr_qry.filter(
|
||||
FirewallRouterAssociation.router_id.in_(router_ids),
|
||||
FirewallRouterAssociation.fw_id != fwid).all()
|
||||
if fw_rtrs:
|
||||
router_ids = [entry.router_id for entry in fw_rtrs]
|
||||
raise fwrtrins.FirewallRouterInUse(router_ids=router_ids)
|
||||
|
||||
@log.log
|
||||
def update_firewall_routers(self, context, fw):
|
||||
"""Update the firewall with new routers.
|
||||
|
||||
This involves removing existing router associations and replacing
|
||||
it with the new router associations provided in the update method.
|
||||
"""
|
||||
with context.session.begin(subtransactions=True):
|
||||
fw_rtr_qry = context.session.query(FirewallRouterAssociation)
|
||||
fw_rtr_qry.filter_by(fw_id=fw['fw_id']).delete()
|
||||
if fw['router_ids']:
|
||||
self.set_routers_for_firewall(context, fw)
|
||||
|
||||
# TODO(sridar): Investigate potential corner case if rpc failure
|
||||
# happens on PENDING_UPDATE and agent did not restart. Evaluate
|
||||
# complexity vs benefit of holding on to old entries until ack
|
||||
# from agent.
|
||||
|
||||
return fw
|
|
@ -0,0 +1,56 @@
|
|||
# Copyright 2014 OpenStack Foundation
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
"""FWaaS router insertion
|
||||
|
||||
Revision ID: 540142f314f4
|
||||
Revises: 4202e3047e47
|
||||
Create Date: 2015-02-06 17:02:24.279337
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '540142f314f4'
|
||||
down_revision = '4202e3047e47'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
SQL_STATEMENT = (
|
||||
"insert into firewall_router_associations "
|
||||
"select "
|
||||
"f.id as fw_id, r.id as router_id "
|
||||
"from firewalls f, routers r "
|
||||
"where "
|
||||
"f.tenant_id=r.tenant_id"
|
||||
)
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.create_table('firewall_router_associations',
|
||||
sa.Column('fw_id', sa.String(length=36), nullable=False),
|
||||
sa.Column('router_id', sa.String(length=36), nullable=False),
|
||||
sa.ForeignKeyConstraint(['fw_id'], ['firewalls.id'],
|
||||
ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['router_id'], ['routers.id'],
|
||||
ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('fw_id', 'router_id'),
|
||||
)
|
||||
|
||||
op.execute(SQL_STATEMENT)
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_table('firewall_router_associations')
|
|
@ -1 +1 @@
|
|||
start_neutron_fwaas
|
||||
540142f314f4
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
# Copyright 2015 Cisco Systems Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutron.api import extensions
|
||||
from neutron.api.v2 import attributes as attr
|
||||
from neutron.common import exceptions as nexception
|
||||
from oslo_log import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FirewallRouterInUse(nexception.InUse):
|
||||
message = _("Router(s) %(router_ids)s provided already associated with "
|
||||
"other Firewall(s). ")
|
||||
|
||||
|
||||
EXTENDED_ATTRIBUTES_2_0 = {
|
||||
'firewalls': {
|
||||
'router_ids': {'allow_post': True, 'allow_put': True,
|
||||
'validate': {'type:uuid_list': None},
|
||||
'is_visible': True, 'default': attr.ATTR_NOT_SPECIFIED},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Firewallrouterinsertion(extensions.ExtensionDescriptor):
|
||||
"""Extension class supporting Firewall and Router(s) association.
|
||||
|
||||
The extension enables providing an option to specify router-ids of
|
||||
routers where the firewall is to be installed. This is supported in
|
||||
a manner so that the older version of the API continues to be supported.
|
||||
On a CREATE, if the router_ids option is not specified then the firewall
|
||||
is installed on all routers on the tenant. If the router-ids option is
|
||||
provided with a list of routers then the firewall is installed on the
|
||||
specified routers. If the router-ids option is provided with an empty
|
||||
list then the firewall is created but put in an INACTIVE state to reflect
|
||||
that no routers are associated. This firewall can be updated with a list
|
||||
of routers which will then drive the state to ACTIVE after the agent
|
||||
installs and acks back. UPDATE also supports the option in a similar
|
||||
manner. If the router_ids option is not provided, then there is no change
|
||||
to the existing association with the routers. When the router_is option is
|
||||
provided with a list of routers or an empty list - this drives the new
|
||||
set of routers that the firewall is associated with.
|
||||
"""
|
||||
@classmethod
|
||||
def get_name(cls):
|
||||
return "Firewall Router insertion"
|
||||
|
||||
@classmethod
|
||||
def get_alias(cls):
|
||||
return "fwaasrouterinsertion"
|
||||
|
||||
@classmethod
|
||||
def get_description(cls):
|
||||
return "Firewall Router insertion on specified set of routers"
|
||||
|
||||
@classmethod
|
||||
def get_namespace(cls):
|
||||
return ("http://docs.openstack.org/ext/neutron/fwaasrouterinsertion"
|
||||
"/api/v1.0")
|
||||
|
||||
@classmethod
|
||||
def get_updated(cls):
|
||||
return "2015-01-27T10:00:00-00:00"
|
||||
|
||||
def get_extended_resources(self, version):
|
||||
if version == "2.0":
|
||||
return EXTENDED_ATTRIBUTES_2_0
|
||||
else:
|
||||
return {}
|
|
@ -14,6 +14,7 @@
|
|||
# under the License.
|
||||
|
||||
from neutron.agent.linux import ip_lib
|
||||
from neutron.common import log
|
||||
from neutron.common import topics
|
||||
from neutron import context
|
||||
from neutron.i18n import _LE
|
||||
|
@ -30,7 +31,6 @@ LOG = logging.getLogger(__name__)
|
|||
|
||||
class FWaaSL3PluginApi(api.FWaaSPluginApiMixin):
|
||||
"""Agent side of the FWaaS agent to FWaaS Plugin RPC API."""
|
||||
|
||||
def __init__(self, topic, host):
|
||||
super(FWaaSL3PluginApi, self).__init__(topic, host)
|
||||
|
||||
|
@ -73,20 +73,39 @@ class FWaaSL3AgentRpcCallback(api.FWaaSAgentRpcCallbackMixin):
|
|||
self.fw_service = firewall_service.FirewallService(self)
|
||||
self.event_observers.add(self.fw_service)
|
||||
self.fwaas_driver = self.fw_service.load_device_drivers()
|
||||
self.services_sync = False
|
||||
self.services_sync_needed = False
|
||||
# setup RPC to msg fwaas plugin
|
||||
self.fwplugin_rpc = FWaaSL3PluginApi(topics.FIREWALL_PLUGIN,
|
||||
conf.host)
|
||||
super(FWaaSL3AgentRpcCallback, self).__init__(host=conf.host)
|
||||
|
||||
def _get_router_info_list_for_tenant(self, routers, tenant_id):
|
||||
def _has_router_insertion_fields(self, fw):
|
||||
return 'add-router-ids' in fw
|
||||
|
||||
def _get_router_ids_for_fw(self, context, fw, to_delete=False):
|
||||
"""Return the router_ids either from fw dict or tenant routers."""
|
||||
if self._has_router_insertion_fields(fw):
|
||||
# it is a new version of plugin
|
||||
return (fw['del-router-ids'] if to_delete
|
||||
else fw['add-router-ids'])
|
||||
else:
|
||||
# we are in a upgrade and msg from older version of plugin
|
||||
try:
|
||||
routers = self.plugin_rpc.get_routers(context)
|
||||
except Exception:
|
||||
LOG.exception(
|
||||
_LE("FWaaS RPC failure in _get_router_ids_for_fw "
|
||||
"for firewall: %(fwid)s"),
|
||||
{'fwid': fw['id']})
|
||||
self.services_sync_needed = True
|
||||
return [
|
||||
router['id']
|
||||
for router in routers
|
||||
if router['tenant_id'] == fw['tenant_id']]
|
||||
|
||||
def _get_router_info_list_for_tenant(self, router_ids, tenant_id):
|
||||
"""Returns the list of router info objects on which to apply the fw."""
|
||||
root_ip = ip_lib.IPWrapper()
|
||||
# Get the routers for the tenant
|
||||
router_ids = [
|
||||
router['id']
|
||||
for router in routers
|
||||
if router['tenant_id'] == tenant_id]
|
||||
local_ns_list = (root_ip.get_namespaces()
|
||||
if self.conf.use_namespaces else [])
|
||||
|
||||
|
@ -105,56 +124,6 @@ class FWaaSL3AgentRpcCallback(api.FWaaSAgentRpcCallbackMixin):
|
|||
router_info_list.append(self.router_info[rid])
|
||||
return router_info_list
|
||||
|
||||
def _invoke_driver_for_plugin_api(self, context, fw, func_name):
|
||||
"""Invoke driver method for plugin API and provide status back."""
|
||||
LOG.debug("%(func_name)s from agent for fw: %(fwid)s",
|
||||
{'func_name': func_name, 'fwid': fw['id']})
|
||||
try:
|
||||
routers = self.plugin_rpc.get_routers(context)
|
||||
router_info_list = self._get_router_info_list_for_tenant(
|
||||
routers,
|
||||
fw['tenant_id'])
|
||||
if not router_info_list:
|
||||
LOG.debug('No Routers on tenant: %s', fw['tenant_id'])
|
||||
# fw was created before any routers were added, and if a
|
||||
# delete is sent then we need to ack so that plugin can
|
||||
# cleanup.
|
||||
if func_name == 'delete_firewall':
|
||||
self.fwplugin_rpc.firewall_deleted(context, fw['id'])
|
||||
return
|
||||
LOG.debug("Apply fw on Router List: '%s'",
|
||||
[ri.router['id'] for ri in router_info_list])
|
||||
# call into the driver
|
||||
try:
|
||||
self.fwaas_driver.__getattribute__(func_name)(
|
||||
self.conf.agent_mode,
|
||||
router_info_list,
|
||||
fw)
|
||||
if fw['admin_state_up']:
|
||||
status = constants.ACTIVE
|
||||
else:
|
||||
status = constants.DOWN
|
||||
except fw_ext.FirewallInternalDriverError:
|
||||
LOG.error(_LE("Firewall Driver Error for %(func_name)s "
|
||||
"for fw: %(fwid)s"),
|
||||
{'func_name': func_name, 'fwid': fw['id']})
|
||||
status = constants.ERROR
|
||||
# delete needs different handling
|
||||
if func_name == 'delete_firewall':
|
||||
if status in [constants.ACTIVE, constants.DOWN]:
|
||||
self.fwplugin_rpc.firewall_deleted(context, fw['id'])
|
||||
else:
|
||||
self.fwplugin_rpc.set_firewall_status(
|
||||
context,
|
||||
fw['id'],
|
||||
status)
|
||||
except Exception:
|
||||
LOG.exception(
|
||||
_LE("FWaaS RPC failure in %(func_name)s for fw: %(fwid)s"),
|
||||
{'func_name': func_name, 'fwid': fw['id']})
|
||||
self.services_sync = True
|
||||
return
|
||||
|
||||
def _invoke_driver_for_sync_from_plugin(self, ctx, router_info_list, fw):
|
||||
"""Invoke the delete driver method for status of PENDING_DELETE and
|
||||
update method for all other status to (re)apply on driver which is
|
||||
|
@ -202,39 +171,50 @@ class FWaaSL3AgentRpcCallback(api.FWaaSAgentRpcCallbackMixin):
|
|||
def _process_router_add(self, ri):
|
||||
"""On router add, get fw with rules from plugin and update driver."""
|
||||
LOG.debug("Process router add, router_id: '%s'", ri.router['id'])
|
||||
routers = []
|
||||
routers.append(ri.router)
|
||||
router_ids = ri.router['id']
|
||||
router_info_list = self._get_router_info_list_for_tenant(
|
||||
routers,
|
||||
[router_ids],
|
||||
ri.router['tenant_id'])
|
||||
if router_info_list:
|
||||
# Get the firewall with rules
|
||||
# for the tenant the router is on.
|
||||
ctx = context.Context('', ri.router['tenant_id'])
|
||||
fw_list = self.fwplugin_rpc.get_firewalls_for_tenant(ctx)
|
||||
LOG.debug("Process router add, fw_list: '%s'",
|
||||
[fw['id'] for fw in fw_list])
|
||||
for fw in fw_list:
|
||||
if self._has_router_insertion_fields(fw):
|
||||
# if router extension present apply only if router in fw
|
||||
if (not (router_ids in fw['add-router-ids']) and
|
||||
not (router_ids in fw['del-router-ids'])):
|
||||
continue
|
||||
self._invoke_driver_for_sync_from_plugin(
|
||||
ctx,
|
||||
router_info_list,
|
||||
fw)
|
||||
# router can be present only on one fw
|
||||
return
|
||||
|
||||
def process_router_add(self, ri):
|
||||
"""On router add, get fw with rules from plugin and update driver."""
|
||||
"""On router add, get fw with rules from plugin and update driver.
|
||||
|
||||
Handles agent restart, when a router is added, query the plugin to
|
||||
check if this router is in the router list for any firewall. If so
|
||||
install firewall rules on this router.
|
||||
"""
|
||||
# avoid msg to plugin when fwaas is not configured
|
||||
if not self.fwaas_enabled:
|
||||
return
|
||||
try:
|
||||
# TODO(sridar): as per discussion with pc_m, we may want to hook
|
||||
# this up to the l3 agent observer notification
|
||||
self._process_router_add(ri)
|
||||
except Exception:
|
||||
LOG.exception(
|
||||
_LE("FWaaS RPC info call failed for '%s'."),
|
||||
ri.router['id'])
|
||||
self.services_sync = True
|
||||
self.services_sync_needed = True
|
||||
|
||||
def process_services_sync(self, ctx):
|
||||
if not self.services_sync:
|
||||
if not self.services_sync_needed:
|
||||
return
|
||||
|
||||
"""On RPC issues sync with plugin and apply the sync data."""
|
||||
|
@ -242,8 +222,6 @@ class FWaaSL3AgentRpcCallback(api.FWaaSAgentRpcCallbackMixin):
|
|||
if not self.fwaas_enabled:
|
||||
return
|
||||
try:
|
||||
# get all routers
|
||||
routers = self.plugin_rpc.get_routers(ctx)
|
||||
# get the list of tenants with firewalls configured
|
||||
# from the plugin
|
||||
tenant_ids = self.fwplugin_rpc.get_tenants_with_firewalls(ctx)
|
||||
|
@ -251,51 +229,174 @@ class FWaaSL3AgentRpcCallback(api.FWaaSAgentRpcCallbackMixin):
|
|||
for tenant_id in tenant_ids:
|
||||
ctx = context.Context('', tenant_id)
|
||||
fw_list = self.fwplugin_rpc.get_firewalls_for_tenant(ctx)
|
||||
if fw_list:
|
||||
# if fw present on tenant
|
||||
router_info_list = self._get_router_info_list_for_tenant(
|
||||
routers,
|
||||
tenant_id)
|
||||
if router_info_list:
|
||||
LOG.debug("Router List: '%s'",
|
||||
[ri.router['id'] for ri in router_info_list])
|
||||
LOG.debug("fw_list: '%s'",
|
||||
[fw['id'] for fw in fw_list])
|
||||
# apply sync data on fw for this tenant
|
||||
for fw in fw_list:
|
||||
# fw, routers present on this host for tenant
|
||||
# install
|
||||
LOG.debug("Apply fw on Router List: '%s'",
|
||||
[ri.router['id']
|
||||
for ri in router_info_list])
|
||||
# no need to apply sync data for ACTIVE fw
|
||||
if fw['status'] != constants.ACTIVE:
|
||||
self._invoke_driver_for_sync_from_plugin(
|
||||
ctx,
|
||||
router_info_list,
|
||||
fw)
|
||||
self.services_sync = False
|
||||
for fw in fw_list:
|
||||
if fw['status'] == constants.PENDING_DELETE:
|
||||
self.delete_firewall(ctx, fw, self.host)
|
||||
# no need to apply sync data for ACTIVE fw
|
||||
elif fw['status'] != constants.ACTIVE:
|
||||
self.update_firewall(ctx, fw, self.host)
|
||||
self.services_sync_needed = False
|
||||
except Exception:
|
||||
LOG.exception(_LE("Failed fwaas process services sync"))
|
||||
self.services_sync = True
|
||||
self.services_sync_needed = True
|
||||
|
||||
@log.log
|
||||
def create_firewall(self, context, firewall, host):
|
||||
"""Handle Rpc from plugin to create a firewall."""
|
||||
return self._invoke_driver_for_plugin_api(
|
||||
context,
|
||||
firewall,
|
||||
'create_firewall')
|
||||
|
||||
router_ids = self._get_router_ids_for_fw(context, firewall)
|
||||
if not router_ids:
|
||||
return
|
||||
router_info_list = self._get_router_info_list_for_tenant(
|
||||
router_ids,
|
||||
firewall['tenant_id'])
|
||||
LOG.debug("Create: Add firewall on Router List: '%s'",
|
||||
[ri.router['id'] for ri in router_info_list])
|
||||
# call into the driver
|
||||
try:
|
||||
self.fwaas_driver.create_firewall(
|
||||
self.conf.agent_mode,
|
||||
router_info_list,
|
||||
firewall)
|
||||
if firewall['admin_state_up']:
|
||||
status = constants.ACTIVE
|
||||
else:
|
||||
status = constants.DOWN
|
||||
except fw_ext.FirewallInternalDriverError:
|
||||
LOG.error(_LE("Firewall Driver Error for create_firewall "
|
||||
"for firewall: %(fwid)s"),
|
||||
{'fwid': firewall['id']})
|
||||
status = constants.ERROR
|
||||
|
||||
try:
|
||||
# send status back to plugin
|
||||
self.fwplugin_rpc.set_firewall_status(
|
||||
context,
|
||||
firewall['id'],
|
||||
status)
|
||||
except Exception:
|
||||
LOG.exception(
|
||||
_LE("FWaaS RPC failure in create_firewall "
|
||||
"for firewall: %(fwid)s"),
|
||||
{'fwid': firewall['id']})
|
||||
self.services_sync_needed = True
|
||||
|
||||
@log.log
|
||||
def update_firewall(self, context, firewall, host):
|
||||
"""Handle Rpc from plugin to update a firewall."""
|
||||
return self._invoke_driver_for_plugin_api(
|
||||
context,
|
||||
firewall,
|
||||
'update_firewall')
|
||||
|
||||
status = ""
|
||||
if self._has_router_insertion_fields(firewall):
|
||||
# with the router_ids extension, we may need to delete and add
|
||||
# based on the list of routers. On the older version, we just
|
||||
# update (add) all routers on the tenant - delete not needed.
|
||||
router_ids = self._get_router_ids_for_fw(
|
||||
context, firewall, to_delete=True)
|
||||
if router_ids:
|
||||
router_info_list = self._get_router_info_list_for_tenant(
|
||||
router_ids,
|
||||
firewall['tenant_id'])
|
||||
# remove the firewall from this set of routers
|
||||
# but no ack sent yet, check if we need to add
|
||||
LOG.debug("Update: Delete firewall on Router List: '%s'",
|
||||
[ri.router['id'] for ri in router_info_list])
|
||||
try:
|
||||
self.fwaas_driver.delete_firewall(
|
||||
self.conf.agent_mode,
|
||||
router_info_list,
|
||||
firewall)
|
||||
if firewall['last-router']:
|
||||
status = constants.INACTIVE
|
||||
elif firewall['admin_state_up']:
|
||||
status = constants.ACTIVE
|
||||
else:
|
||||
status = constants.DOWN
|
||||
except fw_ext.FirewallInternalDriverError:
|
||||
LOG.error(_LE("Firewall Driver Error for "
|
||||
"update_firewall for firewall: "
|
||||
"%(fwid)s"),
|
||||
{'fwid': firewall['id']})
|
||||
status = constants.ERROR
|
||||
|
||||
# the add
|
||||
if status not in (constants.ERROR, constants.INACTIVE):
|
||||
router_ids = self._get_router_ids_for_fw(context, firewall)
|
||||
if router_ids:
|
||||
router_info_list = self._get_router_info_list_for_tenant(
|
||||
router_ids,
|
||||
firewall['tenant_id'])
|
||||
LOG.debug("Update: Add firewall on Router List: '%s'",
|
||||
[ri.router['id'] for ri in router_info_list])
|
||||
# call into the driver
|
||||
try:
|
||||
self.fwaas_driver.update_firewall(
|
||||
self.conf.agent_mode,
|
||||
router_info_list,
|
||||
firewall)
|
||||
if firewall['admin_state_up']:
|
||||
status = constants.ACTIVE
|
||||
else:
|
||||
status = constants.DOWN
|
||||
except fw_ext.FirewallInternalDriverError:
|
||||
LOG.error(_LE("Firewall Driver Error for "
|
||||
"update_firewall for firewall: "
|
||||
"%(fwid)s"),
|
||||
{'fwid': firewall['id']})
|
||||
status = constants.ERROR
|
||||
|
||||
try:
|
||||
# send status back to plugin
|
||||
self.fwplugin_rpc.set_firewall_status(
|
||||
context,
|
||||
firewall['id'],
|
||||
status)
|
||||
except Exception:
|
||||
LOG.exception(
|
||||
_LE("FWaaS RPC failure in update_firewall "
|
||||
"for firewall: %(fwid)s"),
|
||||
{'fwid': firewall['id']})
|
||||
self.services_sync_needed = True
|
||||
|
||||
@log.log
|
||||
def delete_firewall(self, context, firewall, host):
|
||||
"""Handle Rpc from plugin to delete a firewall."""
|
||||
return self._invoke_driver_for_plugin_api(
|
||||
context,
|
||||
firewall,
|
||||
'delete_firewall')
|
||||
|
||||
router_ids = self._get_router_ids_for_fw(
|
||||
context, firewall, to_delete=True)
|
||||
if router_ids:
|
||||
router_info_list = self._get_router_info_list_for_tenant(
|
||||
router_ids,
|
||||
firewall['tenant_id'])
|
||||
LOG.debug("Delete: Delete firewall on Router List: '%s'",
|
||||
[ri.router['id'] for ri in router_info_list])
|
||||
# call into the driver
|
||||
try:
|
||||
self.fwaas_driver.delete_firewall(
|
||||
self.conf.agent_mode,
|
||||
router_info_list,
|
||||
firewall)
|
||||
if firewall['admin_state_up']:
|
||||
status = constants.ACTIVE
|
||||
else:
|
||||
status = constants.DOWN
|
||||
except fw_ext.FirewallInternalDriverError:
|
||||
LOG.error(_LE("Firewall Driver Error for delete_firewall "
|
||||
"for firewall: %(fwid)s"),
|
||||
{'fwid': firewall['id']})
|
||||
status = constants.ERROR
|
||||
|
||||
try:
|
||||
# send status back to plugin
|
||||
if status in [constants.ACTIVE, constants.DOWN]:
|
||||
self.fwplugin_rpc.firewall_deleted(context, firewall['id'])
|
||||
else:
|
||||
self.fwplugin_rpc.set_firewall_status(
|
||||
context,
|
||||
firewall['id'],
|
||||
status)
|
||||
except Exception:
|
||||
LOG.exception(
|
||||
_LE("FWaaS RPC failure in delete_firewall "
|
||||
"for firewall: %(fwid)s"),
|
||||
{'fwid': firewall['id']})
|
||||
self.services_sync_needed = True
|
||||
|
|
|
@ -13,17 +13,19 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutron.common import exceptions as n_exception
|
||||
from neutron.api.v2 import attributes as attr
|
||||
from neutron.common import rpc as n_rpc
|
||||
from neutron.common import topics
|
||||
from neutron import context as neutron_context
|
||||
from neutron.i18n import _LW
|
||||
from neutron import manager
|
||||
from neutron.plugins.common import constants as const
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
import oslo_messaging
|
||||
|
||||
from neutron_fwaas.db.firewall import firewall_db
|
||||
from neutron_fwaas.db.firewall import firewall_router_insertion_db
|
||||
from neutron_fwaas.extensions import firewall as fw_ext
|
||||
|
||||
|
||||
|
@ -51,7 +53,7 @@ class FirewallCallbacks(object):
|
|||
"not changing to %(status)s",
|
||||
{'fw_id': firewall_id, 'status': status})
|
||||
return False
|
||||
if status in (const.ACTIVE, const.DOWN):
|
||||
if status in (const.ACTIVE, const.DOWN, const.INACTIVE):
|
||||
fw_db.status = status
|
||||
return True
|
||||
else:
|
||||
|
@ -77,10 +79,19 @@ class FirewallCallbacks(object):
|
|||
def get_firewalls_for_tenant(self, context, **kwargs):
|
||||
"""Agent uses this to get all firewalls and rules for a tenant."""
|
||||
LOG.debug("get_firewalls_for_tenant() called")
|
||||
fw_list = [
|
||||
self.plugin._make_firewall_dict_with_rules(context, fw['id'])
|
||||
for fw in self.plugin.get_firewalls(context)
|
||||
]
|
||||
fw_list = []
|
||||
for fw in self.plugin.get_firewalls(context):
|
||||
fw_with_rules = self.plugin._make_firewall_dict_with_rules(
|
||||
context, fw['id'])
|
||||
if fw['status'] == const.PENDING_DELETE:
|
||||
fw_with_rules['add-router-ids'] = []
|
||||
fw_with_rules['del-router-ids'] = (
|
||||
self.plugin.get_firewall_routers(context, fw['id']))
|
||||
else:
|
||||
fw_with_rules['add-router-ids'] = (
|
||||
self.plugin.get_firewall_routers(context, fw['id']))
|
||||
fw_with_rules['del-router-ids'] = []
|
||||
fw_list.append(fw_with_rules)
|
||||
return fw_list
|
||||
|
||||
def get_firewalls_for_tenant_without_rules(self, context, **kwargs):
|
||||
|
@ -122,18 +133,9 @@ class FirewallAgentApi(object):
|
|||
host=self.host)
|
||||
|
||||
|
||||
class FirewallCountExceeded(n_exception.Conflict):
|
||||
|
||||
"""Reference implementation specific exception for firewall count.
|
||||
|
||||
Only one firewall is supported per tenant. When a second
|
||||
firewall is tried to be created, this exception will be raised.
|
||||
"""
|
||||
message = _("Exceeded allowed count of firewalls for tenant "
|
||||
"%(tenant_id)s. Only one firewall is supported per tenant.")
|
||||
|
||||
|
||||
class FirewallPlugin(firewall_db.Firewall_db_mixin):
|
||||
class FirewallPlugin(
|
||||
firewall_db.Firewall_db_mixin,
|
||||
firewall_router_insertion_db.FirewallRouterInsertionDbMixin):
|
||||
|
||||
"""Implementation of the Neutron Firewall Service Plugin.
|
||||
|
||||
|
@ -141,11 +143,10 @@ class FirewallPlugin(firewall_db.Firewall_db_mixin):
|
|||
Most DB related works are implemented in class
|
||||
firewall_db.Firewall_db_mixin.
|
||||
"""
|
||||
supported_extension_aliases = ["fwaas"]
|
||||
supported_extension_aliases = ["fwaas", "fwaasrouterinsertion"]
|
||||
|
||||
def __init__(self):
|
||||
"""Do the initialization for the firewall service plugin here."""
|
||||
|
||||
self.endpoints = [FirewallCallbacks(self)]
|
||||
|
||||
self.conn = n_rpc.create_connection(new=True)
|
||||
|
@ -164,6 +165,11 @@ class FirewallPlugin(firewall_db.Firewall_db_mixin):
|
|||
status_update)
|
||||
fw_with_rules = self._make_firewall_dict_with_rules(context,
|
||||
firewall_id)
|
||||
# this is triggered on an update to fw rule or policy, no
|
||||
# change in associated routers.
|
||||
fw_with_rules['add-router-ids'] = self.get_firewall_routers(
|
||||
context, id)
|
||||
fw_with_rules['del-router-ids'] = []
|
||||
self.agent_rpc.update_firewall(context, fw_with_rules)
|
||||
|
||||
def _rpc_update_firewall_policy(self, context, firewall_policy_id):
|
||||
|
@ -192,28 +198,123 @@ class FirewallPlugin(firewall_db.Firewall_db_mixin):
|
|||
self._ensure_update_firewall_policy(context,
|
||||
fw_rule['firewall_policy_id'])
|
||||
|
||||
def _get_routers_for_create_firewall(self, tenant_id, context, firewall):
|
||||
|
||||
# pop router_id as this goes in the router association db
|
||||
# and not firewall db
|
||||
router_ids = firewall['firewall'].pop('router_ids', None)
|
||||
if router_ids == attr.ATTR_NOT_SPECIFIED:
|
||||
# old semantics router-ids keyword not specified pick up
|
||||
# all routers on tenant.
|
||||
l3_plugin = manager.NeutronManager.get_service_plugins().get(
|
||||
const.L3_ROUTER_NAT)
|
||||
ctx = neutron_context.get_admin_context()
|
||||
routers = l3_plugin.get_routers(ctx)
|
||||
router_ids = [
|
||||
router['id']
|
||||
for router in routers
|
||||
if router['tenant_id'] == tenant_id]
|
||||
# validation can still fail this if there is another fw
|
||||
# which is associated with one of these routers.
|
||||
self.validate_firewall_routers_not_in_use(context, router_ids)
|
||||
return router_ids
|
||||
else:
|
||||
if not router_ids:
|
||||
# This indicates that user specifies no routers.
|
||||
return []
|
||||
else:
|
||||
# some router(s) provided.
|
||||
self.validate_firewall_routers_not_in_use(context, router_ids)
|
||||
return router_ids
|
||||
|
||||
def create_firewall(self, context, firewall):
|
||||
LOG.debug("create_firewall() called")
|
||||
tenant_id = self._get_tenant_id_for_create(context,
|
||||
firewall['firewall'])
|
||||
fw_count = self.get_firewalls_count(context,
|
||||
filters={'tenant_id': [tenant_id]})
|
||||
if fw_count:
|
||||
raise FirewallCountExceeded(tenant_id=tenant_id)
|
||||
fw = super(FirewallPlugin, self).create_firewall(context, firewall)
|
||||
firewall['firewall'])
|
||||
|
||||
fw_new_rtrs = self._get_routers_for_create_firewall(
|
||||
tenant_id, context, firewall)
|
||||
|
||||
if not fw_new_rtrs:
|
||||
# no messaging to agent needed, and fw needs to go
|
||||
# to INACTIVE(no associated rtrs) state.
|
||||
status = const.INACTIVE
|
||||
fw = super(FirewallPlugin, self).create_firewall(
|
||||
context, firewall, status)
|
||||
fw['router_ids'] = []
|
||||
return fw
|
||||
else:
|
||||
fw = super(FirewallPlugin, self).create_firewall(
|
||||
context, firewall)
|
||||
fw['router_ids'] = fw_new_rtrs
|
||||
|
||||
fw_with_rules = (
|
||||
self._make_firewall_dict_with_rules(context, fw['id']))
|
||||
|
||||
fw_with_rtrs = {'fw_id': fw['id'],
|
||||
'router_ids': fw_new_rtrs}
|
||||
self.set_routers_for_firewall(context, fw_with_rtrs)
|
||||
fw_with_rules['add-router-ids'] = fw_new_rtrs
|
||||
fw_with_rules['del-router-ids'] = []
|
||||
|
||||
self.agent_rpc.create_firewall(context, fw_with_rules)
|
||||
|
||||
return fw
|
||||
|
||||
def update_firewall(self, context, id, firewall):
|
||||
LOG.debug("update_firewall() called")
|
||||
|
||||
self._ensure_update_firewall(context, id)
|
||||
firewall['firewall']['status'] = const.PENDING_UPDATE
|
||||
fw = super(FirewallPlugin, self).update_firewall(context, id, firewall)
|
||||
# pop router_id as this goes in the router association db
|
||||
# and not firewall db
|
||||
router_ids = firewall['firewall'].pop('router_ids', None)
|
||||
fw_current_rtrs = self.get_firewall_routers(context, id)
|
||||
if router_ids is not None:
|
||||
if router_ids == []:
|
||||
# This indicates that user is indicating no routers.
|
||||
fw_new_rtrs = []
|
||||
else:
|
||||
self.validate_firewall_routers_not_in_use(
|
||||
context, router_ids, id)
|
||||
fw_new_rtrs = router_ids
|
||||
self.update_firewall_routers(context, {'fw_id': id,
|
||||
'router_ids': fw_new_rtrs})
|
||||
else:
|
||||
# router-ids keyword not specified for update pick up
|
||||
# existing routers.
|
||||
fw_new_rtrs = self.get_firewall_routers(context, id)
|
||||
|
||||
if not fw_new_rtrs and not fw_current_rtrs:
|
||||
# no messaging to agent needed, and we need to continue
|
||||
# in INACTIVE state
|
||||
firewall['firewall']['status'] = const.INACTIVE
|
||||
fw = super(FirewallPlugin, self).update_firewall(
|
||||
context, id, firewall)
|
||||
fw['router_ids'] = []
|
||||
return fw
|
||||
else:
|
||||
firewall['firewall']['status'] = const.PENDING_UPDATE
|
||||
fw = super(FirewallPlugin, self).update_firewall(
|
||||
context, id, firewall)
|
||||
fw['router_ids'] = fw_new_rtrs
|
||||
|
||||
fw_with_rules = (
|
||||
self._make_firewall_dict_with_rules(context, fw['id']))
|
||||
|
||||
# determine rtrs to add fw to and del from
|
||||
fw_with_rules['add-router-ids'] = fw_new_rtrs
|
||||
fw_with_rules['del-router-ids'] = list(
|
||||
set(fw_current_rtrs).difference(set(fw_new_rtrs)))
|
||||
|
||||
# last-router drives agent to ack with status to set state to INACTIVE
|
||||
fw_with_rules['last-router'] = not fw_new_rtrs
|
||||
|
||||
LOG.debug("update_firewall(): Add Routers: %s, Del Routers: %s",
|
||||
fw_with_rules['add-router-ids'],
|
||||
fw_with_rules['del-router-ids'])
|
||||
|
||||
self.agent_rpc.update_firewall(context, fw_with_rules)
|
||||
|
||||
return fw
|
||||
|
||||
def delete_db_firewall_object(self, context, id):
|
||||
|
@ -228,7 +329,14 @@ class FirewallPlugin(firewall_db.Firewall_db_mixin):
|
|||
status_update)
|
||||
fw_with_rules = (
|
||||
self._make_firewall_dict_with_rules(context, fw['id']))
|
||||
self.agent_rpc.delete_firewall(context, fw_with_rules)
|
||||
fw_with_rules['del-router-ids'] = self.get_firewall_routers(
|
||||
context, id)
|
||||
fw_with_rules['add-router-ids'] = []
|
||||
if not fw_with_rules['del-router-ids']:
|
||||
# no routers to delete on the agent side
|
||||
self.delete_db_firewall_object(context, id)
|
||||
else:
|
||||
self.agent_rpc.delete_firewall(context, fw_with_rules)
|
||||
|
||||
def update_firewall_policy(self, context, id, firewall_policy):
|
||||
LOG.debug("update_firewall_policy() called")
|
||||
|
@ -263,3 +371,20 @@ class FirewallPlugin(firewall_db.Firewall_db_mixin):
|
|||
self).remove_rule(context, id, rule_info)
|
||||
self._rpc_update_firewall_policy(context, id)
|
||||
return fwp
|
||||
|
||||
def get_firewalls(self, context, filters=None, fields=None):
|
||||
LOG.debug("fwaas get_firewalls() called")
|
||||
fw_list = super(FirewallPlugin, self).get_firewalls(
|
||||
context, filters, fields)
|
||||
for fw in fw_list:
|
||||
fw_current_rtrs = self.get_firewall_routers(context, fw['id'])
|
||||
fw['router_ids'] = fw_current_rtrs
|
||||
return fw_list
|
||||
|
||||
def get_firewall(self, context, id, fields=None):
|
||||
LOG.debug("fwaas get_firewall() called")
|
||||
res = super(FirewallPlugin, self).get_firewall(
|
||||
context, id, fields)
|
||||
fw_current_rtrs = self.get_firewall_routers(context, id)
|
||||
res['router_ids'] = fw_current_rtrs
|
||||
return res
|
||||
|
|
|
@ -97,55 +97,15 @@ class TestFwaasL3AgentRpcCallback(base.BaseTestCase):
|
|||
test_agent_class(cfg.CONF)
|
||||
|
||||
def test_create_firewall(self):
|
||||
fake_firewall = {'id': 0}
|
||||
with mock.patch.object(
|
||||
self.api,
|
||||
'_invoke_driver_for_plugin_api'
|
||||
) as mock_driver:
|
||||
self.assertEqual(
|
||||
self.api.create_firewall(
|
||||
mock.sentinel.context,
|
||||
fake_firewall,
|
||||
'host'),
|
||||
mock_driver.return_value)
|
||||
|
||||
def test_update_firewall(self):
|
||||
fake_firewall = {'id': 0}
|
||||
with mock.patch.object(
|
||||
self.api,
|
||||
'_invoke_driver_for_plugin_api'
|
||||
) as mock_driver:
|
||||
self.assertEqual(
|
||||
self.api.update_firewall(
|
||||
mock.sentinel.context,
|
||||
fake_firewall,
|
||||
'host'),
|
||||
mock_driver.return_value)
|
||||
|
||||
def test_delete_firewall(self):
|
||||
fake_firewall = {'id': 0}
|
||||
with mock.patch.object(
|
||||
self.api,
|
||||
'_invoke_driver_for_plugin_api'
|
||||
) as mock_driver:
|
||||
self.assertEqual(
|
||||
self.api.delete_firewall(
|
||||
mock.sentinel.context,
|
||||
fake_firewall,
|
||||
'host'),
|
||||
mock_driver.return_value)
|
||||
|
||||
def test_invoke_driver_for_plugin_api(self):
|
||||
fake_firewall = {'id': 0, 'tenant_id': 1,
|
||||
'admin_state_up': True}
|
||||
'admin_state_up': True,
|
||||
'add-router-ids': [1, 2]}
|
||||
self.api.plugin_rpc = mock.Mock()
|
||||
with contextlib.nested(
|
||||
mock.patch.object(self.api.plugin_rpc, 'get_routers'),
|
||||
mock.patch.object(self.api, '_get_router_info_list_for_tenant'),
|
||||
mock.patch.object(self.api.fwaas_driver, 'create_firewall'),
|
||||
mock.patch.object(self.api.fwplugin_rpc, 'set_firewall_status')
|
||||
) as (
|
||||
mock_get_routers,
|
||||
mock_get_router_info_list_for_tenant,
|
||||
mock_driver_create_firewall,
|
||||
mock_set_firewall_status):
|
||||
|
@ -155,199 +115,144 @@ class TestFwaasL3AgentRpcCallback(base.BaseTestCase):
|
|||
context=mock.sentinel.context,
|
||||
firewall=fake_firewall, host='host')
|
||||
|
||||
mock_get_routers.assert_called_once_with(
|
||||
mock.sentinel.context)
|
||||
|
||||
mock_get_router_info_list_for_tenant.assert_called_once_with(
|
||||
mock_get_routers.return_value, fake_firewall['tenant_id'])
|
||||
fake_firewall['add-router-ids'], fake_firewall['tenant_id'])
|
||||
|
||||
mock_set_firewall_status.assert_called_once_with(
|
||||
mock.sentinel.context,
|
||||
fake_firewall['id'],
|
||||
'ACTIVE')
|
||||
|
||||
def test_invoke_driver_for_plugin_api_admin_state_down(self):
|
||||
def test_update_firewall_with_routers_added_and_deleted(self):
|
||||
fake_firewall = {'id': 0, 'tenant_id': 1,
|
||||
'admin_state_up': False}
|
||||
'admin_state_up': True,
|
||||
'add-router-ids': [1, 2],
|
||||
'del-router-ids': [3, 4],
|
||||
'last-router': False}
|
||||
|
||||
self.api.plugin_rpc = mock.Mock()
|
||||
with contextlib.nested(
|
||||
mock.patch.object(self.api.plugin_rpc, 'get_routers'),
|
||||
mock.patch.object(self.api, '_get_router_info_list_for_tenant'),
|
||||
mock.patch.object(self.api.fwaas_driver, 'update_firewall'),
|
||||
mock.patch.object(self.api.fwplugin_rpc,
|
||||
'get_firewalls_for_tenant'),
|
||||
mock.patch.object(self.api.fwaas_driver, 'delete_firewall'),
|
||||
mock.patch.object(self.api.fwplugin_rpc, 'set_firewall_status')
|
||||
) as (
|
||||
mock_get_routers,
|
||||
mock_get_router_info_list_for_tenant,
|
||||
mock_driver_delete_firewall,
|
||||
mock_driver_update_firewall,
|
||||
mock_get_firewalls_for_tenant,
|
||||
mock_set_firewall_status):
|
||||
|
||||
mock_driver_delete_firewall.return_value = True
|
||||
mock_driver_update_firewall.return_value = True
|
||||
|
||||
calls = [mock.call(fake_firewall['del-router-ids'],
|
||||
fake_firewall['tenant_id']),
|
||||
mock.call(fake_firewall['add-router-ids'],
|
||||
fake_firewall['tenant_id'])]
|
||||
|
||||
self.api.update_firewall(
|
||||
context=mock.sentinel.context,
|
||||
firewall=fake_firewall, host='host')
|
||||
|
||||
mock_get_routers.assert_called_once_with(
|
||||
mock.sentinel.context)
|
||||
self.assertEqual(
|
||||
mock_get_router_info_list_for_tenant.call_args_list,
|
||||
calls)
|
||||
|
||||
mock_set_firewall_status.assert_called_once_with(
|
||||
mock.sentinel.context,
|
||||
fake_firewall['id'],
|
||||
'ACTIVE')
|
||||
|
||||
def test_update_firewall_with_routers_added_and_admin_state_down(self):
|
||||
fake_firewall = {'id': 0, 'tenant_id': 1,
|
||||
'admin_state_up': False,
|
||||
'add-router-ids': [1, 2],
|
||||
'del-router-ids': [],
|
||||
'last-router': False}
|
||||
|
||||
self.api.plugin_rpc = mock.Mock()
|
||||
with contextlib.nested(
|
||||
mock.patch.object(self.api, '_get_router_info_list_for_tenant'),
|
||||
mock.patch.object(self.api.fwaas_driver, 'update_firewall'),
|
||||
mock.patch.object(self.api.fwplugin_rpc, 'set_firewall_status')
|
||||
) as (
|
||||
mock_get_router_info_list_for_tenant,
|
||||
mock_driver_update_firewall,
|
||||
mock_set_firewall_status):
|
||||
|
||||
mock_driver_update_firewall.return_value = True
|
||||
|
||||
self.api.update_firewall(
|
||||
context=mock.sentinel.context,
|
||||
firewall=fake_firewall, host='host')
|
||||
|
||||
mock_get_router_info_list_for_tenant.assert_called_once_with(
|
||||
mock_get_routers.return_value, fake_firewall['tenant_id'])
|
||||
fake_firewall['add-router-ids'], fake_firewall['tenant_id'])
|
||||
|
||||
mock_set_firewall_status.assert_called_once_with(
|
||||
mock.sentinel.context,
|
||||
fake_firewall['id'],
|
||||
'DOWN')
|
||||
|
||||
def test_invoke_driver_for_plugin_api_delete(self):
|
||||
def test_update_firewall_with_all_routers_deleted(self):
|
||||
fake_firewall = {'id': 0, 'tenant_id': 1,
|
||||
'admin_state_up': True}
|
||||
'admin_state_up': True,
|
||||
'add-router-ids': [],
|
||||
'del-router-ids': [3, 4],
|
||||
'last-router': True}
|
||||
|
||||
self.api.plugin_rpc = mock.Mock()
|
||||
with contextlib.nested(
|
||||
mock.patch.object(self.api.plugin_rpc, 'get_routers'),
|
||||
mock.patch.object(self.api, '_get_router_info_list_for_tenant'),
|
||||
mock.patch.object(self.api.fwaas_driver, 'delete_firewall'),
|
||||
mock.patch.object(self.api.fwplugin_rpc, 'firewall_deleted')
|
||||
mock.patch.object(self.api.fwplugin_rpc, 'set_firewall_status')
|
||||
) as (
|
||||
mock_get_routers,
|
||||
mock_get_router_info_list_for_tenant,
|
||||
mock_driver_delete_firewall,
|
||||
mock_firewall_deleted):
|
||||
mock_set_firewall_status):
|
||||
|
||||
mock_driver_delete_firewall.return_value = True
|
||||
self.api.delete_firewall(
|
||||
|
||||
self.api.update_firewall(
|
||||
context=mock.sentinel.context,
|
||||
firewall=fake_firewall, host='host')
|
||||
|
||||
mock_get_routers.assert_called_once_with(
|
||||
mock.sentinel.context)
|
||||
|
||||
mock_get_router_info_list_for_tenant.assert_called_once_with(
|
||||
mock_get_routers.return_value, fake_firewall['tenant_id'])
|
||||
|
||||
mock_firewall_deleted.assert_called_once_with(
|
||||
mock.sentinel.context,
|
||||
fake_firewall['id'])
|
||||
|
||||
def test_delete_firewall_no_router(self):
|
||||
fake_firewall = {'id': 0, 'tenant_id': 1}
|
||||
self.api.plugin_rpc = mock.Mock()
|
||||
with contextlib.nested(
|
||||
mock.patch.object(self.api.plugin_rpc, 'get_routers'),
|
||||
mock.patch.object(self.api, '_get_router_info_list_for_tenant'),
|
||||
mock.patch.object(self.api.fwplugin_rpc, 'firewall_deleted')
|
||||
) as (
|
||||
mock_get_routers,
|
||||
mock_get_router_info_list_for_tenant,
|
||||
mock_firewall_deleted):
|
||||
|
||||
mock_get_router_info_list_for_tenant.return_value = []
|
||||
self.api.delete_firewall(
|
||||
context=mock.sentinel.context,
|
||||
firewall=fake_firewall, host='host')
|
||||
|
||||
mock_get_routers.assert_called_once_with(
|
||||
mock.sentinel.context)
|
||||
|
||||
mock_get_router_info_list_for_tenant.assert_called_once_with(
|
||||
mock_get_routers.return_value, fake_firewall['tenant_id'])
|
||||
|
||||
mock_firewall_deleted.assert_called_once_with(
|
||||
mock.sentinel.context,
|
||||
fake_firewall['id'])
|
||||
|
||||
def test_process_router_add_fw_update(self):
|
||||
fake_firewall_list = [{'id': 0, 'tenant_id': 1,
|
||||
'status': constants.PENDING_UPDATE,
|
||||
'admin_state_up': True}]
|
||||
fake_router = {'id': 1111, 'tenant_id': 2}
|
||||
self.api.plugin_rpc = mock.Mock()
|
||||
agent_mode = 'legacy'
|
||||
ri = mock.Mock()
|
||||
ri.router = fake_router
|
||||
routers = [ri.router]
|
||||
with contextlib.nested(
|
||||
mock.patch.object(self.api.plugin_rpc, 'get_routers'),
|
||||
mock.patch.object(self.api, '_get_router_info_list_for_tenant'),
|
||||
mock.patch.object(self.api.fwaas_driver, 'update_firewall'),
|
||||
mock.patch.object(self.api.fwplugin_rpc, 'set_firewall_status'),
|
||||
mock.patch.object(self.api.fwplugin_rpc,
|
||||
'get_firewalls_for_tenant'),
|
||||
mock.patch.object(context, 'Context')
|
||||
) as (
|
||||
mock_get_routers,
|
||||
mock_get_router_info_list_for_tenant,
|
||||
mock_driver_update_firewall,
|
||||
mock_set_firewall_status,
|
||||
mock_get_firewalls_for_tenant,
|
||||
mock_Context):
|
||||
|
||||
mock_driver_update_firewall.return_value = True
|
||||
ctx = mock.sentinel.context
|
||||
mock_Context.return_value = ctx
|
||||
mock_get_router_info_list_for_tenant.return_value = routers
|
||||
mock_get_firewalls_for_tenant.return_value = fake_firewall_list
|
||||
|
||||
self.api._process_router_add(ri)
|
||||
mock_get_router_info_list_for_tenant.assert_called_with(
|
||||
routers,
|
||||
ri.router['tenant_id'])
|
||||
mock_get_firewalls_for_tenant.assert_called_once_with(ctx)
|
||||
mock_driver_update_firewall.assert_called_once_with(
|
||||
agent_mode,
|
||||
routers,
|
||||
fake_firewall_list[0])
|
||||
fake_firewall['del-router-ids'], fake_firewall['tenant_id'])
|
||||
|
||||
mock_set_firewall_status.assert_called_once_with(
|
||||
ctx,
|
||||
fake_firewall_list[0]['id'],
|
||||
constants.ACTIVE)
|
||||
mock.sentinel.context,
|
||||
fake_firewall['id'],
|
||||
'INACTIVE')
|
||||
|
||||
def test_delete_firewall(self):
|
||||
fake_firewall = {'id': 0, 'tenant_id': 1,
|
||||
'admin_state_up': True,
|
||||
'add-router-ids': [],
|
||||
'del-router-ids': [3, 4],
|
||||
'last-router': True}
|
||||
|
||||
def test_process_router_add_fw_delete(self):
|
||||
fake_firewall_list = [{'id': 0, 'tenant_id': 1,
|
||||
'status': constants.PENDING_DELETE}]
|
||||
fake_router = {'id': 1111, 'tenant_id': 2}
|
||||
agent_mode = 'legacy'
|
||||
self.api.plugin_rpc = mock.Mock()
|
||||
ri = mock.Mock()
|
||||
ri.router = fake_router
|
||||
routers = [ri.router]
|
||||
with contextlib.nested(
|
||||
mock.patch.object(self.api.plugin_rpc, 'get_routers'),
|
||||
mock.patch.object(self.api, '_get_router_info_list_for_tenant'),
|
||||
mock.patch.object(self.api.fwaas_driver, 'delete_firewall'),
|
||||
mock.patch.object(self.api.fwplugin_rpc, 'firewall_deleted'),
|
||||
mock.patch.object(self.api.fwplugin_rpc,
|
||||
'get_firewalls_for_tenant'),
|
||||
mock.patch.object(context, 'Context')
|
||||
mock.patch.object(self.api.fwplugin_rpc, 'firewall_deleted')
|
||||
) as (
|
||||
mock_get_routers,
|
||||
mock_get_router_info_list_for_tenant,
|
||||
mock_driver_delete_firewall,
|
||||
mock_firewall_deleted,
|
||||
mock_get_firewalls_for_tenant,
|
||||
mock_Context):
|
||||
mock_firewall_deleted):
|
||||
|
||||
mock_driver_delete_firewall.return_value = True
|
||||
ctx = mock.sentinel.context
|
||||
mock_Context.return_value = ctx
|
||||
mock_get_router_info_list_for_tenant.return_value = routers
|
||||
mock_get_firewalls_for_tenant.return_value = fake_firewall_list
|
||||
self.api.delete_firewall(
|
||||
context=mock.sentinel.context,
|
||||
firewall=fake_firewall, host='host')
|
||||
|
||||
self.api._process_router_add(ri)
|
||||
mock_get_router_info_list_for_tenant.assert_called_with(
|
||||
routers,
|
||||
ri.router['tenant_id'])
|
||||
mock_get_firewalls_for_tenant.assert_called_once_with(ctx)
|
||||
mock_driver_delete_firewall.assert_called_once_with(
|
||||
agent_mode,
|
||||
routers,
|
||||
fake_firewall_list[0])
|
||||
mock_get_router_info_list_for_tenant.assert_called_once_with(
|
||||
fake_firewall['del-router-ids'], fake_firewall['tenant_id'])
|
||||
|
||||
mock_firewall_deleted.assert_called_once_with(
|
||||
ctx,
|
||||
fake_firewall_list[0]['id'])
|
||||
mock.sentinel.context,
|
||||
fake_firewall['id'])
|
||||
|
||||
def _prepare_router_data(self):
|
||||
return router_info.RouterInfo(self.router_id,
|
||||
|
@ -357,12 +262,13 @@ class TestFwaasL3AgentRpcCallback(base.BaseTestCase):
|
|||
self.conf.set_override('use_namespaces', use_namespaces)
|
||||
ri = self._prepare_router_data()
|
||||
routers = [ri.router]
|
||||
router_ids = [router['id'] for router in routers]
|
||||
self.api.router_info = {ri.router_id: ri}
|
||||
with mock.patch.object(ip_lib.IPWrapper,
|
||||
'get_namespaces') as mock_get_namespaces:
|
||||
mock_get_namespaces.return_value = []
|
||||
router_info_list = self.api._get_router_info_list_for_tenant(
|
||||
routers,
|
||||
router_ids,
|
||||
ri.router['tenant_id'])
|
||||
if use_namespaces:
|
||||
mock_get_namespaces.assert_called_once_with()
|
||||
|
@ -390,11 +296,12 @@ class TestFwaasL3AgentRpcCallback(base.BaseTestCase):
|
|||
self.api.router_info[ri.router_id] = ri
|
||||
routers.append(ri.router)
|
||||
ri_expected.append(ri)
|
||||
router_ids = [router['id'] for router in routers]
|
||||
with mock.patch.object(ip_lib.IPWrapper,
|
||||
'get_namespaces') as mock_get_namespaces:
|
||||
mock_get_namespaces.return_value = ri.ns_name
|
||||
router_info_list = self.api._get_router_info_list_for_tenant(
|
||||
routers,
|
||||
router_ids,
|
||||
ri.router['tenant_id'])
|
||||
self.assertEqual(ri_expected, router_info_list)
|
||||
|
||||
|
|
|
@ -17,22 +17,114 @@
|
|||
import contextlib
|
||||
|
||||
import mock
|
||||
|
||||
from neutron.api.v2 import attributes as attr
|
||||
from neutron import context
|
||||
from neutron import manager
|
||||
from neutron.plugins.common import constants as const
|
||||
from neutron.tests.unit import test_l3_plugin
|
||||
from neutron.tests.unit import testlib_plugin
|
||||
from oslo_config import cfg
|
||||
from webob import exc
|
||||
|
||||
import neutron_fwaas.extensions
|
||||
from neutron_fwaas.extensions import firewall
|
||||
from neutron_fwaas.extensions import firewallrouterinsertion
|
||||
from neutron_fwaas.services.firewall import fwaas_plugin
|
||||
from neutron_fwaas.tests import base
|
||||
from neutron_fwaas.tests.unit.db.firewall import test_db_firewall
|
||||
|
||||
extensions_path = neutron_fwaas.extensions.__path__[0]
|
||||
|
||||
FW_PLUGIN_KLASS = (
|
||||
"neutron_fwaas.services.firewall.fwaas_plugin.FirewallPlugin"
|
||||
)
|
||||
|
||||
|
||||
class TestFirewallCallbacks(test_db_firewall.FirewallPluginDbTestCase):
|
||||
class FirewallTestExtensionManager(test_l3_plugin.L3TestExtensionManager):
|
||||
|
||||
def get_resources(self):
|
||||
res = super(FirewallTestExtensionManager, self).get_resources()
|
||||
firewall.RESOURCE_ATTRIBUTE_MAP['firewalls'].update(
|
||||
firewallrouterinsertion.EXTENDED_ATTRIBUTES_2_0['firewalls'])
|
||||
return res + firewall.Firewall.get_resources()
|
||||
|
||||
def get_actions(self):
|
||||
return []
|
||||
|
||||
def get_request_extensions(self):
|
||||
return []
|
||||
|
||||
|
||||
class TestFirewallRouterInsertionBase(
|
||||
test_db_firewall.FirewallPluginDbTestCase,
|
||||
testlib_plugin.NotificationSetupHelper):
|
||||
|
||||
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)
|
||||
self.agentapi_del_fw_p.start()
|
||||
|
||||
plugin = None
|
||||
# the plugin without L3 support
|
||||
if not plugin:
|
||||
plugin = 'neutron.tests.unit.test_l3_plugin.TestNoL3NatPlugin'
|
||||
# the L3 service plugin
|
||||
l3_plugin = ('neutron.tests.unit.test_l3_plugin.'
|
||||
'TestL3NatServicePlugin')
|
||||
|
||||
cfg.CONF.set_override('api_extensions_path', extensions_path)
|
||||
self.saved_attr_map = {}
|
||||
for resource, attrs in attr.RESOURCE_ATTRIBUTE_MAP.iteritems():
|
||||
self.saved_attr_map[resource] = attrs.copy()
|
||||
if not fw_plugin:
|
||||
fw_plugin = FW_PLUGIN_KLASS
|
||||
service_plugins = {'l3_plugin_name': l3_plugin,
|
||||
'fw_plugin_name': fw_plugin}
|
||||
|
||||
if not ext_mgr:
|
||||
ext_mgr = FirewallTestExtensionManager()
|
||||
super(test_db_firewall.FirewallPluginDbTestCase, self).setUp(
|
||||
plugin=plugin, service_plugins=service_plugins, ext_mgr=ext_mgr)
|
||||
|
||||
self.setup_notification_driver()
|
||||
|
||||
self.l3_plugin = manager.NeutronManager.get_service_plugins().get(
|
||||
const.L3_ROUTER_NAT)
|
||||
self.plugin = manager.NeutronManager.get_service_plugins().get(
|
||||
const.FIREWALL)
|
||||
self.callbacks = self.plugin.endpoints[0]
|
||||
|
||||
def restore_attribute_map(self):
|
||||
# Remove the csrfirewallinsertion extension
|
||||
firewall.RESOURCE_ATTRIBUTE_MAP['firewalls'].pop('router_ids')
|
||||
# Restore the original RESOURCE_ATTRIBUTE_MAP
|
||||
attr.RESOURCE_ATTRIBUTE_MAP = self.saved_attr_map
|
||||
|
||||
def tearDown(self):
|
||||
self.restore_attribute_map()
|
||||
super(TestFirewallRouterInsertionBase, self).tearDown()
|
||||
|
||||
def _create_firewall(self, fmt, name, description, firewall_policy_id,
|
||||
admin_state_up=True, expected_res_status=None,
|
||||
**kwargs):
|
||||
tenant_id = kwargs.get('tenant_id', self._tenant_id)
|
||||
router_ids = kwargs.get('router_ids')
|
||||
data = {'firewall': {'name': name,
|
||||
'description': description,
|
||||
'firewall_policy_id': firewall_policy_id,
|
||||
'admin_state_up': admin_state_up,
|
||||
'tenant_id': tenant_id}}
|
||||
if router_ids is not None:
|
||||
data['firewall']['router_ids'] = router_ids
|
||||
firewall_req = self.new_create_request('firewalls', data, fmt)
|
||||
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
|
||||
|
||||
|
||||
class TestFirewallCallbacks(TestFirewallRouterInsertionBase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestFirewallCallbacks,
|
||||
|
@ -144,6 +236,8 @@ class TestFirewallCallbacks(test_db_firewall.FirewallPluginDbTestCase):
|
|||
self.plugin._make_firewall_dict_with_rules(ctx,
|
||||
fw_id)
|
||||
)
|
||||
fw_rules['add-router-ids'] = []
|
||||
fw_rules['del-router-ids'] = []
|
||||
self.assertEqual(res[0], fw_rules)
|
||||
self._compare_firewall_rule_lists(
|
||||
fwp_id, fr, res[0]['firewall_rule_list'])
|
||||
|
@ -158,6 +252,8 @@ class TestFirewallCallbacks(test_db_firewall.FirewallPluginDbTestCase):
|
|||
with self.firewall(firewall_policy_id=fwp_id, tenant_id=tenant_id,
|
||||
admin_state_up=test_db_firewall.ADMIN_STATE_UP
|
||||
) as fw:
|
||||
# router_ids is not present in the firewall db
|
||||
# but is added in the get_firewalls override by plugin
|
||||
fw_list = [fw['firewall']]
|
||||
f = self.callbacks.get_firewalls_for_tenant_without_rules
|
||||
res = f(ctx, host='dummy')
|
||||
|
@ -202,18 +298,67 @@ class TestFirewallAgentApi(base.BaseTestCase):
|
|||
self._call_test_helper('delete_firewall')
|
||||
|
||||
|
||||
class TestFirewallPluginBase(test_db_firewall.TestFirewallDBPlugin):
|
||||
class TestFirewallPluginBase(TestFirewallRouterInsertionBase,
|
||||
test_l3_plugin.L3NatTestCaseMixin):
|
||||
|
||||
def setUp(self):
|
||||
super(TestFirewallPluginBase, self).setUp(fw_plugin=FW_PLUGIN_KLASS)
|
||||
self.callbacks = self.plugin.endpoints[0]
|
||||
|
||||
def test_create_second_firewall_not_permitted(self):
|
||||
with self.firewall():
|
||||
res = self._create_firewall(
|
||||
None, 'firewall2', description='test',
|
||||
firewall_policy_id=None, admin_state_up=True)
|
||||
self.assertEqual(res.status_int, exc.HTTPConflict.code)
|
||||
def tearDown(self):
|
||||
super(TestFirewallPluginBase, self).tearDown()
|
||||
|
||||
def test_create_firewall_routers_not_specified(self):
|
||||
"""neutron firewall-create test-policy """
|
||||
with self.router(name='router1', admin_state_up=True,
|
||||
tenant_id=self._tenant_id):
|
||||
with self.router(name='router2', admin_state_up=True,
|
||||
tenant_id=self._tenant_id):
|
||||
with self.firewall() as fw1:
|
||||
self.assertEqual(const.PENDING_CREATE,
|
||||
fw1['firewall']['status'])
|
||||
|
||||
def test_create_firewall_routers_specified(self):
|
||||
"""neutron firewall-create test-policy --router-ids "r1 r2" """
|
||||
with self.router(name='router1', admin_state_up=True,
|
||||
tenant_id=self._tenant_id) as router1:
|
||||
with self.router(name='router2', admin_state_up=True,
|
||||
tenant_id=self._tenant_id) as router2:
|
||||
router_ids = [router1['router']['id'], router2['router']['id']]
|
||||
with self.firewall(router_ids=router_ids) as fw1:
|
||||
self.assertEqual(const.PENDING_CREATE,
|
||||
fw1['firewall']['status'])
|
||||
|
||||
def test_create_firewall_routers_present_empty_list_specified(self):
|
||||
"""neutron firewall-create test-policy --router-ids "" """
|
||||
with self.router(name='router1', admin_state_up=True,
|
||||
tenant_id=self._tenant_id):
|
||||
with self.router(name='router2', admin_state_up=True,
|
||||
tenant_id=self._tenant_id):
|
||||
router_ids = []
|
||||
with self.firewall(router_ids=router_ids) as fw1:
|
||||
self.assertEqual(const.INACTIVE,
|
||||
fw1['firewall']['status'])
|
||||
|
||||
def test_create_firewall_no_routers_empty_list_specified(self):
|
||||
"""neutron firewall-create test-policy --router-ids "" """
|
||||
router_ids = []
|
||||
with self.firewall(router_ids=router_ids) as fw1:
|
||||
self.assertEqual(const.INACTIVE,
|
||||
fw1['firewall']['status'])
|
||||
|
||||
def test_create_second_firewall_on_same_tenant(self):
|
||||
"""fw1 created with default routers, fw2 no routers on same tenant."""
|
||||
with self.router(name='router1', admin_state_up=True,
|
||||
tenant_id=self._tenant_id):
|
||||
with self.router(name='router2', admin_state_up=True,
|
||||
tenant_id=self._tenant_id):
|
||||
router_ids = []
|
||||
with self.firewall() as fw1:
|
||||
with self.firewall(router_ids=router_ids) as fw2:
|
||||
self.assertEqual(const.PENDING_CREATE,
|
||||
fw1['firewall']['status'])
|
||||
self.assertEqual(const.INACTIVE,
|
||||
fw2['firewall']['status'])
|
||||
|
||||
def test_create_firewall_admin_not_affected_by_other_tenant(self):
|
||||
# Create fw with admin after creating fw with other tenant
|
||||
|
@ -227,123 +372,158 @@ class TestFirewallPluginBase(test_db_firewall.TestFirewallDBPlugin):
|
|||
name = "new_firewall1"
|
||||
attrs = self._get_test_firewall_attrs(name)
|
||||
|
||||
with self.firewall_policy() as fwp:
|
||||
fwp_id = fwp['firewall_policy']['id']
|
||||
attrs['firewall_policy_id'] = fwp_id
|
||||
with self.firewall(
|
||||
firewall_policy_id=fwp_id,
|
||||
admin_state_up=test_db_firewall.ADMIN_STATE_UP
|
||||
) as firewall:
|
||||
fw_id = firewall['firewall']['id']
|
||||
res = self.callbacks.set_firewall_status(ctx, fw_id,
|
||||
with self.router(name='router1', admin_state_up=True,
|
||||
tenant_id=self._tenant_id) as router1:
|
||||
with self.firewall_policy() as fwp:
|
||||
fwp_id = fwp['firewall_policy']['id']
|
||||
attrs['firewall_policy_id'] = fwp_id
|
||||
with self.firewall(
|
||||
firewall_policy_id=fwp_id,
|
||||
admin_state_up=test_db_firewall.ADMIN_STATE_UP,
|
||||
router_ids=[router1['router']['id']]
|
||||
) as firewall:
|
||||
fw_id = firewall['firewall']['id']
|
||||
res = self.callbacks.set_firewall_status(ctx, fw_id,
|
||||
const.ACTIVE)
|
||||
data = {'firewall': {'name': name}}
|
||||
req = self.new_update_request('firewalls', data, fw_id)
|
||||
res = self.deserialize(self.fmt,
|
||||
data = {'firewall': {'name': name}}
|
||||
req = self.new_update_request('firewalls', data, fw_id)
|
||||
res = self.deserialize(self.fmt,
|
||||
req.get_response(self.ext_api))
|
||||
attrs = self._replace_firewall_status(attrs,
|
||||
attrs = self._replace_firewall_status(attrs,
|
||||
const.PENDING_CREATE,
|
||||
const.PENDING_UPDATE)
|
||||
for k, v in attrs.iteritems():
|
||||
self.assertEqual(res['firewall'][k], v)
|
||||
for k, v in attrs.iteritems():
|
||||
self.assertEqual(res['firewall'][k], v)
|
||||
|
||||
def test_update_firewall_fails_when_firewall_pending(self):
|
||||
name = "new_firewall1"
|
||||
attrs = self._get_test_firewall_attrs(name)
|
||||
|
||||
with self.firewall_policy() as fwp:
|
||||
fwp_id = fwp['firewall_policy']['id']
|
||||
attrs['firewall_policy_id'] = fwp_id
|
||||
with self.firewall(
|
||||
firewall_policy_id=fwp_id,
|
||||
admin_state_up=test_db_firewall.ADMIN_STATE_UP
|
||||
) as firewall:
|
||||
fw_id = firewall['firewall']['id']
|
||||
data = {'firewall': {'name': name}}
|
||||
req = self.new_update_request('firewalls', data, fw_id)
|
||||
res = req.get_response(self.ext_api)
|
||||
self.assertEqual(res.status_int, exc.HTTPConflict.code)
|
||||
with self.router(name='router1', admin_state_up=True,
|
||||
tenant_id=self._tenant_id) as router1:
|
||||
with self.firewall_policy() as fwp:
|
||||
fwp_id = fwp['firewall_policy']['id']
|
||||
attrs['firewall_policy_id'] = fwp_id
|
||||
with self.firewall(
|
||||
firewall_policy_id=fwp_id,
|
||||
admin_state_up=test_db_firewall.ADMIN_STATE_UP,
|
||||
router_ids=[router1['router']['id']]
|
||||
) as firewall:
|
||||
fw_id = firewall['firewall']['id']
|
||||
data = {'firewall': {'name': name}}
|
||||
req = self.new_update_request('firewalls', data, fw_id)
|
||||
res = req.get_response(self.ext_api)
|
||||
self.assertEqual(exc.HTTPConflict.code, res.status_int)
|
||||
|
||||
def test_update_firewall_with_router_when_firewall_inactive(self):
|
||||
name = "firewall1"
|
||||
attrs = self._get_test_firewall_attrs(name)
|
||||
|
||||
with self.router(name='router1', admin_state_up=True,
|
||||
tenant_id=self._tenant_id) as router1:
|
||||
with self.firewall_policy() as fwp:
|
||||
fwp_id = fwp['firewall_policy']['id']
|
||||
attrs['firewall_policy_id'] = fwp_id
|
||||
with self.firewall(
|
||||
name=name,
|
||||
firewall_policy_id=fwp_id,
|
||||
admin_state_up=test_db_firewall.ADMIN_STATE_UP,
|
||||
router_ids=[]
|
||||
) as firewall:
|
||||
fw_id = firewall['firewall']['id']
|
||||
data = {
|
||||
'firewall': {'router_ids': [router1['router']['id']]}}
|
||||
req = self.new_update_request('firewalls', data, fw_id)
|
||||
res = self.deserialize(self.fmt,
|
||||
req.get_response(self.ext_api))
|
||||
attrs = self._replace_firewall_status(attrs,
|
||||
const.PENDING_CREATE,
|
||||
const.PENDING_UPDATE)
|
||||
for k, v in attrs.iteritems():
|
||||
self.assertEqual(res['firewall'][k], v)
|
||||
|
||||
def test_update_firewall_shared_fails_for_non_admin(self):
|
||||
ctx = context.get_admin_context()
|
||||
with self.firewall_policy() as fwp:
|
||||
fwp_id = fwp['firewall_policy']['id']
|
||||
with self.firewall(
|
||||
firewall_policy_id=fwp_id,
|
||||
admin_state_up=test_db_firewall.ADMIN_STATE_UP,
|
||||
tenant_id='noadmin'
|
||||
) as firewall:
|
||||
fw_id = firewall['firewall']['id']
|
||||
self.callbacks.set_firewall_status(ctx, fw_id,
|
||||
with self.router(name='router1', admin_state_up=True,
|
||||
tenant_id=self._tenant_id) as router1:
|
||||
with self.firewall_policy() as fwp:
|
||||
fwp_id = fwp['firewall_policy']['id']
|
||||
with self.firewall(
|
||||
firewall_policy_id=fwp_id,
|
||||
admin_state_up=test_db_firewall.ADMIN_STATE_UP,
|
||||
tenant_id='noadmin',
|
||||
router_ids=[router1['router']['id']]
|
||||
) as firewall:
|
||||
fw_id = firewall['firewall']['id']
|
||||
self.callbacks.set_firewall_status(ctx, fw_id,
|
||||
const.ACTIVE)
|
||||
data = {'firewall': {'shared': True}}
|
||||
req = self.new_update_request(
|
||||
'firewalls', data, fw_id,
|
||||
context=context.Context('', 'noadmin'))
|
||||
res = req.get_response(self.ext_api)
|
||||
self.assertEqual(res.status_int, exc.HTTPForbidden.code)
|
||||
data = {'firewall': {'shared': True}}
|
||||
req = self.new_update_request(
|
||||
'firewalls', data, fw_id,
|
||||
context=context.Context('', 'noadmin'))
|
||||
res = req.get_response(self.ext_api)
|
||||
self.assertEqual(exc.HTTPForbidden.code, res.status_int)
|
||||
|
||||
def test_update_firewall_policy_fails_when_firewall_pending(self):
|
||||
name = "new_firewall1"
|
||||
attrs = self._get_test_firewall_attrs(name)
|
||||
|
||||
with self.firewall_policy() as fwp:
|
||||
fwp_id = fwp['firewall_policy']['id']
|
||||
attrs['firewall_policy_id'] = fwp_id
|
||||
with self.firewall(
|
||||
firewall_policy_id=fwp_id,
|
||||
admin_state_up=test_db_firewall.ADMIN_STATE_UP
|
||||
):
|
||||
data = {'firewall_policy': {'name': name}}
|
||||
req = self.new_update_request('firewall_policies',
|
||||
data, fwp_id)
|
||||
res = req.get_response(self.ext_api)
|
||||
self.assertEqual(res.status_int, exc.HTTPConflict.code)
|
||||
|
||||
def test_update_firewall_rule_fails_when_firewall_pending(self):
|
||||
with self.firewall_rule(name='fwr1') as fr:
|
||||
with self.router(name='router1', admin_state_up=True,
|
||||
tenant_id=self._tenant_id):
|
||||
with self.firewall_policy() as fwp:
|
||||
fwp_id = fwp['firewall_policy']['id']
|
||||
fr_id = fr['firewall_rule']['id']
|
||||
fw_rule_ids = [fr_id]
|
||||
data = {'firewall_policy':
|
||||
{'firewall_rules': fw_rule_ids}}
|
||||
req = self.new_update_request('firewall_policies', data,
|
||||
fwp_id)
|
||||
req.get_response(self.ext_api)
|
||||
attrs['firewall_policy_id'] = fwp_id
|
||||
with self.firewall(
|
||||
firewall_policy_id=fwp_id,
|
||||
admin_state_up=test_db_firewall.ADMIN_STATE_UP
|
||||
):
|
||||
data = {'firewall_rule': {'protocol': 'udp'}}
|
||||
req = self.new_update_request('firewall_rules',
|
||||
data, fr_id)
|
||||
data = {'firewall_policy': {'name': name}}
|
||||
req = self.new_update_request('firewall_policies',
|
||||
data, fwp_id)
|
||||
res = req.get_response(self.ext_api)
|
||||
self.assertEqual(res.status_int, exc.HTTPConflict.code)
|
||||
self.assertEqual(exc.HTTPConflict.code, res.status_int)
|
||||
|
||||
def test_delete_firewall(self):
|
||||
def test_update_firewall_rule_fails_when_firewall_pending(self):
|
||||
with self.router(name='router1', admin_state_up=True,
|
||||
tenant_id=self._tenant_id):
|
||||
with self.firewall_rule(name='fwr1') as fr:
|
||||
with self.firewall_policy() as fwp:
|
||||
fwp_id = fwp['firewall_policy']['id']
|
||||
fr_id = fr['firewall_rule']['id']
|
||||
fw_rule_ids = [fr_id]
|
||||
data = {'firewall_policy':
|
||||
{'firewall_rules': fw_rule_ids}}
|
||||
req = self.new_update_request('firewall_policies', data,
|
||||
fwp_id)
|
||||
req.get_response(self.ext_api)
|
||||
with self.firewall(
|
||||
firewall_policy_id=fwp_id,
|
||||
admin_state_up=test_db_firewall.ADMIN_STATE_UP
|
||||
):
|
||||
data = {'firewall_rule': {'protocol': 'udp'}}
|
||||
req = self.new_update_request('firewall_rules',
|
||||
data, fr_id)
|
||||
res = req.get_response(self.ext_api)
|
||||
self.assertEqual(exc.HTTPConflict.code, res.status_int)
|
||||
|
||||
def test_delete_firewall_with_no_routers(self):
|
||||
ctx = context.get_admin_context()
|
||||
attrs = self._get_test_firewall_attrs()
|
||||
# stop the AgentRPC patch for this one to test pending states
|
||||
self.agentapi_delf_p.stop()
|
||||
self.agentapi_del_fw_p.stop()
|
||||
with self.firewall_policy() as fwp:
|
||||
fwp_id = fwp['firewall_policy']['id']
|
||||
attrs['firewall_policy_id'] = fwp_id
|
||||
with self.firewall(
|
||||
firewall_policy_id=fwp_id,
|
||||
admin_state_up=test_db_firewall.ADMIN_STATE_UP
|
||||
) as firewall:
|
||||
fw_id = firewall['firewall']['id']
|
||||
attrs = self._replace_firewall_status(attrs,
|
||||
const.PENDING_CREATE,
|
||||
const.PENDING_DELETE)
|
||||
admin_state_up=test_db_firewall.ADMIN_STATE_UP,
|
||||
do_delete=False
|
||||
) as fw:
|
||||
fw_id = fw['firewall']['id']
|
||||
req = self.new_delete_request('firewalls', fw_id)
|
||||
req.get_response(self.ext_api)
|
||||
fw_db = self.plugin._get_firewall(ctx, fw_id)
|
||||
for k, v in attrs.iteritems():
|
||||
self.assertEqual(fw_db[k], v)
|
||||
# cleanup the pending firewall
|
||||
self.plugin.endpoints[0].firewall_deleted(ctx, fw_id)
|
||||
res = req.get_response(self.ext_api)
|
||||
self.assertEqual(res.status_int, exc.HTTPNoContent.code)
|
||||
self.assertRaises(firewall.FirewallNotFound,
|
||||
self.plugin.get_firewall,
|
||||
ctx, fw_id)
|
||||
|
||||
def test_delete_firewall_after_agent_delete(self):
|
||||
ctx = context.get_admin_context()
|
||||
|
@ -376,7 +556,8 @@ class TestFirewallPluginBase(test_db_firewall.TestFirewallDBPlugin):
|
|||
attrs['firewall_policy_id'] = fwp_id
|
||||
with self.firewall(
|
||||
firewall_policy_id=fwp_id,
|
||||
admin_state_up=test_db_firewall.ADMIN_STATE_UP
|
||||
admin_state_up=test_db_firewall.ADMIN_STATE_UP,
|
||||
router_ids=[]
|
||||
) as fw:
|
||||
fw_id = fw['firewall']['id']
|
||||
fw_rules = (
|
||||
|
@ -392,7 +573,7 @@ class TestFirewallPluginBase(test_db_firewall.TestFirewallDBPlugin):
|
|||
with self.firewall() as fw:
|
||||
fw_id = fw['firewall']['id']
|
||||
fw_rules = self.plugin._make_firewall_dict_with_rules(ctx, fw_id)
|
||||
self.assertEqual(fw_rules['firewall_rule_list'], [])
|
||||
self.assertEqual([], fw_rules['firewall_rule_list'])
|
||||
|
||||
def test_list_firewalls(self):
|
||||
with self.firewall_policy() as fwp:
|
||||
|
|
Loading…
Reference in New Issue