FWaaS v2 extension for L2 agent
This patch adds L2 agent extension for FWaaS v2 to handle create/update/delete firewall groups on ports. It also handles applying firewall group on port, when a port is added/created/deleted. DocImpact Depends-On: Ifd6758617ab8fd49e69ad1a0483fefa479d7b8e7 Co-Authored-By: Paddu Krishnan <kprad1@yahoo.com> Co-Authored-By: Chandan Dutta Chowdhury <chandanc@juniper.net> Co-Authored-By: Nguyen Phuong An <AnNP@vn.fujitsu.com>¬ Co-Authored-By: Inessa Vasilevskaya <ivasilevskaya@mirantis.com> Partial-Implements: blueprint fwaas-api-2.0 Change-Id: I9f172be46ee590b99313106fa262019a2583774a
This commit is contained in:
parent
74eac2ca29
commit
dbac4b8922
|
@ -46,6 +46,8 @@ function configure_fwaas_v2() {
|
|||
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
|
||||
iniset $NEUTRON_CORE_PLUGIN_CONF fwaas firewall_l2_driver $FW_L2_DRIVER
|
||||
iniset $NEUTRON_CORE_PLUGIN_CONF agent extensions fwaas_v2
|
||||
}
|
||||
|
||||
function neutron_fwaas_generate_config_files {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
FWAAS_DRIVER_V1=${FWAAS_DRIVER_V1:-iptables}
|
||||
FWAAS_DRIVER_V2=${FWAAS_DRIVER_V2:-iptables_v2}
|
||||
FW_L2_DRIVER=${FW_L2_DRIVER:-noop}
|
||||
FWAAS_PLUGIN_V1=${FWAAS_PLUGIN:-firewall}
|
||||
FWAAS_PLUGIN_V2=${FWAAS_PLUGIN:-firewall_v2}
|
||||
|
||||
|
|
|
@ -25,3 +25,12 @@ FIREWALL_RULE_LIST = 'firewall_rule_list'
|
|||
DEFAULT_FWG = 'default'
|
||||
DEFAULT_FWP_INGRESS = 'default ingress'
|
||||
DEFAULT_FWP_EGRESS = 'default egress'
|
||||
|
||||
# Firewall group events for agent-side
|
||||
DELETE_FWG = 'delete_firewall_group'
|
||||
UPDATE_FWG = 'update_firewall_group'
|
||||
CREATE_FWG = 'create_firewall_group'
|
||||
|
||||
# Port events for L2 agent extension
|
||||
HANDLE_PORT = 'handle_port'
|
||||
DELETE_PORT = 'delete_port'
|
||||
|
|
|
@ -1079,3 +1079,16 @@ class Firewall_db_mixin_v2(fw_ext.Firewallv2PluginBase, base_db.CommonDbMixin):
|
|||
return self._get_collection(context, FirewallGroup,
|
||||
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)
|
||||
|
|
|
@ -22,7 +22,7 @@ from neutron_fwaas._i18n import _
|
|||
|
||||
FWAAS_V1 = "v1"
|
||||
FWAAS_V2 = "v2"
|
||||
|
||||
FW_L2_NOOP_DRIVER = 'noop'
|
||||
|
||||
FWaaSOpts = [
|
||||
cfg.StrOpt(
|
||||
|
@ -35,12 +35,17 @@ FWaaSOpts = [
|
|||
help=_("Enable FWaaS")),
|
||||
cfg.StrOpt(
|
||||
'agent_version',
|
||||
default=FWAAS_V1,
|
||||
default=FWAAS_V2,
|
||||
help=_("Firewall agent class")),
|
||||
cfg.StrOpt(
|
||||
'conntrack_driver',
|
||||
default='conntrack',
|
||||
help=_("Name of the FWaaS Conntrack Driver")),
|
||||
cfg.StrOpt(
|
||||
'firewall_l2_driver',
|
||||
default=FW_L2_NOOP_DRIVER,
|
||||
help=_("Name of the firewall l2 driver")
|
||||
)
|
||||
]
|
||||
cfg.CONF.register_opts(FWaaSOpts, 'fwaas')
|
||||
|
||||
|
|
|
@ -0,0 +1,449 @@
|
|||
# Copyright 2017 FUJITSU LIMITED
|
||||
#
|
||||
# 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 oslo_concurrency import lockutils
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
import six
|
||||
|
||||
from neutron.agent import securitygroups_rpc
|
||||
from neutron.common import rpc as n_rpc
|
||||
from neutron import manager
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent import vlanmanager
|
||||
from neutron_lib.agent import l2_extension
|
||||
from neutron_lib import constants as nl_const
|
||||
from neutron_lib.exceptions import firewall_v2 as f_exc
|
||||
|
||||
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
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
FWAAS_L2_DRIVER = 'neutron.agent.l2.firewall_drivers'
|
||||
|
||||
|
||||
class FWaaSL2PluginApi(api.FWaaSPluginApiMixin):
|
||||
"""L2 agent side of FWaaS agent-to-plugin RPC API"""
|
||||
|
||||
def get_firewall_group_for_port(self, context, port_id):
|
||||
"""Get firewall group is associated with a port"""
|
||||
|
||||
LOG.debug("Get firewall group is associated with port %s", port_id)
|
||||
cctxt = self.client.prepare()
|
||||
return cctxt.call(context, 'get_firewall_group_for_port',
|
||||
port_id=port_id)
|
||||
|
||||
def set_firewall_group_status(self, context, fwg_id, status, host):
|
||||
"""Set the status of a group operation."""
|
||||
|
||||
LOG.debug("Fetch firewall group changing status")
|
||||
cctxt = self.client.prepare()
|
||||
return cctxt.call(context, 'set_firewall_group_status',
|
||||
fwg_id=fwg_id, status=status, host=host)
|
||||
|
||||
def firewall_group_deleted(self, context, fwg_id, host):
|
||||
"""Notifies the plugin that a firewall group has been deleted."""
|
||||
|
||||
LOG.debug("Notify to the plugin that firewall group has been deleted")
|
||||
cctxt = self.client.prepare()
|
||||
return cctxt.call(context, 'firewall_group_deleted',
|
||||
fwg_id=fwg_id, host=host)
|
||||
|
||||
|
||||
class FWaaSV2AgentExtension(l2_extension.L2AgentExtension):
|
||||
|
||||
def initialize(self, connection, driver_type):
|
||||
"""Perform Agent Extension initialization"""
|
||||
|
||||
self.conf = cfg.CONF
|
||||
int_br = self.agent_api.request_int_br()
|
||||
self.vlan_manager = vlanmanager.LocalVlanManager()
|
||||
fw_l2_driver_cls = self._load_l2_driver_class(driver_type)
|
||||
sg_enabled = securitygroups_rpc.is_firewall_enabled()
|
||||
self.driver = manager.NeutronManager.load_class_for_provider(
|
||||
FWAAS_L2_DRIVER, fw_l2_driver_cls)(int_br, sg_enabled)
|
||||
self.plugin_rpc = FWaaSL2PluginApi(
|
||||
consts.FIREWALL_PLUGIN, self.conf.host)
|
||||
self.start_rpc_listeners()
|
||||
self.fwg_map = PortFirewallGroupMap()
|
||||
|
||||
def consume_api(self, agent_api):
|
||||
self.agent_api = agent_api
|
||||
|
||||
def start_rpc_listeners(self):
|
||||
self.conn = n_rpc.create_connection()
|
||||
endpoints = [self]
|
||||
self.conn.create_consumer(consts.FW_AGENT, endpoints, fanout=False)
|
||||
return self.conn.consume_in_threads()
|
||||
|
||||
def _load_l2_driver_class(self, driver_type):
|
||||
driver = self.conf.fwaas.firewall_l2_driver or 'noop'
|
||||
if driver == api.FW_L2_NOOP_DRIVER:
|
||||
return driver
|
||||
|
||||
if driver != driver_type:
|
||||
raise Exception(
|
||||
_("Firewall l2 driver: %s is not compatible"), driver_type)
|
||||
return driver
|
||||
|
||||
def _is_port_layer2(self, port):
|
||||
"""This function checks if a port belongs to a L2 case.
|
||||
|
||||
Currently both DHCP and router ports are eliminated.
|
||||
"""
|
||||
|
||||
return port and port.get('device_owner', '').startswith(
|
||||
nl_const.DEVICE_OWNER_COMPUTE_PREFIX)
|
||||
|
||||
def _get_firewall_group_ports(self, fwg, host, to_delete=False):
|
||||
port_list = []
|
||||
port_ids = fwg['del-port-ids'] if to_delete else fwg['add-port-ids']
|
||||
|
||||
LOG.debug("_get_fwg fwg=%(fwg)s ports=%(port)s to_delete=%(delete)s",
|
||||
{'fwg': fwg, 'port': port_ids, 'delete': to_delete})
|
||||
for fw_port in port_ids:
|
||||
port_detail = fwg['port_details'].get(fw_port)
|
||||
if (self._is_port_layer2(port_detail) and
|
||||
port_detail.get('host') == host):
|
||||
port_list.append(port_detail)
|
||||
return port_list
|
||||
|
||||
@staticmethod
|
||||
def _has_ports(fwg, event):
|
||||
"""Verifying fwg has ports or not
|
||||
|
||||
This function verify applying firewall group on ports
|
||||
:param fwg: a fwg object
|
||||
:param event: create/update firewall group or
|
||||
create/update/delete port
|
||||
:return: True if applying firewall group is fine. Otherwise is False
|
||||
"""
|
||||
if event == consts.UPDATE_FWG and 'last-port' in fwg:
|
||||
return not fwg['last-port']
|
||||
else:
|
||||
return bool(fwg['ports'])
|
||||
|
||||
@staticmethod
|
||||
def _has_policy(fwg):
|
||||
"""Verifying fwg has policy or not"""
|
||||
return bool(fwg['ingress_firewall_policy_id'] or
|
||||
fwg['egress_firewall_policy_id'])
|
||||
|
||||
def _compute_status(self, fwg, result, event=consts.CREATE_FWG):
|
||||
"""Compute a status of specified firewall group for update
|
||||
|
||||
Validates 'ACTIVE', 'DOWN', 'INACTIVE', 'ERROR' and None as follows:
|
||||
- "ERROR" : if result is not True
|
||||
- "ACTIVE" : admin_state_up is True and exists ports
|
||||
- "INACTIVE" : admin_state_up is True and with no ports
|
||||
- "DOWN" : admin_state_up is False
|
||||
- None : In case of 'delete_firewall_group'
|
||||
"""
|
||||
if not result:
|
||||
return nl_const.ERROR
|
||||
|
||||
if not fwg['admin_state_up']:
|
||||
return nl_const.DOWN
|
||||
|
||||
if event == consts.DELETE_FWG:
|
||||
# This firewall_group will be deleted. No need to update status.
|
||||
return
|
||||
|
||||
if (self._has_ports(fwg, event) and self._has_policy(fwg)):
|
||||
return nl_const.ACTIVE
|
||||
|
||||
return nl_const.INACTIVE
|
||||
|
||||
def _get_network_id(self, fwg_port):
|
||||
port_id = fwg_port.get('port_id', fwg_port.get('id'))
|
||||
port_details = fwg_port.get('port_details')
|
||||
|
||||
if port_details:
|
||||
target = port_details.get(port_id)
|
||||
if target:
|
||||
return target.get('network_id')
|
||||
return
|
||||
|
||||
return fwg_port.get('network_id')
|
||||
|
||||
def _add_local_vlan_to_ports(self, fwg_ports):
|
||||
"""Add local VLAN to ports if found
|
||||
|
||||
This function tries to add local VLAN related to ports.
|
||||
"""
|
||||
|
||||
ports_with_lvlan = []
|
||||
for fwg_port in fwg_ports:
|
||||
try:
|
||||
network_id = self._get_network_id(fwg_port)
|
||||
l_vlan = self.vlan_manager.get(network_id).vlan
|
||||
fwg_port['lvlan'] = int(l_vlan)
|
||||
except vlanmanager.MappingNotFound:
|
||||
LOG.warning("No Local VLAN found in network %s", network_id)
|
||||
# NOTE(yushiro): We ignore this exception because we should send
|
||||
# all selected ports to driver layer. It depends on driver's
|
||||
# behavior whether it occurs an error with no local VLAN or not.
|
||||
ports_with_lvlan.append(fwg_port)
|
||||
|
||||
return ports_with_lvlan
|
||||
|
||||
def _apply_fwg_rules(self, fwg, ports, event=consts.UPDATE_FWG):
|
||||
"""This function invokes the driver create/update routine. """
|
||||
# Set firewall group status; will be overwritten if call to driver
|
||||
# fails.
|
||||
if event in [consts.CREATE_FWG, consts.UPDATE_FWG]:
|
||||
ports_for_driver = self._add_local_vlan_to_ports(ports)
|
||||
else:
|
||||
ports_for_driver = ports
|
||||
|
||||
# apply firewall group to driver
|
||||
try:
|
||||
if event == consts.UPDATE_FWG:
|
||||
self.driver.update_firewall_group(ports_for_driver, fwg)
|
||||
elif event == consts.DELETE_FWG:
|
||||
self.driver.delete_firewall_group(ports_for_driver, fwg)
|
||||
elif event == consts.CREATE_FWG:
|
||||
self.driver.create_firewall_group(ports_for_driver, fwg)
|
||||
except f_exc.FirewallInternalDriverError:
|
||||
msg = _("FWaaS driver error in %(event)s_firewall_group "
|
||||
"for firewall group: %(fwg_id)s")
|
||||
LOG.exception(msg, {'event': event, 'fwg_id': fwg['id']})
|
||||
return False
|
||||
return True
|
||||
|
||||
def _send_fwg_status(self, context, fwg_id, status, host):
|
||||
"""Send firewall group's status to plugin.
|
||||
|
||||
:returns: True if no exception occurred otherwise False
|
||||
:rtype: boolean
|
||||
"""
|
||||
try:
|
||||
self.plugin_rpc.set_firewall_group_status(
|
||||
context, fwg_id, status, host)
|
||||
LOG.debug("Successfully sent status(%s) for firewall_group(%s)",
|
||||
status, fwg_id)
|
||||
except Exception:
|
||||
msg = _("Failed to send status for firewall_group(%s)")
|
||||
LOG.exception(msg, fwg_id)
|
||||
|
||||
def _create_firewall_group(self, context, fwg, host,
|
||||
event=consts.CREATE_FWG):
|
||||
"""Handles RPC from plugin to create a firewall group. """
|
||||
|
||||
add_ports = self._get_firewall_group_ports(fwg, host)
|
||||
if not add_ports:
|
||||
status = nl_const.INACTIVE
|
||||
else:
|
||||
ret = self._apply_fwg_rules(fwg, add_ports, event)
|
||||
|
||||
# cleanup port_map
|
||||
for port in add_ports:
|
||||
self.fwg_map.remove_port(port)
|
||||
|
||||
status = self._compute_status(fwg, ret, event)
|
||||
for port in add_ports:
|
||||
self.fwg_map.set_port_fwg(port, fwg)
|
||||
# Update status of firewall group which is associated with ports
|
||||
# after updating.
|
||||
self._send_fwg_status(context, fwg['id'], status, host)
|
||||
|
||||
def _delete_firewall_group(self, context, fwg, host,
|
||||
event=consts.DELETE_FWG):
|
||||
"""Handles RPC from plugin to delete a firewall group. """
|
||||
|
||||
del_ports = self._get_firewall_group_ports(fwg, host, to_delete=True)
|
||||
if not del_ports:
|
||||
return
|
||||
|
||||
# cleanup all flows of del_ports
|
||||
ret = self._apply_fwg_rules(fwg, del_ports, event=consts.DELETE_FWG)
|
||||
del_port_ids = []
|
||||
for port in del_ports:
|
||||
del_port_ids.append(port['id'])
|
||||
self.fwg_map.remove_port(port)
|
||||
|
||||
if event == consts.DELETE_FWG:
|
||||
self.fwg_map.remove_fwg(fwg)
|
||||
self.plugin_rpc.firewall_group_deleted(
|
||||
context, fwg['id'], host=self.conf.host)
|
||||
else:
|
||||
status = self._compute_status(fwg, ret, event)
|
||||
self._send_fwg_status(context, fwg['id'], status, self.conf.host)
|
||||
|
||||
@lockutils.synchronized('fwg')
|
||||
def create_firewall_group(self, context, firewall_group, host):
|
||||
"""Handles create firewall group event"""
|
||||
with self.driver.defer_apply():
|
||||
try:
|
||||
self._create_firewall_group(context, firewall_group, host)
|
||||
except Exception as exc:
|
||||
LOG.exception(
|
||||
"Exception caught in create_firewall_group %s", exc)
|
||||
self._send_fwg_status(context, firewall_group['id'],
|
||||
status=nl_const.ERROR, host=host)
|
||||
|
||||
@lockutils.synchronized('fwg')
|
||||
def delete_firewall_group(self, context, firewall_group, host):
|
||||
"""Handles delete firewall group event"""
|
||||
with self.driver.defer_apply():
|
||||
try:
|
||||
self._delete_firewall_group(context, firewall_group, host)
|
||||
except Exception as exc:
|
||||
LOG.exception(
|
||||
"Exception caught in delete_firewall_group %s", exc)
|
||||
self._send_fwg_status(context, firewall_group['id'],
|
||||
status=nl_const.ERROR, host=host)
|
||||
|
||||
@lockutils.synchronized('fwg')
|
||||
def update_firewall_group(self, context, firewall_group, host):
|
||||
"""Handles update firewall group event"""
|
||||
with self.driver.defer_apply():
|
||||
try:
|
||||
self._delete_firewall_group(
|
||||
context, firewall_group, host, event=consts.UPDATE_FWG)
|
||||
self._create_firewall_group(
|
||||
context, firewall_group, host, event=consts.UPDATE_FWG)
|
||||
except Exception as exc:
|
||||
LOG.exception(
|
||||
"Exception caught in update_firewall_group %s", exc)
|
||||
self._send_fwg_status(context, firewall_group['id'],
|
||||
status=nl_const.ERROR, host=host)
|
||||
|
||||
@lockutils.synchronized('fwg-port')
|
||||
def handle_port(self, context, port):
|
||||
"""Handle port update event"""
|
||||
|
||||
if not self._is_port_layer2(port):
|
||||
return
|
||||
|
||||
# check if port is already assigned to a fwg
|
||||
if self.fwg_map.get_port_fwg(port):
|
||||
return
|
||||
|
||||
fwg = self.plugin_rpc.get_firewall_group_for_port(
|
||||
context, port.get('port_id'))
|
||||
if not fwg:
|
||||
LOG.info("Firewall group applied to port %s is "
|
||||
"not available on server.", port['port_id'])
|
||||
return
|
||||
|
||||
ret = self._apply_fwg_rules(fwg, [port])
|
||||
status = self._compute_status(fwg, ret, event=consts.HANDLE_PORT)
|
||||
self.fwg_map.set_port_fwg(port, fwg)
|
||||
self._send_fwg_status(
|
||||
context, fwg_id=fwg['id'], status=status, host=self.conf.host)
|
||||
|
||||
def delete_port(self, context, port):
|
||||
"""This is being called when a port is deleted by the agent. """
|
||||
|
||||
# delete_port should be handled only unbound timing for a port.
|
||||
# If 'vif_port' is included in the port dict, this is called after
|
||||
# deleted the port and should be ignored.
|
||||
if 'vif_port' in port:
|
||||
return
|
||||
|
||||
port = self.fwg_map.get_port(port)
|
||||
if not self._is_port_layer2(port):
|
||||
return
|
||||
|
||||
fwg = self.fwg_map.get_port_fwg(port)
|
||||
if not fwg:
|
||||
LOG.info("Firewall group associated to port %(port_id)s is "
|
||||
"not available on server.", {'port_id': port['port_id']})
|
||||
return
|
||||
|
||||
ret = self._apply_fwg_rules(fwg, [port], event=consts.DELETE_FWG)
|
||||
|
||||
port_id = self.fwg_map.port_id(port)
|
||||
if port_id in fwg['ports']:
|
||||
fwg['ports'].remove(port_id)
|
||||
|
||||
# update the fwg dict to known_fwgs
|
||||
self.fwg_map.set_fwg(fwg)
|
||||
self.fwg_map.remove_port(port)
|
||||
status = self._compute_status(fwg, ret, event=consts.DELETE_PORT)
|
||||
self._send_fwg_status(context, fwg['id'], status, self.conf.host)
|
||||
|
||||
|
||||
class PortFirewallGroupMap(object):
|
||||
"""Store relations between Port and FirewallGroup
|
||||
|
||||
This map is used in deleting firewall_group because the firewall_group has
|
||||
been deleted at that time. Therefore, it is impossible to refer 'ports'.
|
||||
This map enables to refer 'ports' for specified firewall_group.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.known_fwgs = {}
|
||||
self.port_fwg = {}
|
||||
self.port_detail = {}
|
||||
# TODO(yushiro): If agent is restarted, this map doesn't have any
|
||||
# information. Need to consider map initialization in __init__()
|
||||
|
||||
def port_id(self, port):
|
||||
return (port if isinstance(port, six.string_types)
|
||||
else port.get('port_id', port.get('id')))
|
||||
|
||||
def get_fwg(self, fwg_id):
|
||||
return self.known_fwgs.get(fwg_id)
|
||||
|
||||
def set_fwg(self, fwg):
|
||||
self.known_fwgs[fwg['id']] = fwg
|
||||
|
||||
def get_port(self, port):
|
||||
return self.port_detail.get(self.port_id(port))
|
||||
|
||||
def get_port_fwg(self, port):
|
||||
fwg_id = self.port_fwg.get(self.port_id(port))
|
||||
if fwg_id:
|
||||
return self.get_fwg(fwg_id)
|
||||
|
||||
def set_port_fwg(self, port, fwg):
|
||||
"""Add a new port into fwg['ports']"""
|
||||
port_id = self.port_id(port)
|
||||
# Update fwg['ports'] data
|
||||
fwg['ports'] = list(set(fwg['ports'] + [port_id]))
|
||||
# Update fwg_id -> firewall_group data
|
||||
self.known_fwgs[fwg['id']] = fwg
|
||||
# Update port_id -> port data
|
||||
self.port_detail[port_id] = port
|
||||
# Update port_id -> firewall_group_id relation
|
||||
self.port_fwg[port_id] = fwg['id']
|
||||
|
||||
def remove_port(self, port):
|
||||
"""Remove port from fwg['ports'] and port_fwg dictionary
|
||||
|
||||
When removing 'port' from several cases, the port should be removed
|
||||
from this map.
|
||||
"""
|
||||
port_id = self.port_id(port)
|
||||
# Check if 'port_id' has registered in port_fwg dictionary.
|
||||
# Update firewall_group
|
||||
if port_id in self.port_fwg:
|
||||
fwg_id = self.port_fwg.get(port_id)
|
||||
if not fwg_id:
|
||||
return
|
||||
new_fwg = self.known_fwgs[fwg_id]
|
||||
new_fwg['ports'] = [p for p in new_fwg['ports'] if p != port_id]
|
||||
self.known_fwgs[fwg_id] = new_fwg
|
||||
del self.port_fwg[port_id]
|
||||
del self.port_detail[port_id]
|
||||
|
||||
def remove_fwg(self, fwg):
|
||||
"""Remove firewall_group from known_fwgs dictionary
|
||||
|
||||
When removing firewall_group, it should be removed from this map
|
||||
"""
|
||||
if fwg['id'] in self.known_fwgs:
|
||||
del self.known_fwgs[fwg['id']]
|
|
@ -402,12 +402,21 @@ class FWaaSL3AgentExtension(l3_extension.L3AgentExtension):
|
|||
|
||||
# Get the list of in-namespace ports from which to delete the firewall
|
||||
# group.
|
||||
ports_for_fwg = self._get_firewall_group_ports(context, firewall_group,
|
||||
to_delete=True, require_new_plugin=True)
|
||||
del_fwg_ports = self._get_firewall_group_ports(
|
||||
context, firewall_group, to_delete=True, require_new_plugin=True)
|
||||
add_fwg_ports = self._get_firewall_group_ports(context, firewall_group)
|
||||
|
||||
port_ids = (firewall_group.get('del-port-ids') +
|
||||
firewall_group.get('add-port-ids'))
|
||||
|
||||
if port_ids and not (del_fwg_ports or add_fwg_ports):
|
||||
LOG.debug("All ports are not router port."
|
||||
"No need to update firewall driver.")
|
||||
return
|
||||
|
||||
# Remove firewall group from ports if requested.
|
||||
if ports_for_fwg:
|
||||
fw_ports = [p for ri_ports in ports_for_fwg for p in ri_ports[1]]
|
||||
if del_fwg_ports:
|
||||
fw_ports = [p for ri_port in del_fwg_ports for p in ri_port[1]]
|
||||
LOG.debug("Update (delete) firewall group %(fwg_id)s on ports: "
|
||||
"%(ports)s",
|
||||
{'fwg_id': firewall_group['id'],
|
||||
|
@ -426,7 +435,7 @@ class FWaaSL3AgentExtension(l3_extension.L3AgentExtension):
|
|||
# Call the driver.
|
||||
try:
|
||||
self.fwaas_driver.delete_firewall_group(self.conf.agent_mode,
|
||||
ports_for_fwg,
|
||||
del_fwg_ports,
|
||||
firewall_group)
|
||||
except fw_ext.FirewallInternalDriverError:
|
||||
msg = ("FWaaS driver error in update_firewall_group "
|
||||
|
@ -437,11 +446,9 @@ class FWaaSL3AgentExtension(l3_extension.L3AgentExtension):
|
|||
# Handle the add router and/or rule, policy, firewall group attribute
|
||||
# updates.
|
||||
if status not in (nl_constants.ERROR, nl_constants.INACTIVE):
|
||||
ports_for_fwg = self._get_firewall_group_ports(context,
|
||||
firewall_group)
|
||||
if ports_for_fwg:
|
||||
fw_ports = [p for ri_ports in ports_for_fwg
|
||||
for p in ri_ports[1]]
|
||||
if add_fwg_ports:
|
||||
fw_ports = [p for ri_port in add_fwg_ports
|
||||
for p in ri_port[1]]
|
||||
LOG.debug("Update (create) firewall group %(fwg_id)s on "
|
||||
"ports: %(ports)s",
|
||||
{'fwg_id': firewall_group['id'],
|
||||
|
@ -457,7 +464,7 @@ class FWaaSL3AgentExtension(l3_extension.L3AgentExtension):
|
|||
# Call the driver.
|
||||
try:
|
||||
self.fwaas_driver.update_firewall_group(
|
||||
self.conf.agent_mode, ports_for_fwg,
|
||||
self.conf.agent_mode, add_fwg_ports,
|
||||
firewall_group)
|
||||
except fw_ext.FirewallInternalDriverError:
|
||||
msg = ("FWaaS driver error in update_firewall_group "
|
||||
|
|
|
@ -22,7 +22,7 @@ import six
|
|||
class FirewallL2DriverBase(object):
|
||||
"""Abstract firewall L2 driver base"""
|
||||
|
||||
def __init__(self, integration_bridge):
|
||||
def __init__(self, integration_bridge, sg_enabled=False):
|
||||
pass
|
||||
|
||||
def filter_defer_apply_on(self):
|
||||
|
|
|
@ -16,6 +16,7 @@ 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_lib.api.definitions import firewall_v2
|
||||
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
|
||||
|
@ -28,7 +29,6 @@ import oslo_messaging
|
|||
from neutron_fwaas.common import fwaas_constants
|
||||
from neutron_fwaas.db.firewall.v2 import firewall_db_v2
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
@ -142,6 +142,13 @@ class FirewallCallbacks(object):
|
|||
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'))
|
||||
|
||||
|
||||
class FirewallPluginV2(
|
||||
firewall_db_v2.Firewall_db_mixin_v2):
|
||||
|
@ -190,6 +197,8 @@ class FirewallPluginV2(
|
|||
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):
|
||||
|
@ -226,12 +235,14 @@ class FirewallPluginV2(
|
|||
# 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['device_owner'] != "network:router_interface":
|
||||
raise f_exc.FirewallGroupPortInvalid(port_id=port_id)
|
||||
if port_db['tenant_id'] != tenant_id:
|
||||
raise f_exc.FirewallGroupPortInvalidProject(
|
||||
port_id=port_id, project_id=port_db['tenant_id'])
|
||||
return
|
||||
device_owner = port_db.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)):
|
||||
raise f_exc.FirewallGroupPortInvalid(port_id=port_id)
|
||||
|
||||
def _check_no_need_pending(self, context, fwg_id, fwg_body):
|
||||
fwg_db = self._get_firewall_group(context, fwg_id)
|
||||
|
@ -245,6 +256,34 @@ class FirewallPluginV2(
|
|||
return True
|
||||
return False
|
||||
|
||||
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
|
||||
|
||||
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 create_firewall_group(self, context, firewall_group):
|
||||
LOG.debug("create_firewall_group() called")
|
||||
fwgrp = firewall_group['firewall_group']
|
||||
|
@ -280,6 +319,8 @@ class FirewallPluginV2(
|
|||
|
||||
fwg_with_rules['add-port-ids'] = fwg_ports
|
||||
fwg_with_rules['del-ports-ids'] = []
|
||||
fwg_with_rules['port_details'] = self._get_fwg_port_details(
|
||||
context, fwg_ports)
|
||||
|
||||
self.agent_rpc.create_firewall_group(context, fwg_with_rules)
|
||||
|
||||
|
@ -337,6 +378,10 @@ class FirewallPluginV2(
|
|||
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
|
||||
|
@ -346,12 +391,18 @@ class FirewallPluginV2(
|
|||
|
||||
def delete_firewall_group(self, context, id):
|
||||
LOG.debug("delete_firewall_group() called on firewall_group %s", id)
|
||||
fw_with_rules = (
|
||||
|
||||
fwg_db = self._get_firewall_group(context, id)
|
||||
|
||||
if fwg_db['status'] == nl_constants.ACTIVE:
|
||||
raise f_exc.FirewallGroupInUse(firewall_id=id)
|
||||
|
||||
fwg_with_rules = (
|
||||
self._make_firewall_group_dict_with_rules(context, id))
|
||||
fw_with_rules['del-port-ids'] = self._get_ports_in_firewall_group(
|
||||
fwg_with_rules['del-port-ids'] = self._get_ports_in_firewall_group(
|
||||
context, id)
|
||||
fw_with_rules['add-port-ids'] = []
|
||||
if not fw_with_rules['del-port-ids']:
|
||||
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:
|
||||
|
@ -359,9 +410,11 @@ class FirewallPluginV2(
|
|||
nl_constants.PENDING_DELETE}}
|
||||
super(FirewallPluginV2, self).update_firewall_group(
|
||||
context, id, status)
|
||||
# Reflect state change in fw_with_rules
|
||||
fw_with_rules['status'] = status['firewall_group']['status']
|
||||
self.agent_rpc.delete_firewall_group(context, fw_with_rules)
|
||||
# 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)
|
||||
|
||||
def update_firewall_policy(self, context, id, firewall_policy):
|
||||
LOG.debug("update_firewall_policy() called")
|
||||
|
@ -381,18 +434,16 @@ class FirewallPluginV2(
|
|||
self._rpc_update_firewall_policy(context, fwp_id)
|
||||
return fwr
|
||||
|
||||
def insert_rule(self, context, policy_id, rule_info):
|
||||
LOG.debug("insert_rule() called for policy %s ", policy_id)
|
||||
self._ensure_update_firewall_policy(context, policy_id)
|
||||
fwp = super(FirewallPluginV2, self).insert_rule(context, policy_id,
|
||||
rule_info)
|
||||
self._rpc_update_firewall_policy(context, policy_id)
|
||||
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
|
||||
|
||||
def remove_rule(self, context, policy_id, rule_info):
|
||||
LOG.debug("remove_rule() called for policy %s ", policy_id)
|
||||
self._ensure_update_firewall_policy(context, policy_id)
|
||||
fwp = super(FirewallPluginV2, self).remove_rule(context, policy_id,
|
||||
rule_info)
|
||||
self._rpc_update_firewall_policy(context, policy_id)
|
||||
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
|
||||
|
|
|
@ -272,7 +272,6 @@ class FWaaSv2ExtensionTestJSON(v2_base.BaseFWaaSTest):
|
|||
ports=[intf_1['port_id'], intf_2['port_id']])
|
||||
created_firewall_group = body['firewall_group']
|
||||
fwg_id = created_firewall_group['id']
|
||||
self.addCleanup(self._try_delete_firewall_group, fwg_id)
|
||||
|
||||
# Wait for the firewall resource to become ready
|
||||
self._wait_until_ready(fwg_id)
|
||||
|
@ -299,6 +298,8 @@ class FWaaSv2ExtensionTestJSON(v2_base.BaseFWaaSTest):
|
|||
m['ingress_firewall_policy_id'],
|
||||
m['egress_firewall_policy_id']) for m in fwgs])
|
||||
|
||||
# Disassociate all port with this firewall group
|
||||
self.firewall_groups_client.update_firewall_group(fwg_id, ports=[])
|
||||
# Delete firewall_group
|
||||
self.firewall_groups_client.delete_firewall_group(fwg_id)
|
||||
|
||||
|
|
|
@ -294,3 +294,7 @@ class TestFWaaS_v2(base.FWaaSScenarioTest_V2):
|
|||
topology['private_key2'],
|
||||
address_list=[topology['server_fixed_ip_1']],
|
||||
should_connect=False)
|
||||
|
||||
# Disassociate ports of this firewall group for cleanup resources
|
||||
self.firewall_groups_client.update_firewall_group(
|
||||
fw_group['id'], ports=[])
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
# Copyright 2017 FUJITSU LIMITED
|
||||
# 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 copy
|
||||
|
||||
import mock
|
||||
from neutron_lib import constants as nl_consts
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
TENANT_UUID = uuidutils.generate_uuid()
|
||||
TENANT_ID = TENANT_UUID
|
||||
PROJECT_ID = TENANT_UUID
|
||||
NETWORK_ID = uuidutils.generate_uuid()
|
||||
SUBNET_ID = uuidutils.generate_uuid()
|
||||
DEVICE_ID = uuidutils.generate_uuid()
|
||||
PORT1 = uuidutils.generate_uuid()
|
||||
PORT2 = uuidutils.generate_uuid()
|
||||
PORT3 = uuidutils.generate_uuid()
|
||||
PORT4 = uuidutils.generate_uuid()
|
||||
HOST = 'fake_host'
|
||||
|
||||
|
||||
class FakeFWaaSL2Agent(object):
|
||||
|
||||
def __init__(self):
|
||||
super(FakeFWaaSL2Agent, self).__init__()
|
||||
|
||||
def create(self, resource, attrs=None, minimal=False):
|
||||
"""Create a fake fwaas v2 resources
|
||||
|
||||
:param resource: A dictionary with all attributes
|
||||
:type resource: string
|
||||
:param attrs: A dictionary of each attribute you need to modify
|
||||
:type attrs: dictionary
|
||||
:param minimal: True if minimal port_detail is necessary
|
||||
otherwise False
|
||||
:type minimal: boolean
|
||||
:return:
|
||||
A OrderedDict faking the fwaas v2 resource
|
||||
"""
|
||||
target = getattr(self, "_" + resource)
|
||||
return copy.deepcopy(target(attrs=attrs, minimal=minimal))
|
||||
|
||||
def _fwg(self, **kwargs):
|
||||
|
||||
fwg = {
|
||||
'id': uuidutils.generate_uuid(),
|
||||
'name': 'my-group-' + uuidutils.generate_uuid(),
|
||||
'ingress_firewall_policy_id': uuidutils.generate_uuid(),
|
||||
'egress_firewall_policy_id': uuidutils.generate_uuid(),
|
||||
'description': 'my firewall group',
|
||||
'status': nl_consts.PENDING_CREATE,
|
||||
'ports': [PORT3, PORT4],
|
||||
'admin_state_up': True,
|
||||
'shared': False,
|
||||
'tenant_id': TENANT_ID,
|
||||
'project_id': PROJECT_ID
|
||||
}
|
||||
attrs = kwargs.get('attrs', None)
|
||||
if attrs:
|
||||
fwg.update(attrs)
|
||||
return fwg
|
||||
|
||||
def _fwg_with_rule(self, **kwargs):
|
||||
|
||||
fwg_with_rule = self.create('fwg', attrs={'ports': [PORT1, PORT2]})
|
||||
rules = {
|
||||
'ingress_rule_list': [mock.Mock()],
|
||||
'egress_rule_list': [mock.Mock()],
|
||||
'add-port-ids': [PORT1],
|
||||
'del-port-ids': [PORT2],
|
||||
'port_details': {
|
||||
PORT1: {
|
||||
'device': uuidutils.generate_uuid(),
|
||||
'device_owner': 'compute:nova',
|
||||
'host': HOST,
|
||||
'network_id': NETWORK_ID,
|
||||
'fixed_ips': [
|
||||
{'subnet_id': SUBNET_ID, 'ip_address': '172.24.4.5'}],
|
||||
'allowed_address_pairs': [],
|
||||
'port_security_enabled': True,
|
||||
'id': PORT1
|
||||
},
|
||||
PORT2: {
|
||||
'device': uuidutils.generate_uuid(),
|
||||
'device_owner': 'compute:nova',
|
||||
'host': HOST,
|
||||
'network_id': NETWORK_ID,
|
||||
'fixed_ips': [
|
||||
{'subnet_id': SUBNET_ID, 'ip_address': '172.24.4.6'}],
|
||||
'allowed_address_pairs': [],
|
||||
'port_security_enabled': True,
|
||||
'id': PORT2
|
||||
}
|
||||
},
|
||||
}
|
||||
fwg_with_rule.update(rules)
|
||||
|
||||
if kwargs.get('minimal', None):
|
||||
fwg_with_rule.update({'ports': []})
|
||||
fwg_with_rule.update({'add-port-ids': []})
|
||||
fwg_with_rule.update({'del-port-ids': []})
|
||||
fwg_with_rule.update({'port_details': {}})
|
||||
|
||||
attrs = kwargs.get('attrs', None)
|
||||
if attrs:
|
||||
fwg_with_rule.update(attrs)
|
||||
return fwg_with_rule
|
||||
|
||||
def _port(self, **kwargs):
|
||||
|
||||
if kwargs.get('minimal', None):
|
||||
return {'port_id': uuidutils.generate_uuid()}
|
||||
|
||||
port_detail = {
|
||||
'profile': {},
|
||||
'network_qos_policy_id': None,
|
||||
'qos_policy_id': None,
|
||||
'allowed_address_pairs': [],
|
||||
'admin_state_up': True,
|
||||
'network_id': NETWORK_ID,
|
||||
'segmentation_id': None,
|
||||
'fixed_ips': [
|
||||
{'subnet_id': SUBNET_ID, 'ip_address': '172.24.4.5'}],
|
||||
'vif_port': mock.Mock(),
|
||||
'device_owner': 'compute:node',
|
||||
'physical_network': 'physnet',
|
||||
'mac_address': 'fa:16:3e:8a:80:2b',
|
||||
'device': DEVICE_ID,
|
||||
'port_security_enabled': True,
|
||||
'port_id': uuidutils.generate_uuid(),
|
||||
'network_type': 'flat',
|
||||
'security_groups': []
|
||||
}
|
||||
|
||||
attrs = kwargs.get('attrs', None)
|
||||
|
||||
if attrs:
|
||||
port_detail.update(attrs)
|
||||
return port_detail
|
|
@ -0,0 +1,732 @@
|
|||
# Copyright 2017 Cisco Systems
|
||||
#
|
||||
# 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 copy
|
||||
|
||||
import mock
|
||||
from neutron_lib import constants as nl_consts
|
||||
from neutron_lib import context
|
||||
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.tests import base
|
||||
from neutron_fwaas.tests.unit.services.firewall.agents.l2 import fake_data
|
||||
|
||||
|
||||
class TestFWaasV2AgentExtensionBase(base.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestFWaasV2AgentExtensionBase, self).setUp()
|
||||
|
||||
self.fake = fake_data.FakeFWaaSL2Agent()
|
||||
self.port = self.fake.create('port')
|
||||
self.port_minimal = self.fake.create('port', minimal=True)
|
||||
self.fwg = self.fake.create('fwg')
|
||||
self.fwg_with_rule = self.fake.create('fwg_with_rule')
|
||||
self.port_id = self.port['port_id']
|
||||
self.fwg_id = self.fwg['id']
|
||||
self.host = fake_data.HOST
|
||||
self.ctx = context.get_admin_context()
|
||||
|
||||
self.l2 = fwaas_v2.FWaaSV2AgentExtension()
|
||||
self.l2.consume_api(mock.Mock())
|
||||
self.driver = mock.patch(
|
||||
'neutron.manager.NeutronManager.load_class_for_provider').start()
|
||||
self.l2.initialize(None, 'ovs')
|
||||
self.l2.vlan_manager = mock.Mock()
|
||||
self.conf = cfg.ConfigOpts()
|
||||
self.l2.fwg_map = mock.Mock()
|
||||
self.l2.conf.host = self.host
|
||||
self.rpc = self.l2.plugin_rpc
|
||||
|
||||
|
||||
class TestFWaasV2AgentExtension(TestFWaasV2AgentExtensionBase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestFWaasV2AgentExtension, self).setUp()
|
||||
cfg.CONF.set_override('firewall_l2_driver', 'ovs', group='fwaas')
|
||||
|
||||
def test_initialize(self):
|
||||
with mock.patch('neutron.common.rpc.create_connection') as conn:
|
||||
self.l2.initialize(None, 'ovs')
|
||||
self.driver.assert_called_with('neutron.agent.l2.firewall_drivers',
|
||||
'ovs')
|
||||
conn.assert_called_with()
|
||||
self.l2.conn.create_consumer.assert_called_with(
|
||||
consts.FW_AGENT, [self.l2], fanout=False)
|
||||
self.l2.conn.consume_in_threads.assert_called_with()
|
||||
|
||||
|
||||
class TestHandlePort(TestFWaasV2AgentExtensionBase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestHandlePort, self).setUp()
|
||||
self.rpc.get_firewall_group_for_port = mock.Mock(
|
||||
return_value=self.fwg)
|
||||
self.l2._compute_status = mock.Mock(return_value=nl_consts.ACTIVE)
|
||||
self.l2._apply_fwg_rules = mock.Mock(return_value=True)
|
||||
self.l2._send_fwg_status = mock.Mock()
|
||||
self.ctx = context.get_admin_context()
|
||||
|
||||
def test_normal(self):
|
||||
self.l2.fwg_map.get_port_fwg.return_value = None
|
||||
self.l2.handle_port(self.ctx, self.port)
|
||||
self.rpc.get_firewall_group_for_port.assert_called_once_with(
|
||||
self.ctx, self.port['port_id'])
|
||||
self.l2._apply_fwg_rules.assert_called_once_with(self.fwg, [self.port])
|
||||
self.l2._compute_status.assert_called_once_with(
|
||||
self.fwg, True, event=consts.HANDLE_PORT)
|
||||
self.l2.fwg_map.set_port_fwg.assert_called_once_with(self.port,
|
||||
self.fwg)
|
||||
self.l2._send_fwg_status.assert_called_once_with(
|
||||
self.ctx, fwg_id=self.fwg['id'],
|
||||
status=nl_consts.ACTIVE, host=self.l2.conf.host)
|
||||
|
||||
def test_non_layer2_port(self):
|
||||
self.port['device_owner'] = 'network:router_gateway'
|
||||
self.l2.handle_port(self.ctx, self.port)
|
||||
|
||||
self.rpc.get_firewall_group_for_port.assert_not_called()
|
||||
self.l2._apply_fwg_rules.assert_not_called()
|
||||
self.l2._compute_status.assert_not_called()
|
||||
self.l2.fwg_map.set_port_fwg.assert_not_called()
|
||||
self.l2._send_fwg_status.assert_not_called()
|
||||
|
||||
def test_no_fwg_is_asossicate_to_port(self):
|
||||
self.l2.fwg_map.get_port_fwg.return_value = None
|
||||
self.rpc.get_firewall_group_for_port.return_value = None
|
||||
self.l2.handle_port(self.ctx, self.port)
|
||||
|
||||
self.rpc.get_firewall_group_for_port.assert_called_once_with(
|
||||
self.ctx, self.port['port_id'])
|
||||
self.l2._apply_fwg_rules.assert_not_called()
|
||||
self.l2._compute_status.assert_not_called()
|
||||
self.l2.fwg_map.set_port_fwg.assert_not_called()
|
||||
self.l2._send_fwg_status.assert_not_called()
|
||||
|
||||
def test_port_already_apply_fwg(self):
|
||||
self.l2.fwg_map.get_port_fwg.return_value = self.fwg
|
||||
self.l2.handle_port(self.ctx, self.port)
|
||||
|
||||
self.rpc.get_firewall_group_for_port.assert_not_called()
|
||||
self.l2._apply_fwg_rules.assert_not_called()
|
||||
self.l2._compute_status.assert_not_called()
|
||||
self.l2.fwg_map.set_port_fwg.assert_not_called()
|
||||
self.l2._send_fwg_status.assert_not_called()
|
||||
|
||||
|
||||
class TestDeletePort(TestFWaasV2AgentExtensionBase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestDeletePort, self).setUp()
|
||||
self.l2._compute_status = mock.Mock(return_value=nl_consts.ACTIVE)
|
||||
self.l2._apply_fwg_rules = mock.Mock(return_value=True)
|
||||
self.l2._send_fwg_status = mock.Mock()
|
||||
|
||||
self.l2.fwg_map.get_port_fwg = mock.Mock(return_value=self.fwg)
|
||||
self.l2.fwg_map.set_fwg = mock.Mock()
|
||||
self.l2.fwg_map.get_port = mock.Mock(return_value=self.port)
|
||||
self.l2.fwg_map.remove_port = mock.Mock()
|
||||
|
||||
def test_include_vif_port_attribute(self):
|
||||
self.port_minimal.update({'vif_port': None})
|
||||
self.l2.fwg_map.get_port_fwg.return_value = None
|
||||
self.l2.delete_port(self.ctx, self.port_minimal)
|
||||
|
||||
self.l2.fwg_map.get_port_fwg.assert_not_called()
|
||||
self.l2._apply_fwg_rules.assert_not_called()
|
||||
|
||||
def test_port_belongs_to_fwg(self):
|
||||
expected_ports = self.fwg['ports']
|
||||
self.fwg['ports'].append(self.port['port_id'])
|
||||
self.l2.delete_port(self.ctx, self.port_minimal)
|
||||
|
||||
self.l2.fwg_map.get_port_fwg.assert_called_once_with(self.port)
|
||||
self.l2._apply_fwg_rules.assert_called_once_with(
|
||||
self.fwg, [self.port], event=consts.DELETE_FWG)
|
||||
# 'port_id' has been removed from 'ports'
|
||||
self.assertEqual(expected_ports, self.fwg['ports'])
|
||||
self.l2.fwg_map.set_fwg.assert_called_once_with(self.fwg)
|
||||
|
||||
def test_port_belongs_to_no_fwg(self):
|
||||
expected_ports = self.fwg['ports']
|
||||
self.l2.delete_port(self.ctx, self.port_minimal)
|
||||
|
||||
self.l2.fwg_map.get_port_fwg.assert_called_once_with(self.port)
|
||||
self.l2._apply_fwg_rules.assert_called_once_with(
|
||||
self.fwg, [self.port], event=consts.DELETE_FWG)
|
||||
# 'ports' not changed during delete_port()
|
||||
self.assertEqual(expected_ports, self.fwg['ports'])
|
||||
self.l2.fwg_map.set_fwg.assert_called_once_with(self.fwg)
|
||||
|
||||
def test_non_layer2_port(self):
|
||||
self.port['device_owner'] = 'network:router_gateway'
|
||||
self.l2.delete_port(self.ctx, self.port_minimal)
|
||||
|
||||
self.l2.fwg_map.get_port_fwg.assert_not_called()
|
||||
|
||||
def test_cannot_get_fwg_from_port(self):
|
||||
self.l2.fwg_map.get_port_fwg.return_value = None
|
||||
self.l2.delete_port(self.ctx, self.port_minimal)
|
||||
|
||||
self.l2.fwg_map.get_port_fwg.assert_called_once_with(self.port)
|
||||
self.l2._apply_fwg_rules.assert_not_called()
|
||||
|
||||
|
||||
class TestCreateFirewallGroup(TestFWaasV2AgentExtensionBase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestCreateFirewallGroup, self).setUp()
|
||||
self.l2._apply_fwg_rules = mock.Mock(return_value=True)
|
||||
self.l2._compute_status = mock.Mock(return_value='ACTIVE')
|
||||
self.l2._send_fwg_status = mock.Mock()
|
||||
|
||||
def test_create_event_is_create(self):
|
||||
fwg = self.fwg_with_rule
|
||||
fwg['ports'] = [fake_data.PORT1]
|
||||
ports = [fwg['port_details'][fake_data.PORT1]]
|
||||
self.l2._create_firewall_group(
|
||||
self.ctx, fwg, self.host, event=consts.CREATE_FWG)
|
||||
self.l2._apply_fwg_rules.assert_called_once_with(
|
||||
fwg, ports, consts.CREATE_FWG)
|
||||
self.l2._compute_status.assert_called_once_with(
|
||||
fwg, True, consts.CREATE_FWG)
|
||||
|
||||
def test_create_event_is_not_create(self):
|
||||
fwg = self.fwg_with_rule
|
||||
fwg['ports'] = [fake_data.PORT1]
|
||||
ports = [fwg['port_details'][fake_data.PORT1]]
|
||||
self.l2._create_firewall_group(
|
||||
self.ctx, fwg, self.host, event=consts.UPDATE_FWG)
|
||||
self.l2._apply_fwg_rules.assert_called_once_with(
|
||||
fwg, ports, consts.UPDATE_FWG)
|
||||
|
||||
def test_create_with_port(self):
|
||||
fwg = self.fwg_with_rule
|
||||
ports = [fwg['port_details'][fake_data.PORT1]]
|
||||
self.l2.create_firewall_group(self.ctx, fwg, self.host)
|
||||
self.l2._apply_fwg_rules.assert_called_once_with(
|
||||
fwg, ports, consts.CREATE_FWG)
|
||||
|
||||
for idx, args in enumerate(self.l2._compute_status.call_args_list):
|
||||
self.assertEqual(fwg, args[0][0])
|
||||
self.assertEqual(True, args[0][1])
|
||||
self.assertEqual(consts.CREATE_FWG, args[0][2])
|
||||
|
||||
for idx, args in enumerate(self.l2._send_fwg_status.call_args_list):
|
||||
self.assertEqual(self.ctx, args[0][0])
|
||||
self.assertEqual(fwg['id'], args[0][1])
|
||||
self.assertEqual('ACTIVE', args[0][2])
|
||||
self.assertEqual(self.host, args[0][3])
|
||||
|
||||
def test_create_with_no_ports(self):
|
||||
self.fwg_with_rule['add-port-ids'] = []
|
||||
self.assertIsNone(self.l2.create_firewall_group(
|
||||
self.ctx, self.fwg_with_rule, self.host))
|
||||
self.l2._apply_fwg_rules.assert_not_called()
|
||||
self.l2.fwg_map.set_port_fwg.assert_not_called()
|
||||
self.l2._send_fwg_status.assert_called_once_with(
|
||||
self.ctx, self.fwg_with_rule['id'], 'INACTIVE', self.host)
|
||||
|
||||
def test_create_with_invalid_host(self):
|
||||
self.fwg_with_rule['port_details'][fake_data.PORT1]['host'] = 'invalid'
|
||||
self.l2.create_firewall_group(self.ctx, self.fwg_with_rule, self.host)
|
||||
self.l2._apply_fwg_rules.assert_not_called()
|
||||
self.l2._send_fwg_status.assert_called_once_with(
|
||||
self.ctx, self.fwg_with_rule['id'], 'INACTIVE', self.host)
|
||||
|
||||
def test_illegal_create_with_no_l2_ports(self):
|
||||
fwg = {
|
||||
'name': 'non-default',
|
||||
'id': self.fwg_id,
|
||||
'ports': [],
|
||||
'add-port-ids': [self.port_id],
|
||||
'admin_state_up': True,
|
||||
'port_details': {
|
||||
self.port_id: {
|
||||
'device_owner': 'network:router_interface'
|
||||
}
|
||||
}
|
||||
}
|
||||
self.l2.create_firewall_group(self.ctx, fwg, self.host)
|
||||
self.l2._apply_fwg_rules.assert_not_called()
|
||||
self.l2.fwg_map.set_port_fwg.assert_not_called()
|
||||
self.l2._send_fwg_status.assert_called_once_with(
|
||||
self.ctx, fwg['id'], 'INACTIVE', self.host)
|
||||
|
||||
|
||||
class TestDeleteFirewallGroup(TestFWaasV2AgentExtensionBase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestDeleteFirewallGroup, self).setUp()
|
||||
self.l2._apply_fwg_rules = mock.Mock(return_value=True)
|
||||
self.l2._compute_status = mock.Mock(return_value='ACTIVE')
|
||||
self.l2._send_fwg_status = mock.Mock()
|
||||
self.rpc.firewall_group_deleted = mock.Mock()
|
||||
|
||||
def test_delete_with_port(self):
|
||||
fwg = self.fwg_with_rule
|
||||
ports = [fwg['port_details'][fake_data.PORT2]]
|
||||
|
||||
self.assertIsNone(self.l2.delete_firewall_group(
|
||||
self.ctx, self.fwg_with_rule, self.host))
|
||||
self.l2._apply_fwg_rules.assert_called_once_with(
|
||||
fwg, ports, event=consts.DELETE_FWG)
|
||||
self.l2.fwg_map.remove_fwg.assert_called_once_with(fwg)
|
||||
for idx, args in enumerate(self.l2._compute_status.call_args_list):
|
||||
self.assertEqual(fwg, args[0][0])
|
||||
self.assertEqual(True, args[0][2])
|
||||
self.assertEqual({'event': consts.CREATE_FWG}, args[1])
|
||||
|
||||
for idx, args in enumerate(self.l2._send_fwg_status.call_args_list):
|
||||
self.assertEqual(self.ctx, args[0][0])
|
||||
self.assertEqual(fwg['id'], args[0][1])
|
||||
self.assertEqual('ACTIVE', args[0][2])
|
||||
self.assertEqual(self.host, args[0][3])
|
||||
|
||||
def test_delete_with_no_ports(self):
|
||||
self.fwg_with_rule['del-port-ids'] = []
|
||||
self.l2.delete_firewall_group(self.ctx, self.fwg_with_rule, self.host)
|
||||
self.l2._apply_fwg_rules.assert_not_called()
|
||||
|
||||
def test_delete_with_no_l2_ports(self):
|
||||
self.fwg_with_rule['port_details'][fake_data.PORT2][
|
||||
'device_owner'] = 'network:router_interface'
|
||||
self.l2.delete_firewall_group(self.ctx, self.fwg_with_rule, self.host)
|
||||
self.l2._apply_fwg_rules.assert_not_called()
|
||||
|
||||
def test_delete_with_exception(self):
|
||||
self.l2._delete_firewall_group = mock.Mock(side_effect=Exception)
|
||||
self.assertIsNone(self.l2.delete_firewall_group(
|
||||
self.ctx, self.fwg_with_rule, self.host))
|
||||
|
||||
def test_delete_event_is_update(self):
|
||||
self.l2._delete_firewall_group(
|
||||
self.ctx, self.fwg_with_rule, self.host, event=consts.UPDATE_FWG)
|
||||
self.l2.fwg_map.remove_fwg.assert_not_called()
|
||||
self.rpc.firewall_group_deleted.assert_not_called()
|
||||
self.l2._compute_status.assert_called_once_with(
|
||||
self.fwg_with_rule, True, consts.UPDATE_FWG)
|
||||
self.l2._send_fwg_status.assert_called_once_with(
|
||||
self.ctx, self.fwg_with_rule['id'], 'ACTIVE', self.host)
|
||||
|
||||
|
||||
class TestUpdateFirewallGroup(TestFWaasV2AgentExtensionBase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestUpdateFirewallGroup, self).setUp()
|
||||
self.l2._delete_firewall_group = mock.Mock()
|
||||
self.l2._create_firewall_group = mock.Mock()
|
||||
self.l2._send_fwg_status = mock.Mock()
|
||||
|
||||
def test_update(self):
|
||||
self.assertIsNone(self.l2.update_firewall_group(
|
||||
self.ctx, mock.ANY, self.host))
|
||||
|
||||
self.l2._delete_firewall_group.assert_called_once_with(
|
||||
self.ctx, mock.ANY, self.host, event=consts.UPDATE_FWG)
|
||||
self.l2._create_firewall_group.assert_called_once_with(
|
||||
self.ctx, mock.ANY, self.host, event=consts.UPDATE_FWG)
|
||||
|
||||
def test_update_raised_in_delete_firewall_group(self):
|
||||
self.l2._delete_firewall_group.side_effect = Exception
|
||||
fwg = self.fwg_with_rule
|
||||
self.assertIsNone(self.l2.update_firewall_group(
|
||||
self.ctx, fwg, self.host))
|
||||
self.l2._send_fwg_status.assert_called_once_with(
|
||||
self.ctx, fwg['id'], status='ERROR', host=self.host)
|
||||
|
||||
def test_update_raised_in_create_firewall_group(self):
|
||||
self.l2._create_firewall_group.side_effect = Exception
|
||||
fwg = self.fwg_with_rule
|
||||
self.assertIsNone(self.l2.update_firewall_group(
|
||||
self.ctx, fwg, self.host))
|
||||
self.l2._send_fwg_status.assert_called_once_with(
|
||||
self.ctx, fwg['id'], status='ERROR', host=self.host)
|
||||
|
||||
|
||||
class TestIsPortLayer2(TestFWaasV2AgentExtensionBase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestIsPortLayer2, self).setUp()
|
||||
|
||||
def test_vm_port(self):
|
||||
self.assertTrue(self.l2._is_port_layer2(self.port))
|
||||
|
||||
def test_not_vm_port(self):
|
||||
for device_owner in [nl_consts.DEVICE_OWNER_ROUTER_INTF,
|
||||
nl_consts.DEVICE_OWNER_ROUTER_GW,
|
||||
nl_consts.DEVICE_OWNER_DHCP,
|
||||
nl_consts.DEVICE_OWNER_DVR_INTERFACE,
|
||||
nl_consts.DEVICE_OWNER_AGENT_GW,
|
||||
nl_consts.DEVICE_OWNER_ROUTER_SNAT,
|
||||
nl_consts.DEVICE_OWNER_LOADBALANCER,
|
||||
nl_consts.DEVICE_OWNER_LOADBALANCERV2,
|
||||
'unknown device_owner',
|
||||
'']:
|
||||
self.port['device_owner'] = device_owner
|
||||
self.assertFalse(self.l2._is_port_layer2(self.port))
|
||||
|
||||
def test_illegal_no_device_owner(self):
|
||||
del self.port['device_owner']
|
||||
self.assertFalse(self.l2._is_port_layer2(self.port))
|
||||
|
||||
|
||||
class TestComputeStatus(TestFWaasV2AgentExtensionBase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestComputeStatus, self).setUp()
|
||||
self.ports = list(self.fwg_with_rule['port_details'].values())
|
||||
|
||||
def test_normal(self):
|
||||
result = True
|
||||
fwg = self.fwg_with_rule
|
||||
self.assertEqual('ACTIVE', self.l2._compute_status(fwg, result))
|
||||
|
||||
def test_event_is_delete(self):
|
||||
result = True
|
||||
fwg = self.fwg_with_rule
|
||||
self.assertIsNone(self.l2._compute_status(
|
||||
fwg, result, consts.DELETE_FWG))
|
||||
|
||||
def test_event_is_update(self):
|
||||
result = True
|
||||
fwg = self.fwg_with_rule
|
||||
self.assertEqual('ACTIVE', self.l2._compute_status(
|
||||
fwg, result, consts.UPDATE_FWG))
|
||||
|
||||
def test_event_is_update_and_has_last_port(self):
|
||||
result = True
|
||||
fwg = self.fake.create('fwg_with_rule', attrs={'last-port': False})
|
||||
|
||||
self.assertEqual('ACTIVE', self.l2._compute_status(
|
||||
fwg, result, consts.UPDATE_FWG))
|
||||
|
||||
fwg = self.fake.create('fwg_with_rule', attrs={'last-port': True})
|
||||
self.assertEqual('INACTIVE', self.l2._compute_status(
|
||||
fwg, result, consts.UPDATE_FWG))
|
||||
|
||||
def test_event_is_update_and_has_no_last_port_but_has_ports(self):
|
||||
result = True
|
||||
fwg = self.fwg_with_rule
|
||||
self.assertEqual('ACTIVE', self.l2._compute_status(
|
||||
fwg, result, consts.UPDATE_FWG))
|
||||
|
||||
def test_event_is_update_and_has_no_last_port_and_ports(self):
|
||||
result = True
|
||||
fwg = self.fwg_with_rule
|
||||
fwg['ports'] = []
|
||||
self.assertEqual('INACTIVE', self.l2._compute_status(
|
||||
fwg, result, consts.UPDATE_FWG))
|
||||
|
||||
def test_event_is_create(self):
|
||||
result = True
|
||||
fwg = self.fwg_with_rule
|
||||
self.assertEqual('ACTIVE', self.l2._compute_status(
|
||||
fwg, result, consts.CREATE_FWG))
|
||||
|
||||
def test_event_is_create_and_no_fwg_ports(self):
|
||||
result = True
|
||||
fwg = self.fwg_with_rule
|
||||
fwg['ports'] = []
|
||||
self.assertEqual('INACTIVE', self.l2._compute_status(
|
||||
fwg, result, consts.CREATE_FWG))
|
||||
|
||||
def test_event_is_handle_port(self):
|
||||
result = True
|
||||
fwg = self.fwg_with_rule
|
||||
self.assertEqual('ACTIVE', self.l2._compute_status(
|
||||
fwg, result, consts.HANDLE_PORT))
|
||||
|
||||
def test_event_is_delete_port(self):
|
||||
result = True
|
||||
fwg = self.fwg_with_rule
|
||||
self.assertEqual('ACTIVE', self.l2._compute_status(
|
||||
fwg, result, consts.DELETE_PORT))
|
||||
|
||||
def test_event_is_delete_port_and_no_fwg_ports(self):
|
||||
result = True
|
||||
fwg = self.fwg_with_rule
|
||||
fwg['ports'] = []
|
||||
self.assertEqual('INACTIVE', self.l2._compute_status(
|
||||
fwg, result, consts.DELETE_PORT))
|
||||
|
||||
def test_driver_result_is_false(self):
|
||||
result = False
|
||||
fwg = self.fwg_with_rule
|
||||
self.assertEqual('ERROR', self.l2._compute_status(
|
||||
fwg, result))
|
||||
|
||||
def test_admin_state_up_is_false(self):
|
||||
result = True
|
||||
self.fwg_with_rule['admin_state_up'] = False
|
||||
|
||||
self.assertEqual('DOWN', self.l2._compute_status(
|
||||
self.fwg_with_rule, self.ports, result))
|
||||
|
||||
def test_active_inactive_patterns(self):
|
||||
result = True
|
||||
fwg = self.fwg_with_rule
|
||||
# Case1: ingress/egress_firewall_policy_id
|
||||
# Case2: ports --> already tested at above cases
|
||||
expect_and_attrs = [
|
||||
('INACTIVE', ('ingress_firewall_policy_id',
|
||||
'egress_firewall_policy_id')),
|
||||
('ACTIVE', ('ingress_firewall_policy_id',)),
|
||||
('ACTIVE', ('egress_firewall_policy_id',)),
|
||||
]
|
||||
for attr in expect_and_attrs:
|
||||
fwg = self.fake.create('fwg_with_rule')
|
||||
expect = attr[0]
|
||||
for p in attr[1]:
|
||||
fwg[p] = None
|
||||
self.assertEqual(expect, self.l2._compute_status(fwg, result))
|
||||
|
||||
|
||||
class TestApplyFwgRules(TestFWaasV2AgentExtensionBase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestApplyFwgRules, self).setUp()
|
||||
|
||||
class DummyVlan(object):
|
||||
|
||||
def __init__(self, vlan=None):
|
||||
self.vlan = vlan
|
||||
|
||||
self.l2.vlan_manager.get.return_value = DummyVlan(vlan='999')
|
||||
|
||||
def test_event_is_create(self):
|
||||
fwg_ports = [self.fwg_with_rule['port_details'][fake_data.PORT1]]
|
||||
driver_ports = copy.deepcopy(fwg_ports)
|
||||
driver_ports[0].update({'lvlan': 999})
|
||||
|
||||
self.assertTrue(self.l2._apply_fwg_rules(
|
||||
self.fwg_with_rule, fwg_ports, event=consts.CREATE_FWG))
|
||||
|
||||
self.l2.driver.create_firewall_group.assert_called_once_with(
|
||||
driver_ports, self.fwg_with_rule)
|
||||
self.l2.driver.delete_firewall_group.assert_not_called()
|
||||
self.l2.driver.update_firewall_group.assert_not_called()
|
||||
|
||||
def test_event_is_update(self):
|
||||
fwg_ports = [self.fwg_with_rule['port_details'][fake_data.PORT1]]
|
||||
driver_ports = copy.deepcopy(fwg_ports)
|
||||
driver_ports[0].update({'lvlan': 999})
|
||||
|
||||
self.assertTrue(self.l2._apply_fwg_rules(
|
||||
self.fwg_with_rule, fwg_ports, event=consts.UPDATE_FWG))
|
||||
|
||||
self.l2.driver.update_firewall_group.assert_called_once_with(
|
||||
driver_ports, self.fwg_with_rule)
|
||||
|
||||
def test_event_is_delete(self):
|
||||
fwg_ports = [self.fwg_with_rule['port_details'][fake_data.PORT1]]
|
||||
driver_ports = copy.deepcopy(fwg_ports)
|
||||
driver_ports[0].update({'lvlan': 999})
|
||||
|
||||
self.assertTrue(self.l2._apply_fwg_rules(
|
||||
self.fwg_with_rule, fwg_ports, event=consts.DELETE_FWG))
|
||||
|
||||
self.l2.driver.delete_firewall_group.assert_called_once_with(
|
||||
fwg_ports, self.fwg_with_rule)
|
||||
|
||||
def test_raised_in_driver(self):
|
||||
self.l2.driver.delete_firewall_group.side_effect = \
|
||||
f_exc.FirewallInternalDriverError(driver='ovs firewall')
|
||||
fwg_ports = [self.fwg_with_rule['port_details'][fake_data.PORT1]]
|
||||
driver_ports = copy.deepcopy(fwg_ports)
|
||||
driver_ports[0].update({'lvlan': 999})
|
||||
|
||||
self.assertFalse(self.l2._apply_fwg_rules(
|
||||
self.fwg_with_rule, fwg_ports, event=consts.DELETE_FWG))
|
||||
|
||||
self.l2.driver.delete_firewall_group.assert_called_once_with(
|
||||
fwg_ports, self.fwg_with_rule)
|
||||
|
||||
|
||||
class TestSendFwgStatus(TestFWaasV2AgentExtensionBase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestSendFwgStatus, self).setUp()
|
||||
self.rpc.set_firewall_group_status = mock.Mock()
|
||||
|
||||
def test_success(self):
|
||||
self.assertIsNone(self.l2._send_fwg_status(
|
||||
self.ctx, self.fwg_id, 'ACTIVE', self.host))
|
||||
|
||||
def test_failure(self):
|
||||
self.rpc.set_firewall_group_status.side_effect = Exception
|
||||
self.assertIsNone(self.l2._send_fwg_status(
|
||||
self.ctx, self.fwg_id, 'ACTIVE', self.host))
|
||||
|
||||
|
||||
class TestAddLocalVlanToPorts(TestFWaasV2AgentExtensionBase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestAddLocalVlanToPorts, self).setUp()
|
||||
|
||||
class DummyVlan(object):
|
||||
|
||||
def __init__(self, vlan=None):
|
||||
self.vlan = vlan
|
||||
|
||||
self.l2.vlan_manager.get.return_value = DummyVlan(vlan='999')
|
||||
self.port_with_detail = {
|
||||
'port_id': fake_data.PORT1,
|
||||
'id': fake_data.PORT1,
|
||||
'network_id': fake_data.NETWORK_ID,
|
||||
'port_details': {
|
||||
fake_data.PORT1: {
|
||||
'device': 'c12e5c1e-d68e-45bd-a2d3-1f2f32604e41',
|
||||
'device_owner': 'compute:nova',
|
||||
'host': self.host,
|
||||
'network_id': fake_data.NETWORK_ID,
|
||||
'fixed_ips': [
|
||||
{'subnet_id': fake_data.SUBNET_ID,
|
||||
'ip_address': '172.24.4.5'}],
|
||||
'allowed_address_pairs': [],
|
||||
'port_security_enabled': True,
|
||||
'id': fake_data.PORT1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def test_port_has_detail_and_port_id(self):
|
||||
del self.port_with_detail['id']
|
||||
expect = [copy.deepcopy(self.port_with_detail)]
|
||||
expect[0].update({'lvlan': 999})
|
||||
actual = self.l2._add_local_vlan_to_ports([self.port_with_detail])
|
||||
|
||||
self.l2.vlan_manager.get.assert_called_once_with(
|
||||
self.port_with_detail['network_id'])
|
||||
self.assertEqual(expect, actual)
|
||||
|
||||
def test_port_has_detail_and_id(self):
|
||||
del self.port_with_detail['port_id']
|
||||
expect = [copy.deepcopy(self.port_with_detail)]
|
||||
expect[0].update({'lvlan': 999})
|
||||
actual = self.l2._add_local_vlan_to_ports([self.port_with_detail])
|
||||
|
||||
self.l2.vlan_manager.get.assert_called_once_with(
|
||||
self.port_with_detail['network_id'])
|
||||
self.assertEqual(expect, actual)
|
||||
|
||||
def test_port_has_no_detail(self):
|
||||
del self.port_with_detail['port_details']
|
||||
expect = [copy.deepcopy(self.port_with_detail)]
|
||||
expect[0].update({'lvlan': 999})
|
||||
actual = self.l2._add_local_vlan_to_ports([self.port_with_detail])
|
||||
|
||||
self.l2.vlan_manager.get.assert_called_once_with(
|
||||
self.port_with_detail['network_id'])
|
||||
self.assertEqual(expect, actual)
|
||||
|
||||
|
||||
class TestFWaaSL2PluginApi(TestFWaasV2AgentExtensionBase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestFWaaSL2PluginApi, self).setUp()
|
||||
|
||||
self.plugin = fwaas_v2.FWaaSL2PluginApi(
|
||||
consts.FIREWALL_PLUGIN, self.host)
|
||||
self.plugin.client = mock.Mock()
|
||||
self.cctxt = self.plugin.client.prepare()
|
||||
|
||||
def test_get_firewall_group_for_port(self):
|
||||
self.plugin.get_firewall_group_for_port(self.ctx, mock.ANY)
|
||||
self.cctxt.call.assert_called_once_with(
|
||||
self.ctx,
|
||||
'get_firewall_group_for_port',
|
||||
port_id=mock.ANY
|
||||
)
|
||||
|
||||
def test_set_firewall_group_status(self):
|
||||
self.plugin.set_firewall_group_status(
|
||||
self.ctx, self.fwg_id, 'ACTIVE', self.host)
|
||||
self.cctxt.call.assert_called_once_with(
|
||||
self.ctx,
|
||||
'set_firewall_group_status',
|
||||
fwg_id=self.fwg_id,
|
||||
status='ACTIVE',
|
||||
host=self.host,
|
||||
)
|
||||
|
||||
def test_firewall_group_deleted(self):
|
||||
self.plugin.firewall_group_deleted(self.ctx, self.fwg_id, self.host)
|
||||
self.cctxt.call.assert_called_once_with(
|
||||
self.ctx,
|
||||
'firewall_group_deleted',
|
||||
fwg_id=self.fwg_id,
|
||||
host=self.host,
|
||||
)
|
||||
|
||||
|
||||
class TestPortFirewallGroupMap(base.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestPortFirewallGroupMap, self).setUp()
|
||||
self.fake = fake_data.FakeFWaaSL2Agent()
|
||||
self.map = fwaas_v2.PortFirewallGroupMap()
|
||||
self.fwg = self.fake.create('fwg')
|
||||
self.fwg_id = self.fwg['id']
|
||||
self.port = self.fake.create('port')
|
||||
self.fwg['ports'] = []
|
||||
|
||||
def test_set_and_get(self):
|
||||
self.map.set_fwg(self.fwg)
|
||||
self.assertEqual(self.fwg, self.map.get_fwg(self.fwg_id))
|
||||
|
||||
def test_set_and_get_port_fwg(self):
|
||||
port1 = self.port
|
||||
port2 = self.fake.create('port')
|
||||
self.map.set_port_fwg(port1, self.fwg)
|
||||
self.map.set_port_fwg(port2, self.fwg)
|
||||
self.assertEqual(self.fwg, self.map.get_port_fwg(port1))
|
||||
self.assertEqual(self.fwg, self.map.get_port_fwg(port2))
|
||||
self.assertIsNone(self.map.get_port_fwg('unknown'))
|
||||
|
||||
def test_remove_port(self):
|
||||
port1 = self.port
|
||||
port2 = self.fake.create('port')
|
||||
self.map.set_port_fwg(port1, self.fwg)
|
||||
self.map.remove_port(port2)
|
||||
|
||||
self.map.set_port_fwg(port2, self.fwg)
|
||||
self.map.remove_port(port1)
|
||||
self.assertIsNone(self.map.get_port(port1))
|
||||
self.assertEqual([port2['port_id']],
|
||||
self.map.get_fwg(self.fwg_id)['ports'])
|
||||
self.map.remove_port(port2)
|
||||
self.assertIsNone(self.map.get_port(port2))
|
||||
self.assertEqual([], self.map.get_fwg(self.fwg_id)['ports'])
|
||||
|
||||
def test_illegal_remove_port_no_relation_with_fwg(self):
|
||||
port1 = self.port
|
||||
port1_id = port1['port_id']
|
||||
self.map.set_port_fwg(port1, self.fwg)
|
||||
self.map.port_fwg[port1_id] = None
|
||||
self.map.remove_port(port1)
|
||||
self.assertEqual(port1, self.map.get_port(port1))
|
||||
|
||||
def test_remove_fwg(self):
|
||||
self.map.set_fwg(self.fwg)
|
||||
self.assertEqual(self.fwg, self.map.get_fwg(self.fwg_id))
|
||||
self.map.remove_fwg(self.fwg)
|
||||
self.assertIsNone(self.map.get_fwg(self.fwg_id))
|
||||
|
||||
def test_remove_fwg_non_exist(self):
|
||||
self.map.remove_fwg(self.fwg)
|
||||
self.assertIsNone(self.map.get_fwg(self.fwg_id))
|
|
@ -59,6 +59,7 @@ def _setup_test_agent_class(service_plugins):
|
|||
def __init__(self, conf):
|
||||
self.event_observers = mock.Mock()
|
||||
self.conf = conf
|
||||
firewall_agent_api._check_required_agent_extension = mock.Mock()
|
||||
super(FWaasTestAgent, self).__init__(conf)
|
||||
|
||||
def delete_router(self, context, data):
|
||||
|
@ -141,6 +142,7 @@ class TestFWaaSL3AgentExtension(base.BaseTestCase):
|
|||
def test_update_firewall_group_with_ports_added_and_deleted(self):
|
||||
firewall_group = {'id': 0, 'project_id': 1,
|
||||
'admin_state_up': True,
|
||||
'ports': [1, 2, 3, 4],
|
||||
'add-port-ids': [1, 2],
|
||||
'del-port-ids': [3, 4],
|
||||
'router_ids': [],
|
||||
|
@ -180,6 +182,7 @@ class TestFWaaSL3AgentExtension(base.BaseTestCase):
|
|||
def test_update_firewall_group_with_ports_added_and_admin_state_down(self):
|
||||
firewall_group = {'id': 0, 'project_id': 1,
|
||||
'admin_state_up': False,
|
||||
'ports': [1, 2],
|
||||
'add-port-ids': [1, 2],
|
||||
'del-port-ids': [],
|
||||
'router_ids': [],
|
||||
|
@ -210,6 +213,7 @@ class TestFWaaSL3AgentExtension(base.BaseTestCase):
|
|||
def test_update_firewall_group_with_all_ports_deleted(self):
|
||||
firewall_group = {'id': 0, 'project_id': 1,
|
||||
'admin_state_up': True,
|
||||
'ports': [3, 4],
|
||||
'add-port-ids': [],
|
||||
'del-port-ids': [3, 4],
|
||||
'last-port': True}
|
||||
|
@ -230,9 +234,14 @@ class TestFWaaSL3AgentExtension(base.BaseTestCase):
|
|||
|
||||
self.api.update_firewall_group(self.context, firewall_group,
|
||||
host='host')
|
||||
|
||||
mock_get_firewall_group_ports.assert_called_once_with(self.context,
|
||||
firewall_group, require_new_plugin=True, to_delete=True)
|
||||
calls = [
|
||||
mock.call._get_firewall_group_ports(
|
||||
self.context, firewall_group, require_new_plugin=True,
|
||||
to_delete=True),
|
||||
mock.call._get_firewall_group_ports(
|
||||
self.context, firewall_group)
|
||||
]
|
||||
mock_get_firewall_group_ports.assert_has_calls(calls)
|
||||
mock_get_in_ns_ports.assert_called
|
||||
mock_set_firewall_group_status.assert_called_once_with(
|
||||
self.context, firewall_group['id'], 'INACTIVE')
|
||||
|
@ -240,6 +249,7 @@ class TestFWaaSL3AgentExtension(base.BaseTestCase):
|
|||
def test_update_firewall_group_with_no_ports_added_or_deleted(self):
|
||||
firewall_group = {'id': 0, 'project_id': 1,
|
||||
'admin_state_up': True,
|
||||
'ports': [],
|
||||
'add-port-ids': [],
|
||||
'del-port-ids': [],
|
||||
'router_ids': []}
|
||||
|
@ -262,6 +272,7 @@ class TestFWaaSL3AgentExtension(base.BaseTestCase):
|
|||
# This test is for bug/1634114
|
||||
firewall_group = {'id': 0, 'project_id': 1,
|
||||
'admin_state_up': True,
|
||||
'ports': [1, 2],
|
||||
'add-port-ids': ['1', '2'],
|
||||
'del-port-ids': [],
|
||||
'last-port': False
|
||||
|
@ -272,6 +283,7 @@ class TestFWaaSL3AgentExtension(base.BaseTestCase):
|
|||
def test_delete_firewall_group(self):
|
||||
firewall_group = {'id': 0, 'project_id': 1,
|
||||
'admin_state_up': True,
|
||||
'ports': [3, 4],
|
||||
'add-port-ids': [],
|
||||
'del-port-ids': [3, 4],
|
||||
'last-port': False}
|
||||
|
@ -357,6 +369,7 @@ class TestFWaaSL3AgentExtension(base.BaseTestCase):
|
|||
'status': 'ACTIVE',
|
||||
'admin_state_up': True,
|
||||
'tenant_id': 'demo_tenant_id',
|
||||
'ports': [1],
|
||||
'del-port-ids': [],
|
||||
'add-port-ids': ['1'],
|
||||
'id': '2932b3d9-3a7b-48a1-a16c-bf9f7b2751a5'
|
||||
|
|
|
@ -122,8 +122,7 @@ class TestFirewallRouterPortBase(
|
|||
class TestFirewallCallbacks(TestFirewallRouterPortBase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestFirewallCallbacks,
|
||||
self).setUp(fw_plugin=FW_PLUGIN_KLASS)
|
||||
super(TestFirewallCallbacks, self).setUp(fw_plugin=FW_PLUGIN_KLASS)
|
||||
self.callbacks = self.plugin.endpoints[0]
|
||||
|
||||
def test_set_firewall_group_status(self):
|
||||
|
@ -229,7 +228,7 @@ class TestFirewallCallbacks(TestFirewallRouterPortBase):
|
|||
|
||||
|
||||
class TestFirewallPluginBasev2(TestFirewallRouterPortBase,
|
||||
test_l3_plugin.L3NatTestCaseMixin):
|
||||
test_l3_plugin.L3NatTestCaseMixin):
|
||||
|
||||
def setUp(self):
|
||||
super(TestFirewallPluginBasev2, self).setUp(fw_plugin=FW_PLUGIN_KLASS)
|
||||
|
|
|
@ -49,6 +49,8 @@ tempest.test_plugins =
|
|||
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
|
||||
neutron.agent.l2.firewall_drivers =
|
||||
noop = neutron_fwaas.services.firewall.drivers.linux.l2.noop.noop_driver:NoopFirewallL2Driver
|
||||
neutron.agent.l3.extensions =
|
||||
|
|
Loading…
Reference in New Issue