Firewall as a Service (FWaaS) APIs and DB Model

Implements: blueprint quantum-fwaas

blueprint: quantum-fwaas-plugin

This is the first iteration of the FWaaS implementation and
is geared towards implementing the model that will be
required to at least address the reference implementation.

This iteration will not include implementation of the following
features:
* grouping or dynamic objects
* application/service objects

Change-Id: I57a62d6e9d3f1e6c4dd44cd5c745710a3d9e488e
This commit is contained in:
snaiksat 2013-05-09 21:31:17 -07:00 committed by Sumit Naiksatam
parent c0d3c5864c
commit a5a88c7ed3
14 changed files with 3275 additions and 2 deletions

View File

@ -58,6 +58,26 @@
"create_router:external_gateway_info:enable_snat": "rule:admin_only",
"update_router:external_gateway_info:enable_snat": "rule:admin_only",
"create_firewall": "",
"get_firewall": "rule:admin_or_owner",
"create_firewall:shared": "rule:admin_only",
"get_firewall:shared": "rule:admin_only",
"update_firewall": "rule:admin_or_owner",
"delete_firewall": "rule:admin_or_owner",
"create_firewall_policy": "",
"get_firewall_policy": "rule:admin_or_owner",
"create_firewall_policy:shared": "rule:admin_or_owner",
"update_firewall_policy": "rule:admin_or_owner",
"delete_firewall_policy": "rule:admin_or_owner",
"create_firewall_rule": "",
"get_firewall_rule": "rule:admin_or_owner",
"create_firewall_rule:shared": "rule:admin_or_owner",
"get_firewall_rule:shared": "rule:admin_or_owner",
"update_firewall_rule": "rule:admin_or_owner",
"delete_firewall_rule": "rule:admin_or_owner",
"create_qos_queue": "rule:admin_only",
"get_qos_queue": "rule:admin_only",

View File

@ -25,6 +25,7 @@ UPDATE = 'update'
AGENT = 'q-agent-notifier'
PLUGIN = 'q-plugin'
DHCP = 'q-dhcp-notifer'
FIREWALL_PLUGIN = 'q-firewall-plugin'
L3_AGENT = 'l3_agent'
DHCP_AGENT = 'dhcp_agent'

View File

@ -0,0 +1,16 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2013 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

View File

@ -0,0 +1,470 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2013 Big Switch Networks, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Sumit Naiksatam, sumitnaiksatam@gmail.com, Big Switch Networks, Inc.
import sqlalchemy as sa
from sqlalchemy.ext.orderinglist import ordering_list
from sqlalchemy import orm
from sqlalchemy.orm import exc
from neutron.db import db_base_plugin_v2 as base_db
from neutron.db import model_base
from neutron.db import models_v2
from neutron.extensions import firewall
from neutron import manager
from neutron.openstack.common import log as logging
from neutron.openstack.common import uuidutils
from neutron.plugins.common import constants as const
LOG = logging.getLogger(__name__)
class FirewallRule(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant):
"""Represents a Firewall rule."""
__tablename__ = 'firewall_rules'
name = sa.Column(sa.String(255))
description = sa.Column(sa.String(1024))
firewall_policy_id = sa.Column(sa.String(36),
sa.ForeignKey('firewall_policies.id'),
nullable=True)
shared = sa.Column(sa.Boolean)
protocol = sa.Column(sa.String(40))
ip_version = sa.Column(sa.Integer, nullable=False)
source_ip_address = sa.Column(sa.String(46))
destination_ip_address = sa.Column(sa.String(46))
source_port_range_min = sa.Column(sa.Integer)
source_port_range_max = sa.Column(sa.Integer)
destination_port_range_min = sa.Column(sa.Integer)
destination_port_range_max = sa.Column(sa.Integer)
action = sa.Column(sa.Enum('allow', 'deny', name='firewallrules_action'))
enabled = sa.Column(sa.Boolean)
position = sa.Column(sa.Integer)
class Firewall(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant):
"""Represents a Firewall resource."""
__tablename__ = 'firewalls'
name = sa.Column(sa.String(255))
description = sa.Column(sa.String(1024))
shared = sa.Column(sa.Boolean)
admin_state_up = sa.Column(sa.Boolean)
status = sa.Column(sa.String(16))
firewall_policy_id = sa.Column(sa.String(36),
sa.ForeignKey('firewall_policies.id'),
nullable=True)
class FirewallPolicy(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant):
"""Represents a Firewall Policy resource."""
__tablename__ = 'firewall_policies'
name = sa.Column(sa.String(255))
description = sa.Column(sa.String(1024))
shared = sa.Column(sa.Boolean)
firewall_rules = orm.relationship(
FirewallRule,
backref=orm.backref('firewall_policies', cascade='all, delete'),
order_by='FirewallRule.position',
collection_class=ordering_list('position', count_from=1))
audited = sa.Column(sa.Boolean)
firewalls = orm.relationship(Firewall, backref='firewall_policies')
class Firewall_db_mixin(firewall.FirewallPluginBase, base_db.CommonDbMixin):
"""Mixin class for Firewall DB implementation."""
@property
def _core_plugin(self):
return manager.NeutronManager.get_plugin()
def _get_firewall(self, context, id):
try:
return self._get_by_id(context, Firewall, id)
except exc.NoResultFound:
raise firewall.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)
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)
def _make_firewall_dict(self, fw, fields=None):
res = {'id': fw['id'],
'tenant_id': fw['tenant_id'],
'name': fw['name'],
'description': fw['description'],
'shared': fw['shared'],
'admin_state_up': fw['admin_state_up'],
'status': fw['status'],
'firewall_policy_id': fw['firewall_policy_id']}
return self._fields(res, fields)
def _make_firewall_policy_dict(self, firewall_policy, fields=None):
fw_rules = [rule['id'] for rule in firewall_policy['firewall_rules']]
firewalls = [fw['id'] for fw in firewall_policy['firewalls']]
res = {'id': firewall_policy['id'],
'tenant_id': firewall_policy['tenant_id'],
'name': firewall_policy['name'],
'description': firewall_policy['description'],
'shared': firewall_policy['shared'],
'audited': firewall_policy['audited'],
'firewall_rules': fw_rules,
'firewall_list': firewalls}
return self._fields(res, fields)
def _make_firewall_rule_dict(self, firewall_rule, fields=None):
position = None
# We return the position only if the firewall_rule is bound to a
# firewall_policy.
if firewall_rule['firewall_policy_id']:
position = firewall_rule['position']
src_port_range = self._get_port_range_from_min_max_ports(
firewall_rule['source_port_range_min'],
firewall_rule['source_port_range_max'])
dst_port_range = self._get_port_range_from_min_max_ports(
firewall_rule['destination_port_range_min'],
firewall_rule['destination_port_range_max'])
res = {'id': firewall_rule['id'],
'tenant_id': firewall_rule['tenant_id'],
'name': firewall_rule['name'],
'description': firewall_rule['description'],
'firewall_policy_id': firewall_rule['firewall_policy_id'],
'shared': firewall_rule['shared'],
'protocol': firewall_rule['protocol'],
'ip_version': firewall_rule['ip_version'],
'source_ip_address': firewall_rule['source_ip_address'],
'destination_ip_address':
firewall_rule['destination_ip_address'],
'source_port': src_port_range,
'destination_port': dst_port_range,
'action': firewall_rule['action'],
'position': position,
'enabled': firewall_rule['enabled']}
return self._fields(res, fields)
def _set_rules_for_policy(self, context, firewall_policy_db, rule_id_list):
fwp_db = firewall_policy_db
with context.session.begin(subtransactions=True):
if not rule_id_list:
fwp_db.firewall_rules = []
fwp_db.audited = False
return
# We will first check if the new list of rules is valid
filters = {'id': [r_id for r_id in rule_id_list]}
rules_in_db = self._get_collection_query(context, FirewallRule,
filters=filters)
rules_dict = dict((fwr_db['id'], fwr_db) for fwr_db in rules_in_db)
for fwrule_id in rule_id_list:
if fwrule_id not in rules_dict:
# 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(firewall_rule_id=
fwrule_id)
# New list of rules is valid so we will first reset the existing
# list and then add each rule in order.
# Note that the list could be empty in which case we interpret
# it as clearing existing rules.
fwp_db.firewall_rules = []
for fwrule_id in rule_id_list:
fwp_db.firewall_rules.append(rules_dict[fwrule_id])
fwp_db.audited = False
def _process_rule_for_policy(self, context, firewall_policy_id,
firewall_rule_db, position):
with context.session.begin(subtransactions=True):
fwp_query = context.session.query(
FirewallPolicy).with_lockmode('update')
fwp_db = fwp_query.filter_by(id=firewall_policy_id).one()
if position:
# Note that although position numbering starts at 1,
# internal ordering of the list starts at 0, so we compensate.
fwp_db.firewall_rules.insert(position - 1, firewall_rule_db)
else:
fwp_db.firewall_rules.remove(firewall_rule_db)
fwp_db.firewall_rules.reorder()
fwp_db.audited = False
return self._make_firewall_policy_dict(fwp_db)
def _get_min_max_ports_from_range(self, port_range):
if not port_range:
return [None, None]
ports = port_range.split(':')
ports[0] = int(ports[0])
if len(ports) < 2:
ports.append(ports[0])
else:
ports[1] = int(ports[1])
return ports
def _get_port_range_from_min_max_ports(self, min_port, max_port):
if not min_port:
return None
if min_port == max_port:
return str(min_port)
else:
return str(min_port) + ':' + str(max_port)
def create_firewall(self, context, firewall):
LOG.debug(_("create_firewall() called"))
fw = firewall['firewall']
tenant_id = self._get_tenant_id_for_create(context, fw)
with context.session.begin(subtransactions=True):
firewall_db = Firewall(id=uuidutils.generate_uuid(),
tenant_id=tenant_id,
name=fw['name'],
description=fw['description'],
firewall_policy_id=
fw['firewall_policy_id'],
admin_state_up=fw['admin_state_up'],
status=const.PENDING_CREATE)
context.session.add(firewall_db)
return self._make_firewall_dict(firewall_db)
def update_firewall(self, context, id, firewall):
LOG.debug(_("update_firewall() called"))
fw = firewall['firewall']
with context.session.begin(subtransactions=True):
fw_query = context.session.query(
Firewall).with_lockmode('update')
firewall_db = fw_query.filter_by(id=id).one()
firewall_db.update(fw)
return self._make_firewall_dict(firewall_db)
def delete_firewall(self, context, id):
LOG.debug(_("delete_firewall() called"))
with context.session.begin(subtransactions=True):
fw_query = context.session.query(
Firewall).with_lockmode('update')
firewall_db = fw_query.filter_by(id=id).one()
# Note: Plugin should ensure that it's okay to delete if the
# firewall is active
context.session.delete(firewall_db)
def get_firewall(self, context, id, fields=None):
LOG.debug(_("get_firewall() called"))
fw = self._get_firewall(context, id)
return self._make_firewall_dict(fw, fields)
def get_firewalls(self, context, filters=None, fields=None):
LOG.debug(_("get_firewalls() called"))
return self._get_collection(context, Firewall,
self._make_firewall_dict,
filters=filters, fields=fields)
def get_firewalls_count(self, context, filters=None):
LOG.debug(_("get_firewalls_count() called"))
return self._get_collection_count(context, Firewall,
filters=filters)
def create_firewall_policy(self, context, firewall_policy):
LOG.debug(_("create_firewall_policy() called"))
fwp = firewall_policy['firewall_policy']
tenant_id = self._get_tenant_id_for_create(context, fwp)
with context.session.begin(subtransactions=True):
fwp_db = FirewallPolicy(id=uuidutils.generate_uuid(),
tenant_id=tenant_id,
name=fwp['name'],
description=fwp['description'],
shared=fwp['shared'])
context.session.add(fwp_db)
self._set_rules_for_policy(context, fwp_db,
fwp['firewall_rules'])
fwp_db.audited = fwp['audited']
return self._make_firewall_policy_dict(fwp_db)
def update_firewall_policy(self, context, id, firewall_policy):
LOG.debug(_("update_firewall_policy() called"))
fwp = firewall_policy['firewall_policy']
with context.session.begin(subtransactions=True):
fwp_db = self._get_firewall_policy(context, id)
if 'firewall_rules' in fwp:
self._set_rules_for_policy(context, fwp_db,
fwp['firewall_rules'])
del fwp['firewall_rules']
fwp_db.update(fwp)
return self._make_firewall_policy_dict(fwp_db)
def delete_firewall_policy(self, context, id):
LOG.debug(_("delete_firewall_policy() called"))
with context.session.begin(subtransactions=True):
fwp = self._get_firewall_policy(context, id)
# Ensure that the firewall_policy is not
# being used
qry = context.session.query(Firewall)
if qry.filter_by(firewall_policy_id=id).first():
raise firewall.FirewallPolicyInUse(firewall_policy_id=id)
else:
context.session.delete(fwp)
def get_firewall_policy(self, context, id, fields=None):
LOG.debug(_("get_firewall_policy() called"))
fwp = self._get_firewall_policy(context, id)
return self._make_firewall_policy_dict(fwp, fields)
def get_firewall_policies(self, context, filters=None, fields=None):
LOG.debug(_("get_firewall_policies() called"))
return self._get_collection(context, FirewallPolicy,
self._make_firewall_policy_dict,
filters=filters, fields=fields)
def get_firewalls_policies_count(self, context, filters=None):
LOG.debug(_("get_firewall_policies_count() called"))
return self._get_collection_count(context, FirewallPolicy,
filters=filters)
def create_firewall_rule(self, context, firewall_rule):
LOG.debug(_("create_firewall_rule() called"))
fwr = firewall_rule['firewall_rule']
tenant_id = self._get_tenant_id_for_create(context, fwr)
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(
fwr['destination_port'])
with context.session.begin(subtransactions=True):
fwr_db = FirewallRule(id=uuidutils.generate_uuid(),
tenant_id=tenant_id,
name=fwr['name'],
description=fwr['description'],
shared=fwr['shared'],
protocol=fwr['protocol'],
ip_version=fwr['ip_version'],
source_ip_address=fwr['source_ip_address'],
destination_ip_address=
fwr['destination_ip_address'],
source_port_range_min=src_port_min,
source_port_range_max=src_port_max,
destination_port_range_min=dst_port_min,
destination_port_range_max=dst_port_max,
action=fwr['action'],
enabled=fwr['enabled'])
context.session.add(fwr_db)
return self._make_firewall_rule_dict(fwr_db)
def update_firewall_rule(self, context, id, firewall_rule):
LOG.debug(_("update_firewall_rule() called"))
fwr = firewall_rule['firewall_rule']
if 'source_port' in fwr:
src_port_min, src_port_max = self._get_min_max_ports_from_range(
fwr['source_port'])
fwr['source_port_range_min'] = src_port_min
fwr['source_port_range_max'] = src_port_max
del fwr['source_port']
if 'destination_port' in fwr:
dst_port_min, dst_port_max = self._get_min_max_ports_from_range(
fwr['destination_port'])
fwr['destination_port_range_min'] = dst_port_min
fwr['destination_port_range_max'] = dst_port_max
del fwr['destination_port']
with context.session.begin(subtransactions=True):
fwr_db = self._get_firewall_rule(context, id)
fwr_db.update(fwr)
if fwr_db.firewall_policy_id:
fwp_db = self._get_firewall_policy(context,
fwr_db.firewall_policy_id)
fwp_db.audited = False
return self._make_firewall_rule_dict(fwr_db)
def delete_firewall_rule(self, context, id):
LOG.debug(_("delete_firewall_rule() called"))
with context.session.begin(subtransactions=True):
fwr = self._get_firewall_rule(context, id)
if fwr.firewall_policy_id:
raise firewall.FirewallRuleInUse(firewall_rule_id=id)
context.session.delete(fwr)
def get_firewall_rule(self, context, id, fields=None):
LOG.debug(_("get_firewall_rule() called"))
fwr = self._get_firewall_rule(context, id)
return self._make_firewall_rule_dict(fwr, fields)
def get_firewall_rules(self, context, filters=None, fields=None):
LOG.debug(_("get_firewall_rules() called"))
return self._get_collection(context, FirewallRule,
self._make_firewall_rule_dict,
filters=filters, fields=fields)
def get_firewalls_rules_count(self, context, filters=None):
LOG.debug(_("get_firewall_rules_count() called"))
return self._get_collection_count(context, FirewallRule,
filters=filters)
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()
def insert_rule(self, context, id, rule_info):
LOG.debug(_("insert_rule() called"))
self._validate_insert_remove_rule_request(id, rule_info)
firewall_rule_id = rule_info['firewall_rule_id']
insert_before = True
ref_firewall_rule_id = None
if not firewall_rule_id:
raise firewall.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:
# If insert_before is set, we will ignore insert_after.
ref_firewall_rule_id = rule_info['insert_after']
insert_before = False
with context.session.begin(subtransactions=True):
fwr_db = self._get_firewall_rule(context, firewall_rule_id)
if fwr_db.firewall_policy_id:
raise firewall.FirewallRuleInUse(firewall_rule_id=fwr_db['id'])
if ref_firewall_rule_id:
# If reference_firewall_rule_id is set, the new rule
# is inserted depending on the value of insert_before.
# If insert_before is set, the new rule is inserted before
# reference_firewall_rule_id, and if it is not set the new
# rule is inserted after reference_firewall_rule_id.
ref_fwr_db = self._get_firewall_rule(
context, ref_firewall_rule_id)
if insert_before:
position = ref_fwr_db.position
else:
position = ref_fwr_db.position + 1
else:
# If reference_firewall_rule_id is not set, it is assumed
# that the new rule needs to be inserted at the top.
# insert_before field is ignored.
# So default insertion is always at the top.
# Also note that position numbering starts at 1.
position = 1
return self._process_rule_for_policy(context, id, fwr_db,
position)
def remove_rule(self, context, id, rule_info):
LOG.debug(_("remove_rule() called"))
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)
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(
firewall_rule_id=fwr_db['id'],
firewall_policy_id=id)
return self._process_rule_for_policy(context, id, fwr_db, None)

View File

@ -0,0 +1,105 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2013 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 Havana-2 model
Revision ID: 39cf3f799352
Revises: e6b16a30d97
Create Date: 2013-07-10 16:16:51.302943
"""
# revision identifiers, used by Alembic.
revision = '39cf3f799352'
down_revision = 'e6b16a30d97'
# Change to ['*'] if this migration applies to all plugins
migration_for_plugins = ['*']
from alembic import op
import sqlalchemy as sa
from neutron.db import migration
def downgrade(active_plugin=None, options=None):
if not migration.should_run(active_plugin, migration_for_plugins):
return
op.drop_table('firewall_rules')
op.drop_table('firewalls')
op.drop_table('firewall_policies')
def upgrade(active_plugin=None, options=None):
if not migration.should_run(active_plugin, migration_for_plugins):
return
op.create_table(
'firewall_policies',
sa.Column('tenant_id', sa.String(length=255), nullable=True),
sa.Column('id', sa.String(length=36), nullable=False),
sa.Column('name', sa.String(length=255), nullable=True),
sa.Column('description', sa.String(length=1024), nullable=True),
sa.Column('shared', sa.Boolean(), autoincrement=False, nullable=True),
sa.Column('audited', sa.Boolean(), autoincrement=False,
nullable=True),
sa.PrimaryKeyConstraint('id'))
op.create_table(
'firewalls', sa.Column('tenant_id', sa.String(length=255),
nullable=True),
sa.Column('id', sa.String(length=36), nullable=False),
sa.Column('name', sa.String(length=255), nullable=True),
sa.Column('description', sa.String(length=1024), nullable=True),
sa.Column('shared', sa.Boolean(), autoincrement=False, nullable=True),
sa.Column('admin_state_up', sa.Boolean(), autoincrement=False,
nullable=True),
sa.Column('status', sa.String(length=16), nullable=True),
sa.Column('firewall_policy_id', sa.String(length=36), nullable=True),
sa.ForeignKeyConstraint(['firewall_policy_id'],
['firewall_policies.id'],
name='firewalls_ibfk_1'),
sa.PrimaryKeyConstraint('id'))
op.create_table(
'firewall_rules',
sa.Column('tenant_id', sa.String(length=255), nullable=True),
sa.Column('id', sa.String(length=36), nullable=False),
sa.Column('name', sa.String(length=255), nullable=True),
sa.Column('description', sa.String(length=1024), nullable=True),
sa.Column('firewall_policy_id', sa.String(length=36), nullable=True),
sa.Column('shared', sa.Boolean(), autoincrement=False,
nullable=True),
sa.Column('protocol', sa.String(length=24), nullable=True),
sa.Column('ip_version', sa.Integer(), autoincrement=False,
nullable=False),
sa.Column('source_ip_address', sa.String(length=46), nullable=True),
sa.Column('destination_ip_address', sa.String(length=46),
nullable=True),
sa.Column('source_port_range_min', sa.Integer(), nullable=True),
sa.Column('source_port_range_max', sa.Integer(), nullable=True),
sa.Column('destination_port_range_min', sa.Integer(), nullable=True),
sa.Column('destination_port_range_max', sa.Integer(), nullable=True),
sa.Column('action', sa.Enum(), nullable=True),
sa.Column('enabled', sa.Boolean(), autoincrement=False,
nullable=True),
sa.Column('position', sa.Integer(), autoincrement=False,
nullable=True),
sa.ForeignKeyConstraint(['firewall_policy_id'],
['firewall_policies.id'],
name='firewall_rules_ibfk_1'),
sa.PrimaryKeyConstraint('id'))

View File

@ -0,0 +1,448 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2013 Big Switch Networks, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Sumit Naiksatam, sumitnaiksatam@gmail.com, Big Switch Networks, Inc.
import abc
from oslo.config import cfg
from neutron.api import extensions
from neutron.api.v2 import attributes as attr
from neutron.api.v2 import base
from neutron.common import exceptions as qexception
from neutron import manager
from neutron.openstack.common import log as logging
from neutron.plugins.common import constants
from neutron.services.service_base import ServicePluginBase
LOG = logging.getLogger(__name__)
# Firewall Exceptions
class FirewallNotFound(qexception.NotFound):
message = _("Firewall %(firewall_id)s could not be found.")
class FirewallInUse(qexception.InUse):
message = _("Firewall %(firewall_id)s is still active.")
class FirewallInPendingState(qexception.Conflict):
message = _("Operation cannot be performed since associated Firewall "
"%(firewall_id)s is in %(pending_state)s.")
class FirewallPolicyNotFound(qexception.NotFound):
message = _("Firewall Policy %(firewall_policy_id)s could not be found.")
class FirewallPolicyInUse(qexception.InUse):
message = _("Firewall Policy %(firewall_policy_id)s is being used.")
class FirewallRuleNotFound(qexception.NotFound):
message = _("Firewall Rule %(firewall_rule_id)s could not be found.")
class FirewallRuleInUse(qexception.InUse):
message = _("Firewall Rule %(firewall_rule_id)s is being used.")
class FirewallRuleNotAssociatedWithPolicy(qexception.InvalidInput):
message = _("Firewall Rule %(firewall_rule_id)s is not associated "
" with Firewall Policy %(firewall_policy_id)s.")
class FirewallRuleInvalidProtocol(qexception.InvalidInput):
message = _("Firewall Rule protocol %(protocol)s is not supported. "
"Only protocol values %(values)s and their integer "
"representation (0 to 255) are supported.")
class FirewallRuleInvalidAction(qexception.InvalidInput):
message = _("Firewall rule action %(action)s is not supported. "
"Only action values %(values)s are supported.")
class FirewallInvalidPortValue(qexception.InvalidInput):
message = _("Invalid value for port %(port)s.")
class FirewallRuleInfoMissing(qexception.InvalidInput):
message = _("Missing rule info argument for insert/remove "
"rule opertaion.")
fw_valid_protocol_values = [None, constants.TCP, constants.UDP, constants.ICMP]
fw_valid_action_values = [constants.FWAAS_ALLOW, constants.FWAAS_DENY]
def convert_protocol(value):
if value is None:
return
if value.isdigit():
val = int(value)
if 0 <= val <= 255:
return val
else:
raise FirewallRuleInvalidProtocol(protocol=value,
values=
fw_valid_protocol_values)
elif value.lower() in fw_valid_protocol_values:
return value.lower()
else:
raise FirewallRuleInvalidProtocol(protocol=value,
values=
fw_valid_protocol_values)
def convert_action_to_case_insensitive(value):
if value is None:
return
else:
return value.lower()
def convert_port_to_string(value):
if value is None:
return
else:
return str(value)
def _validate_port_range(data, key_specs=None):
if data is None:
return
data = str(data)
ports = data.split(':')
for p in ports:
try:
val = int(p)
except (ValueError, TypeError):
msg = _("Port '%s' is not a valid number") % p
LOG.debug(msg)
return msg
if val <= 0 or val > 65535:
msg = _("Invalid port '%s'") % p
LOG.debug(msg)
return msg
def _validate_ip_or_subnet_or_none(data, valid_values=None):
if data is None:
return None
msg_ip = attr._validate_ip_address(data, valid_values)
if not msg_ip:
return
msg_subnet = attr._validate_subnet(data, valid_values)
if not msg_subnet:
return
return _("%(msg_ip)s and %(msg_subnet)s") % {'msg_ip': msg_ip,
'msg_subnet': msg_subnet}
attr.validators['type:port_range'] = _validate_port_range
attr.validators['type:ip_or_subnet_or_none'] = _validate_ip_or_subnet_or_none
RESOURCE_ATTRIBUTE_MAP = {
'firewall_rules': {
'id': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None},
'is_visible': True, 'primary_key': True},
'tenant_id': {'allow_post': True, 'allow_put': False,
'required_by_policy': True,
'is_visible': True},
'name': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'is_visible': True, 'default': ''},
'description': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'is_visible': True, 'default': ''},
'firewall_policy_id': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid_or_none': None},
'is_visible': True},
'shared': {'allow_post': True, 'allow_put': True,
'default': False, 'convert_to': attr.convert_to_boolean,
'is_visible': True, 'required_by_policy': True,
'enforce_policy': True},
'protocol': {'allow_post': True, 'allow_put': True,
'is_visible': True, 'default': None,
'convert_to': convert_protocol,
'validate': {'type:values': fw_valid_protocol_values}},
'ip_version': {'allow_post': True, 'allow_put': True,
'default': 4, 'convert_to': attr.convert_to_int,
'validate': {'type:values': [4, 6]},
'is_visible': True},
'source_ip_address': {'allow_post': True, 'allow_put': True,
'validate': {'type:ip_or_subnet_or_none': None},
'is_visible': True, 'default': None},
'destination_ip_address': {'allow_post': True, 'allow_put': True,
'validate': {'type:ip_or_subnet_or_none':
None},
'is_visible': True, 'default': None},
'source_port': {'allow_post': True, 'allow_put': True,
'validate': {'type:port_range': None},
'convert_to': convert_port_to_string,
'default': None, 'is_visible': True},
'destination_port': {'allow_post': True, 'allow_put': True,
'validate': {'type:port_range': None},
'convert_to': convert_port_to_string,
'default': None, 'is_visible': True},
'position': {'allow_post': False, 'allow_put': False,
'default': None, 'is_visible': True},
'action': {'allow_post': True, 'allow_put': True,
'convert_to': convert_action_to_case_insensitive,
'validate': {'type:values': fw_valid_action_values},
'is_visible': True, 'default': 'deny'},
'enabled': {'allow_post': True, 'allow_put': True,
'default': True, 'convert_to': attr.convert_to_boolean,
'is_visible': True},
},
'firewall_policies': {
'id': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None},
'is_visible': True,
'primary_key': True},
'tenant_id': {'allow_post': True, 'allow_put': False,
'required_by_policy': True,
'is_visible': True},
'name': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'is_visible': True, 'default': ''},
'description': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'is_visible': True, 'default': ''},
'shared': {'allow_post': True, 'allow_put': True,
'default': False, 'convert_to': attr.convert_to_boolean,
'is_visible': True, 'required_by_policy': True,
'enforce_policy': True},
'firewall_rules': {'allow_post': True, 'allow_put': True,
'convert_to': attr.convert_none_to_empty_list,
'default': None, 'is_visible': True},
'audited': {'allow_post': True, 'allow_put': True,
'default': False, 'convert_to': attr.convert_to_boolean,
'is_visible': True},
},
'firewalls': {
'id': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None},
'is_visible': True,
'primary_key': True},
'tenant_id': {'allow_post': True, 'allow_put': False,
'required_by_policy': True,
'is_visible': True},
'name': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'is_visible': True, 'default': ''},
'description': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'is_visible': True, 'default': ''},
'admin_state_up': {'allow_post': True, 'allow_put': True,
'default': True,
'convert_to': attr.convert_to_boolean,
'is_visible': True},
'status': {'allow_post': False, 'allow_put': False,
'is_visible': True},
'shared': {'allow_post': True, 'allow_put': True,
'default': False, 'convert_to': attr.convert_to_boolean,
'is_visible': False, 'required_by_policy': True,
'enforce_policy': True},
'firewall_policy_id': {'allow_post': True, 'allow_put': True,
'validate': {'type:uuid_or_none': None},
'is_visible': True},
},
}
firewall_quota_opts = [
cfg.IntOpt('quota_firewall',
default=1,
help=_('Number of firewalls allowed per tenant, -1 for '
'unlimited')),
cfg.IntOpt('quota_firewall_policy',
default=1,
help=_('Number of firewall policies allowed per tenant, -1 '
'for unlimited')),
cfg.IntOpt('quota_firewall_rule',
default=-1,
help=_('Number of firewall rules allowed per tenant, -1 '
'for unlimited')),
]
cfg.CONF.register_opts(firewall_quota_opts, 'QUOTAS')
class Firewall(extensions.ExtensionDescriptor):
@classmethod
def get_name(cls):
return "Firewall service"
@classmethod
def get_alias(cls):
return "fwaas"
@classmethod
def get_description(cls):
return "Extension for Firewall service"
@classmethod
def get_namespace(cls):
return "http://wiki.openstack.org/Neutron/FWaaS/API_1.0"
@classmethod
def get_updated(cls):
return "2013-02-25T10:00:00-00:00"
@classmethod
def get_resources(cls):
my_plurals = []
for plural in RESOURCE_ATTRIBUTE_MAP:
if plural == 'firewall_policies':
singular = 'firewall_policy'
else:
singular = plural[:-1]
my_plurals.append((plural, singular))
attr.PLURALS.update(dict(my_plurals))
resources = []
plugin = manager.NeutronManager.get_service_plugins()[
constants.FIREWALL]
for collection_name in RESOURCE_ATTRIBUTE_MAP:
# Special handling needed for resources with 'y' ending
if collection_name == 'firewall_policies':
resource_name = 'firewall_policy'
else:
resource_name = collection_name[:-1]
params = RESOURCE_ATTRIBUTE_MAP[collection_name]
member_actions = {}
if resource_name == 'firewall_policy':
member_actions = {'insert_rule': 'PUT',
'remove_rule': 'PUT'}
controller = base.create_resource(
collection_name, resource_name, plugin, params,
member_actions=member_actions,
allow_pagination=cfg.CONF.allow_pagination,
allow_sorting=cfg.CONF.allow_sorting)
resource = extensions.ResourceExtension(
collection_name,
controller,
path_prefix=constants.COMMON_PREFIXES[constants.FIREWALL],
member_actions=member_actions,
attr_map=params)
resources.append(resource)
return resources
@classmethod
def get_plugin_interface(cls):
return FirewallPluginBase
def update_attributes_map(self, attributes):
super(Firewall, self).update_attributes_map(
attributes, extension_attrs_map=RESOURCE_ATTRIBUTE_MAP)
def get_extended_resources(self, version):
if version == "2.0":
return RESOURCE_ATTRIBUTE_MAP
else:
return {}
class FirewallPluginBase(ServicePluginBase):
__metaclass__ = abc.ABCMeta
def get_plugin_name(self):
return constants.FIREWALL
def get_plugin_type(self):
return constants.FIREWALL
def get_plugin_description(self):
return 'Firewall service plugin'
@abc.abstractmethod
def get_firewalls(self, context, filters=None, fields=None):
pass
@abc.abstractmethod
def get_firewall(self, context, id, fields=None):
pass
@abc.abstractmethod
def create_firewall(self, context, firewall):
pass
@abc.abstractmethod
def update_firewall(self, context, id, firewall):
pass
@abc.abstractmethod
def delete_firewall(self, context, id):
pass
@abc.abstractmethod
def get_firewall_rules(self, context, filters=None, fields=None):
pass
@abc.abstractmethod
def get_firewall_rule(self, context, id, fields=None):
pass
@abc.abstractmethod
def create_firewall_rule(self, context, firewall_rule):
pass
@abc.abstractmethod
def update_firewall_rule(self, context, id, firewall_rule):
pass
@abc.abstractmethod
def delete_firewall_rule(self, context, id):
pass
@abc.abstractmethod
def get_firewall_policy(self, context, id, fields=None):
pass
@abc.abstractmethod
def get_firewall_policies(self, context, filters=None, fields=None):
pass
@abc.abstractmethod
def create_firewall_policy(self, context, firewall_policy):
pass
@abc.abstractmethod
def update_firewall_policy(self, context, id, firewall_policy):
pass
@abc.abstractmethod
def delete_firewall_policy(self, context, id):
pass
@abc.abstractmethod
def insert_rule(self, context, id, rule_info):
pass
@abc.abstractmethod
def remove_rule(self, context, id, rule_info):
pass

View File

@ -19,20 +19,23 @@
CORE = "CORE"
DUMMY = "DUMMY"
LOADBALANCER = "LOADBALANCER"
FIREWALL = "FIREWALL"
#maps extension alias to service type
EXT_TO_SERVICE_MAPPING = {
'dummy': DUMMY,
'lbaas': LOADBALANCER
'lbaas': LOADBALANCER,
'fwaas': FIREWALL
}
# TODO(salvatore-orlando): Move these (or derive them) from conf file
ALLOWED_SERVICES = [CORE, DUMMY, LOADBALANCER]
ALLOWED_SERVICES = [CORE, DUMMY, LOADBALANCER, FIREWALL]
COMMON_PREFIXES = {
CORE: "",
DUMMY: "/dummy_svc",
LOADBALANCER: "/lb",
FIREWALL: "/fw",
}
# Service operation status constants
@ -42,3 +45,12 @@ PENDING_UPDATE = "PENDING_UPDATE"
PENDING_DELETE = "PENDING_DELETE"
INACTIVE = "INACTIVE"
ERROR = "ERROR"
# FWaaS firewall rule action
FWAAS_ALLOW = "allow"
FWAAS_DENY = "deny"
# L3 Protocol name constants
TCP = "tcp"
UDP = "udp"
ICMP = "icmp"

View File

@ -0,0 +1,16 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2013 OpenStack Foundation.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

View File

@ -0,0 +1,285 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2013 Big Switch Networks, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Sumit Naiksatam, sumitnaiksatam@gmail.com, Big Switch Networks, Inc.
from oslo.config import cfg
from neutron.common import rpc as q_rpc
from neutron.common import topics
from neutron.db import api as qdbapi
from neutron.db.firewall import firewall_db
from neutron.extensions import firewall as fw_ext
from neutron.openstack.common import log as logging
from neutron.openstack.common import rpc
from neutron.openstack.common.rpc import proxy
from neutron.plugins.common import constants as const
LOG = logging.getLogger(__name__)
class FirewallCallbacks(object):
RPC_API_VERSION = '1.0'
def __init__(self, plugin):
self.plugin = plugin
def create_rpc_dispatcher(self):
return q_rpc.PluginRpcDispatcher([self])
def set_firewall_status(self, context, firewall_id, status, **kwargs):
"""Agent uses this to set a firewall's status."""
LOG.debug(_("set_firewall_status() called"))
with context.session.begin(subtransactions=True):
fw_db = self.plugin._get_firewall(context, firewall_id)
if status in (const.ACTIVE, const.INACTIVE):
fw_db.status = status
return True
else:
fw_db.status = const.ERROR
return False
def firewall_deleted(self, context, firewall_id, **kwargs):
"""Agent uses this to indicate firewall is deleted."""
LOG.debug(_("firewall_deleted() called"))
with context.session.begin(subtransactions=True):
fw_db = self.plugin._get_firewall(context, firewall_id)
if fw_db.status == const.PENDING_DELETE:
self.plugin.delete_db_firewall_object(context, firewall_id)
return True
else:
fw_db.status = const.ERROR
LOG.warn(_('Firewall %s unexpectedly deleted by agent.'),
firewall_id)
return False
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)
]
return fw_list
def get_firewalls_for_tenant_without_rules(self, context, **kwargs):
"""Agent uses this to get all firewalls for a tenant."""
LOG.debug(_("get_firewalls_for_tenant_without_rules() called"))
fw_list = [fw for fw in self.plugin.get_firewalls(context)]
return fw_list
class FirewallAgentApi(proxy.RpcProxy):
"""Plugin side of plugin to agent RPC API."""
API_VERSION = '1.0'
def __init__(self, topic, host):
super(FirewallAgentApi, self).__init__(topic, self.API_VERSION)
self.host = host
def create_firewall(self, context, firewall):
return self.fanout_cast(
context,
self.make_msg('create_firewall', firewall=firewall,
host=self.host),
topic=self.topic
)
def update_firewall(self, context, firewall):
return self.fanout_cast(
context,
self.make_msg('update_firewall', firewall=firewall,
host=self.host),
topic=self.topic
)
def delete_firewall(self, context, firewall):
return self.fanout_cast(
context,
self.make_msg('delete_firewall', firewall=firewall,
host=self.host),
topic=self.topic
)
class FirewallPlugin(firewall_db.Firewall_db_mixin):
"""Implementation of the Neutron Firewall Service Plugin.
This class manages the workflow of FWaaS request/response.
Most DB related works are implemented in class
firewall_db.Firewall_db_mixin.
"""
supported_extension_aliases = ["fwaas"]
def __init__(self):
"""Do the initialization for the firewall service plugin here."""
qdbapi.register_models()
self.callbacks = FirewallCallbacks(self)
self.conn = rpc.create_connection(new=True)
self.conn.create_consumer(
topics.FIREWALL_PLUGIN,
self.callbacks.create_rpc_dispatcher(),
fanout=False)
self.conn.consume_in_thread()
self.agent_rpc = FirewallAgentApi(
topics.L3_AGENT,
cfg.CONF.host
)
def _make_firewall_dict_with_rules(self, context, firewall_id):
firewall = self.get_firewall(context, firewall_id)
fw_policy_id = firewall['firewall_policy_id']
if fw_policy_id:
fw_policy = self.get_firewall_policy(context, fw_policy_id)
fw_rules_list = [self.get_firewall_rule(
context, rule_id) for rule_id in fw_policy['firewall_rules']]
firewall['firewall_rule_list'] = fw_rules_list
else:
firewall['firewall_rule_list'] = []
# FIXME(Sumit): If the size of the firewall object we are creating
# here exceeds the largest message size supported by rabbit/qpid
# then we will have a problem.
return firewall
def _rpc_update_firewall(self, context, firewall_id):
status_update = {"firewall": {"status": const.PENDING_UPDATE}}
fw = super(FirewallPlugin, self).update_firewall(context, firewall_id,
status_update)
if fw:
fw_with_rules = (
self._make_firewall_dict_with_rules(context,
firewall_id))
self.agent_rpc.update_firewall(context, fw_with_rules)
def _rpc_update_firewall_policy(self, context, firewall_policy_id):
firewall_policy = self.get_firewall_policy(context, firewall_policy_id)
if firewall_policy:
for firewall_id in firewall_policy['firewall_list']:
self._rpc_update_firewall(context, firewall_id)
def _ensure_update_firewall(self, context, firewall_id):
fwall = self.get_firewall(context, firewall_id)
if fwall['status'] in [const.PENDING_CREATE,
const.PENDING_UPDATE,
const.PENDING_DELETE]:
raise fw_ext.FirewallInPendingState(firewall_id=firewall_id,
pending_state=fwall['status'])
def _ensure_update_firewall_policy(self, context, firewall_policy_id):
firewall_policy = self.get_firewall_policy(context, firewall_policy_id)
if firewall_policy and 'firewall_list' in firewall_policy:
for firewall_id in firewall_policy['firewall_list']:
self._ensure_update_firewall(context, firewall_id)
def _ensure_update_or_delete_firewall_rule(self, context,
firewall_rule_id):
fw_rule = self.get_firewall_rule(context, firewall_rule_id)
if 'firewall_policy_id' in fw_rule and fw_rule['firewall_policy_id']:
self._ensure_update_firewall_policy(context,
fw_rule['firewall_policy_id'])
def create_firewall(self, context, firewall):
LOG.debug(_("create_firewall() called"))
firewall['firewall']['status'] = const.PENDING_CREATE
fw = super(FirewallPlugin, self).create_firewall(context, firewall)
fw_with_rules = (
self._make_firewall_dict_with_rules(context, fw['id']))
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)
fw_with_rules = (
self._make_firewall_dict_with_rules(context, fw['id']))
self.agent_rpc.update_firewall(context, fw_with_rules)
return fw
def delete_db_firewall_object(self, context, id):
firewall = self.get_firewall(context, id)
if firewall['status'] in [const.PENDING_DELETE]:
super(FirewallPlugin, self).delete_firewall(context, id)
def delete_firewall(self, context, id):
LOG.debug(_("delete_firewall() called"))
status_update = {"firewall": {"status": const.PENDING_DELETE}}
fw = super(FirewallPlugin, self).update_firewall(context, id,
status_update)
fw_with_rules = (
self._make_firewall_dict_with_rules(context, fw['id']))
self.agent_rpc.delete_firewall(context, fw_with_rules)
def update_firewall_policy(self, context, id, firewall_policy):
LOG.debug(_("update_firewall_policy() called"))
self._ensure_update_firewall_policy(context, id)
fwp = super(FirewallPlugin,
self).update_firewall_policy(context, id, firewall_policy)
self._rpc_update_firewall_policy(context, id)
return fwp
def update_firewall_rule(self, context, id, firewall_rule):
LOG.debug(_("update_firewall_rule() called"))
self._ensure_update_or_delete_firewall_rule(context, id)
fwr = super(FirewallPlugin,
self).update_firewall_rule(context, id, firewall_rule)
firewall_policy_id = fwr['firewall_policy_id']
if firewall_policy_id:
self._rpc_update_firewall_policy(context, firewall_policy_id)
return fwr
def delete_firewall_rule(self, context, id):
LOG.debug(_("delete_firewall_rule() called"))
self._ensure_update_or_delete_firewall_rule(context, id)
fwr = self.get_firewall_rule(context, id)
firewall_policy_id = fwr['firewall_policy_id']
super(FirewallPlugin, self).delete_firewall_rule(context, id)
# At this point we have already deleted the rule in the DB,
# however it's still not deleted on the backend firewall.
# Until it gets deleted on the backend we will be setting
# the firewall in PENDING_UPDATE state. The backend firewall
# implementation is responsible for setting the appropriate
# configuration (e.g. do not allow any traffic) until the rule
# is deleted. Once the rule is deleted, the backend should put
# the firewall back in ACTIVE state. While the firewall is in
# PENDING_UPDATE state, the firewall behavior might differ based
# on the backend implementation.
if firewall_policy_id:
self._rpc_update_firewall_policy(context, firewall_policy_id)
def insert_rule(self, context, id, rule_info):
LOG.debug(_("insert_rule() called"))
self._ensure_update_firewall_policy(context, id)
fwp = super(FirewallPlugin,
self).insert_rule(context, id, rule_info)
self._rpc_update_firewall_policy(context, id)
return fwp
def remove_rule(self, context, id, rule_info):
LOG.debug(_("remove_rule() called"))
self._ensure_update_firewall_policy(context, id)
fwp = super(FirewallPlugin,
self).remove_rule(context, id, rule_info)
self._rpc_update_firewall_policy(context, id)
return fwp

View File

@ -0,0 +1,15 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2013 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.

View File

@ -0,0 +1,965 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2013 Big Switch Networks, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the spec
#
# @author: Sumit Naiksatam, sumitnaiksatam@gmail.com, Big Switch Networks, Inc.
import contextlib
import logging
import webob.exc
from neutron.api import extensions as api_ext
from neutron.common import config
from neutron import context
from neutron.db.firewall import firewall_db as fdb
import neutron.extensions
from neutron.extensions import firewall
from neutron.openstack.common import importutils
from neutron.plugins.common import constants
from neutron.tests.unit import test_db_plugin
LOG = logging.getLogger(__name__)
DB_FW_PLUGIN_KLASS = (
"neutron.db.firewall.firewall_db.Firewall_db_mixin"
)
extensions_path = ':'.join(neutron.extensions.__path__)
DESCRIPTION = 'default description'
SHARED = True
PROTOCOL = 'tcp'
IP_VERSION = 4
SOURCE_IP_ADDRESS_RAW = '1.1.1.1'
DESTINATION_IP_ADDRESS_RAW = '2.2.2.2'
SOURCE_PORT = '55000:56000'
DESTINATION_PORT = '56000:57000'
ACTION = 'allow'
AUDITED = True
ENABLED = True
ADMIN_STATE_UP = True
class FirewallPluginDbTestCase(test_db_plugin.NeutronDbPluginV2TestCase):
resource_prefix_map = dict(
(k, constants.COMMON_PREFIXES[constants.FIREWALL])
for k in firewall.RESOURCE_ATTRIBUTE_MAP.keys()
)
def setUp(self, core_plugin=None, fw_plugin=None):
if not fw_plugin:
fw_plugin = DB_FW_PLUGIN_KLASS
service_plugins = {'fw_plugin_name': fw_plugin}
fdb.Firewall_db_mixin.supported_extension_aliases = ["fwaas"]
super(FirewallPluginDbTestCase, self).setUp(
service_plugins=service_plugins
)
self.plugin = importutils.import_object(fw_plugin)
ext_mgr = api_ext.PluginAwareExtensionManager(
extensions_path,
{constants.FIREWALL: self.plugin}
)
app = config.load_paste_app('extensions_test_app')
self.ext_api = api_ext.ExtensionMiddleware(app, ext_mgr=ext_mgr)
def _test_list_resources(self, resource, items,
neutron_context=None,
query_params=None):
if resource.endswith('y'):
resource_plural = resource.replace('y', 'ies')
else:
resource_plural = resource + 's'
res = self._list(resource_plural,
neutron_context=neutron_context,
query_params=query_params)
resource = resource.replace('-', '_')
self.assertEqual(sorted([i['id'] for i in res[resource_plural]]),
sorted([i[resource]['id'] for i in items]))
def _get_test_firewall_rule_attrs(self, name='firewall_rule1'):
attrs = {'name': name,
'tenant_id': self._tenant_id,
'shared': SHARED,
'protocol': PROTOCOL,
'ip_version': IP_VERSION,
'source_ip_address': SOURCE_IP_ADDRESS_RAW,
'destination_ip_address': DESTINATION_IP_ADDRESS_RAW,
'source_port': SOURCE_PORT,
'destination_port': DESTINATION_PORT,
'action': ACTION,
'enabled': ENABLED}
return attrs
def _get_test_firewall_policy_attrs(self, name='firewall_policy1'):
attrs = {'name': name,
'description': DESCRIPTION,
'tenant_id': self._tenant_id,
'shared': SHARED,
'firewall_rules': [],
'audited': AUDITED}
return attrs
def _get_test_firewall_attrs(self, name='firewall_1'):
attrs = {'name': name,
'tenant_id': self._tenant_id,
'admin_state_up': ADMIN_STATE_UP,
'status': 'PENDING_CREATE'}
return attrs
def _create_firewall_policy(self, fmt, name, description, shared,
firewall_rules, audited,
expected_res_status=None, **kwargs):
data = {'firewall_policy': {'name': name,
'description': description,
'tenant_id': self._tenant_id,
'shared': shared,
'firewall_rules': firewall_rules,
'audited': audited}}
fw_policy_req = self.new_create_request('firewall_policies', data, fmt)
fw_policy_res = fw_policy_req.get_response(self.ext_api)
if expected_res_status:
self.assertEqual(fw_policy_res.status_int, expected_res_status)
return fw_policy_res
def _replace_firewall_status(self, attrs, old_status, new_status):
if attrs['status'] is old_status:
attrs['status'] = new_status
return attrs
@contextlib.contextmanager
def firewall_policy(self, fmt=None, name='firewall_policy1',
description=DESCRIPTION, shared=True,
firewall_rules=None, audited=True,
no_delete=False, **kwargs):
if firewall_rules is None:
firewall_rules = []
if not fmt:
fmt = self.fmt
res = self._create_firewall_policy(fmt, name, description, shared,
firewall_rules, audited,
**kwargs)
if res.status_int >= 400:
raise webob.exc.HTTPClientError(code=res.status_int)
firewall_policy = self.deserialize(fmt or self.fmt, res)
try:
yield firewall_policy
finally:
if not no_delete:
self._delete('firewall_policies',
firewall_policy['firewall_policy']['id'])
def _create_firewall_rule(self, fmt, name, shared, protocol,
ip_version, source_ip_address,
destination_ip_address, source_port,
destination_port, action, enabled,
expected_res_status=None, **kwargs):
data = {'firewall_rule': {'name': name,
'tenant_id': self._tenant_id,
'shared': shared,
'protocol': protocol,
'ip_version': ip_version,
'source_ip_address': source_ip_address,
'destination_ip_address':
destination_ip_address,
'source_port': source_port,
'destination_port': destination_port,
'action': action,
'enabled': enabled}}
fw_rule_req = self.new_create_request('firewall_rules', data, fmt)
fw_rule_res = fw_rule_req.get_response(self.ext_api)
if expected_res_status:
self.assertEqual(fw_rule_res.status_int, expected_res_status)
return fw_rule_res
@contextlib.contextmanager
def firewall_rule(self, fmt=None, name='firewall_rule1',
shared=SHARED, protocol=PROTOCOL, ip_version=IP_VERSION,
source_ip_address=SOURCE_IP_ADDRESS_RAW,
destination_ip_address=DESTINATION_IP_ADDRESS_RAW,
source_port=SOURCE_PORT,
destination_port=DESTINATION_PORT,
action=ACTION, enabled=ENABLED,
no_delete=False, **kwargs):
if not fmt:
fmt = self.fmt
res = self._create_firewall_rule(fmt, name, shared, protocol,
ip_version, source_ip_address,
destination_ip_address,
source_port, destination_port,
action, enabled, **kwargs)
if res.status_int >= 400:
raise webob.exc.HTTPClientError(code=res.status_int)
firewall_rule = self.deserialize(fmt or self.fmt, res)
try:
yield firewall_rule
finally:
if not no_delete:
self._delete('firewall_rules',
firewall_rule['firewall_rule']['id'])
def _create_firewall(self, fmt, name, description, firewall_policy_id,
admin_state_up=True, expected_res_status=None,
**kwargs):
data = {'firewall': {'name': name,
'description': description,
'firewall_policy_id': firewall_policy_id,
'admin_state_up': admin_state_up,
'tenant_id': self._tenant_id}}
firewall_req = self.new_create_request('firewalls', data, fmt)
firewall_res = firewall_req.get_response(self.ext_api)
if expected_res_status:
self.assertEqual(firewall_res.status_int, expected_res_status)
return firewall_res
@contextlib.contextmanager
def firewall(self, fmt=None, name='firewall_1', description=DESCRIPTION,
firewall_policy_id=None, admin_state_up=True,
no_delete=False, **kwargs):
if not fmt:
fmt = self.fmt
res = self._create_firewall(fmt, name, description, firewall_policy_id,
admin_state_up, **kwargs)
if res.status_int >= 400:
raise webob.exc.HTTPClientError(code=res.status_int)
firewall = self.deserialize(fmt or self.fmt, res)
try:
yield firewall
finally:
if not no_delete:
self._delete('firewalls', firewall['firewall']['id'])
def _rule_action(self, action, id, firewall_rule_id, insert_before=None,
insert_after=None, expected_code=webob.exc.HTTPOk.code,
expected_body=None, body_data=None):
# We intentionally do this check for None since we want to distinguish
# from empty dictionary
if body_data is None:
if action == 'insert':
body_data = {'firewall_rule_id': firewall_rule_id,
'insert_before': insert_before,
'insert_after': insert_after}
else:
body_data = {'firewall_rule_id': firewall_rule_id}
req = self.new_action_request('firewall_policies',
body_data, id,
"%s_rule" % action)
res = req.get_response(self.ext_api)
self.assertEqual(res.status_int, expected_code)
response = self.deserialize(self.fmt, res)
if expected_body:
self.assertEqual(response, expected_body)
return response
def _compare_firewall_rule_lists(self, firewall_policy_id,
list1, list2):
position = 0
for r1, r2 in zip(list1, list2):
rule = r1['firewall_rule']
rule['firewall_policy_id'] = firewall_policy_id
position += 1
rule['position'] = position
for k in rule:
self.assertEqual(rule[k], r2[k])
class TestFirewallDBPlugin(FirewallPluginDbTestCase):
def test_create_firewall_policy(self):
name = "firewall_policy1"
attrs = self._get_test_firewall_policy_attrs(name)
with self.firewall_policy(name=name, shared=SHARED,
firewall_rules=None,
audited=AUDITED) as firewall_policy:
for k, v in attrs.iteritems():
self.assertEqual(firewall_policy['firewall_policy'][k], v)
def test_create_firewall_policy_with_rules(self):
name = "firewall_policy1"
attrs = self._get_test_firewall_policy_attrs(name)
with contextlib.nested(self.firewall_rule(name='fwr1',
no_delete=True),
self.firewall_rule(name='fwr2',
no_delete=True),
self.firewall_rule(name='fwr3',
no_delete=True)) as fr:
fw_rule_ids = [r['firewall_rule']['id'] for r in fr]
attrs['firewall_rules'] = fw_rule_ids
with self.firewall_policy(name=name, shared=SHARED,
firewall_rules=fw_rule_ids,
audited=AUDITED,
no_delete=True) as fwp:
for k, v in attrs.iteritems():
self.assertEqual(fwp['firewall_policy'][k], v)
def test_show_firewall_policy(self):
name = "firewall_policy1"
attrs = self._get_test_firewall_policy_attrs(name)
with self.firewall_policy(name=name, shared=SHARED,
firewall_rules=None,
audited=AUDITED) as fwp:
req = self.new_show_request('firewall_policies',
fwp['firewall_policy']['id'],
fmt=self.fmt)
res = self.deserialize(self.fmt, req.get_response(self.ext_api))
for k, v in attrs.iteritems():
self.assertEqual(res['firewall_policy'][k], v)
def test_list_firewall_policies(self):
with contextlib.nested(self.firewall_policy(name='fwp1',
description='fwp'),
self.firewall_policy(name='fwp2',
description='fwp'),
self.firewall_policy(name='fwp3',
description='fwp')
) as fw_policies:
self._test_list_resources('firewall_policy',
fw_policies,
query_params='description=fwp')
def test_update_firewall_policy(self):
name = "new_firewall_policy1"
attrs = self._get_test_firewall_policy_attrs(name)
with self.firewall_policy(shared=SHARED,
firewall_rules=None,
audited=AUDITED) as fwp:
data = {'firewall_policy': {'name': name}}
req = self.new_update_request('firewall_policies', data,
fwp['firewall_policy']['id'])
res = self.deserialize(self.fmt, req.get_response(self.ext_api))
for k, v in attrs.iteritems():
self.assertEqual(res['firewall_policy'][k], v)
def test_update_firewall_policy_with_rules(self):
attrs = self._get_test_firewall_policy_attrs()
with self.firewall_policy() as fwp:
with contextlib.nested(self.firewall_rule(name='fwr1',
no_delete=True),
self.firewall_rule(name='fwr2',
no_delete=True),
self.firewall_rule(name='fwr3',
no_delete=True)) as fr:
fw_rule_ids = [r['firewall_rule']['id'] for r in fr]
attrs['firewall_rules'] = fw_rule_ids
data = {'firewall_policy':
{'firewall_rules': fw_rule_ids}}
req = self.new_update_request('firewall_policies', data,
fwp['firewall_policy']['id'])
res = self.deserialize(self.fmt,
req.get_response(self.ext_api))
attrs['audited'] = False
for k, v in attrs.iteritems():
self.assertEqual(res['firewall_policy'][k], v)
def test_update_firewall_policy_replace_rules(self):
attrs = self._get_test_firewall_policy_attrs()
with self.firewall_policy() as fwp:
with contextlib.nested(self.firewall_rule(name='fwr1',
no_delete=True),
self.firewall_rule(name='fwr2',
no_delete=True)) as fr1:
fw_rule_ids = [r['firewall_rule']['id'] for r in fr1]
data = {'firewall_policy':
{'firewall_rules': fw_rule_ids}}
req = self.new_update_request('firewall_policies', data,
fwp['firewall_policy']['id'])
req.get_response(self.ext_api)
with contextlib.nested(self.firewall_rule(name='fwr3',
no_delete=True),
self.firewall_rule(name='fwr4',
no_delete=True)) as fr2:
fw_rule_ids = [r['firewall_rule']['id'] for r in fr2]
attrs['firewall_rules'] = fw_rule_ids
data = {'firewall_policy':
{'firewall_rules': fw_rule_ids}}
req = self.new_update_request('firewall_policies', data,
fwp['firewall_policy']['id'])
res = self.deserialize(self.fmt,
req.get_response(self.ext_api))
attrs['audited'] = False
for k, v in attrs.iteritems():
self.assertEqual(res['firewall_policy'][k], v)
def test_update_firewall_policy_with_non_existing_rule(self):
attrs = self._get_test_firewall_policy_attrs()
with self.firewall_policy() as fwp:
with contextlib.nested(self.firewall_rule(name='fwr1',
no_delete=True),
self.firewall_rule(name='fwr2',
no_delete=True)) as fr:
fw_rule_ids = [r['firewall_rule']['id'] for r in fr]
fw_rule_ids.append('12345') # non-existent rule
data = {'firewall_policy':
{'firewall_rules': fw_rule_ids}}
req = self.new_update_request('firewall_policies', data,
fwp['firewall_policy']['id'])
res = req.get_response(self.ext_api)
#check that the firewall_rule was not found
self.assertEqual(res.status_int, 404)
#check if none of the rules got added to the policy
req = self.new_show_request('firewall_policies',
fwp['firewall_policy']['id'],
fmt=self.fmt)
res = self.deserialize(self.fmt,
req.get_response(self.ext_api))
for k, v in attrs.iteritems():
self.assertEqual(res['firewall_policy'][k], v)
def test_delete_firewall_policy(self):
ctx = context.get_admin_context()
with self.firewall_policy(no_delete=True) as fwp:
fwp_id = fwp['firewall_policy']['id']
req = self.new_delete_request('firewall_policies', fwp_id)
res = req.get_response(self.ext_api)
self.assertEqual(res.status_int, 204)
self.assertRaises(firewall.FirewallPolicyNotFound,
self.plugin.get_firewall_policy,
ctx, fwp_id)
def test_delete_firewall_policy_with_rule(self):
ctx = context.get_admin_context()
attrs = self._get_test_firewall_policy_attrs()
with self.firewall_policy(no_delete=True) as fwp:
fwp_id = fwp['firewall_policy']['id']
with self.firewall_rule(name='fwr1', no_delete=True) as fr:
fr_id = fr['firewall_rule']['id']
fw_rule_ids = [fr_id]
attrs['firewall_rules'] = fw_rule_ids
data = {'firewall_policy':
{'firewall_rules': fw_rule_ids}}
req = self.new_update_request('firewall_policies', data,
fwp['firewall_policy']['id'])
req.get_response(self.ext_api)
fw_rule = self.plugin.get_firewall_rule(ctx, fr_id)
self.assertEqual(fw_rule['firewall_policy_id'], fwp_id)
req = self.new_delete_request('firewall_policies', fwp_id)
res = req.get_response(self.ext_api)
self.assertEqual(res.status_int, 204)
self.assertRaises(firewall.FirewallPolicyNotFound,
self.plugin.get_firewall_policy,
ctx, fwp_id)
fw_rule = self.plugin.get_firewall_rule(ctx, fr_id)
self.assertEqual(fw_rule['firewall_policy_id'], None)
def test_delete_firewall_policy_with_firewall_association(self):
attrs = self._get_test_firewall_attrs()
with self.firewall_policy(no_delete=True) 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=
ADMIN_STATE_UP):
req = self.new_delete_request('firewall_policies', fwp_id)
res = req.get_response(self.ext_api)
self.assertEqual(res.status_int, 409)
def test_create_firewall_rule(self):
attrs = self._get_test_firewall_rule_attrs()
with self.firewall_rule() as firewall_rule:
for k, v in attrs.iteritems():
self.assertEqual(firewall_rule['firewall_rule'][k], v)
attrs['source_port'] = None
attrs['destination_port'] = None
with self.firewall_rule(source_port=None,
destination_port=None) as firewall_rule:
for k, v in attrs.iteritems():
self.assertEqual(firewall_rule['firewall_rule'][k], v)
attrs['source_port'] = '10000'
attrs['destination_port'] = '80'
with self.firewall_rule(source_port=10000,
destination_port=80) as firewall_rule:
for k, v in attrs.iteritems():
self.assertEqual(firewall_rule['firewall_rule'][k], v)
attrs['source_port'] = '10000'
attrs['destination_port'] = '80'
with self.firewall_rule(source_port='10000',
destination_port='80') as firewall_rule:
for k, v in attrs.iteritems():
self.assertEqual(firewall_rule['firewall_rule'][k], v)
def test_show_firewall_rule_with_fw_policy_not_associated(self):
attrs = self._get_test_firewall_rule_attrs()
with self.firewall_rule() as fw_rule:
req = self.new_show_request('firewall_rules',
fw_rule['firewall_rule']['id'],
fmt=self.fmt)
res = self.deserialize(self.fmt,
req.get_response(self.ext_api))
for k, v in attrs.iteritems():
self.assertEqual(res['firewall_rule'][k], v)
def test_show_firewall_rule_with_fw_policy_associated(self):
attrs = self._get_test_firewall_rule_attrs()
with self.firewall_policy(no_delete=True) as fwp:
fwp_id = fwp['firewall_policy']['id']
attrs['firewall_policy_id'] = fwp_id
with self.firewall_rule(no_delete=True) as fw_rule:
data = {'firewall_policy':
{'firewall_rules':
[fw_rule['firewall_rule']['id']]}}
req = self.new_update_request('firewall_policies', data,
fwp['firewall_policy']['id'])
req.get_response(self.ext_api)
req = self.new_show_request('firewall_rules',
fw_rule['firewall_rule']['id'],
fmt=self.fmt)
res = self.deserialize(self.fmt,
req.get_response(self.ext_api))
for k, v in attrs.iteritems():
self.assertEqual(res['firewall_rule'][k], v)
def test_list_firewall_rules(self):
with contextlib.nested(self.firewall_rule(name='fwr1',
no_delete=True),
self.firewall_rule(name='fwr2',
no_delete=True),
self.firewall_rule(name='fwr3',
no_delete=True)) as fr:
query_params = 'protocol=tcp'
self._test_list_resources('firewall_rule', fr,
query_params=query_params)
def test_update_firewall_rule(self):
name = "new_firewall_rule1"
attrs = self._get_test_firewall_rule_attrs(name)
attrs['source_port'] = '10:20'
attrs['destination_port'] = '30:40'
with self.firewall_rule(no_delete=True) as fwr:
data = {'firewall_rule': {'name': name,
'source_port': '10:20',
'destination_port': '30:40'}}
req = self.new_update_request('firewall_rules', data,
fwr['firewall_rule']['id'])
res = self.deserialize(self.fmt,
req.get_response(self.ext_api))
for k, v in attrs.iteritems():
self.assertEqual(res['firewall_rule'][k], v)
attrs['source_port'] = '10000'
attrs['destination_port'] = '80'
with self.firewall_rule(no_delete=True) as fwr:
data = {'firewall_rule': {'name': name,
'source_port': 10000,
'destination_port': 80}}
req = self.new_update_request('firewall_rules', data,
fwr['firewall_rule']['id'])
res = self.deserialize(self.fmt,
req.get_response(self.ext_api))
for k, v in attrs.iteritems():
self.assertEqual(res['firewall_rule'][k], v)
attrs['source_port'] = '10000'
attrs['destination_port'] = '80'
with self.firewall_rule(no_delete=True) as fwr:
data = {'firewall_rule': {'name': name,
'source_port': '10000',
'destination_port': '80'}}
req = self.new_update_request('firewall_rules', data,
fwr['firewall_rule']['id'])
res = self.deserialize(self.fmt,
req.get_response(self.ext_api))
for k, v in attrs.iteritems():
self.assertEqual(res['firewall_rule'][k], v)
attrs['source_port'] = None
attrs['destination_port'] = None
with self.firewall_rule(no_delete=True) as fwr:
data = {'firewall_rule': {'name': name,
'source_port': None,
'destination_port': None}}
req = self.new_update_request('firewall_rules', data,
fwr['firewall_rule']['id'])
res = self.deserialize(self.fmt,
req.get_response(self.ext_api))
for k, v in attrs.iteritems():
self.assertEqual(res['firewall_rule'][k], v)
def test_update_firewall_rule_with_policy_associated(self):
name = "new_firewall_rule1"
attrs = self._get_test_firewall_rule_attrs(name)
with self.firewall_policy(no_delete=True) as fwp:
fwp_id = fwp['firewall_policy']['id']
attrs['firewall_policy_id'] = fwp_id
with self.firewall_rule(no_delete=True) as fwr:
fwr_id = fwr['firewall_rule']['id']
data = {'firewall_policy': {'firewall_rules': [fwr_id]}}
req = self.new_update_request('firewall_policies', data,
fwp['firewall_policy']['id'])
req.get_response(self.ext_api)
data = {'firewall_rule': {'name': name}}
req = self.new_update_request('firewall_rules', data,
fwr['firewall_rule']['id'])
res = self.deserialize(self.fmt,
req.get_response(self.ext_api))
attrs['firewall_policy_id'] = fwp_id
for k, v in attrs.iteritems():
self.assertEqual(res['firewall_rule'][k], v)
req = self.new_show_request('firewall_policies',
fwp['firewall_policy']['id'],
fmt=self.fmt)
res = self.deserialize(self.fmt,
req.get_response(self.ext_api))
self.assertEqual(res['firewall_policy']['firewall_rules'],
[fwr_id])
self.assertEqual(res['firewall_policy']['audited'], False)
def test_delete_firewall_rule(self):
ctx = context.get_admin_context()
with self.firewall_rule(no_delete=True) as fwr:
fwr_id = fwr['firewall_rule']['id']
req = self.new_delete_request('firewall_rules', fwr_id)
res = req.get_response(self.ext_api)
self.assertEqual(res.status_int, 204)
self.assertRaises(firewall.FirewallRuleNotFound,
self.plugin.get_firewall_rule,
ctx, fwr_id)
def test_delete_firewall_rule_with_policy_associated(self):
attrs = self._get_test_firewall_rule_attrs()
with self.firewall_policy(no_delete=True) as fwp:
fwp_id = fwp['firewall_policy']['id']
attrs['firewall_policy_id'] = fwp_id
with self.firewall_rule(no_delete=True) as fwr:
fwr_id = fwr['firewall_rule']['id']
data = {'firewall_policy': {'firewall_rules': [fwr_id]}}
req = self.new_update_request('firewall_policies', data,
fwp['firewall_policy']['id'])
req.get_response(self.ext_api)
req = self.new_delete_request('firewall_rules', fwr_id)
res = req.get_response(self.ext_api)
self.assertEqual(res.status_int, 409)
def test_create_firewall(self):
name = "firewall1"
attrs = self._get_test_firewall_attrs(name)
with self.firewall_policy(no_delete=True) 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=
ADMIN_STATE_UP) as firewall:
for k, v in attrs.iteritems():
self.assertEqual(firewall['firewall'][k], v)
def test_show_firewall(self):
name = "firewall1"
attrs = self._get_test_firewall_attrs(name)
with self.firewall_policy(no_delete=True) 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=
ADMIN_STATE_UP) as firewall:
req = self.new_show_request('firewalls',
firewall['firewall']['id'],
fmt=self.fmt)
res = self.deserialize(self.fmt,
req.get_response(self.ext_api))
for k, v in attrs.iteritems():
self.assertEqual(res['firewall'][k], v)
def test_list_firewalls(self):
with self.firewall_policy(no_delete=True) as fwp:
fwp_id = fwp['firewall_policy']['id']
with contextlib.nested(self.firewall(name='fw1',
firewall_policy_id=fwp_id,
description='fw'),
self.firewall(name='fw2',
firewall_policy_id=fwp_id,
description='fw'),
self.firewall(name='fw3',
firewall_policy_id=fwp_id,
description='fw')) as fwalls:
self._test_list_resources('firewall', fwalls,
query_params='description=fw')
def test_update_firewall(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=
ADMIN_STATE_UP) as firewall:
data = {'firewall': {'name': name}}
req = self.new_update_request('firewalls', data,
firewall['firewall']['id'])
res = self.deserialize(self.fmt,
req.get_response(self.ext_api))
for k, v in attrs.iteritems():
self.assertEqual(res['firewall'][k], v)
def test_delete_firewall(self):
ctx = context.get_admin_context()
with self.firewall_policy(no_delete=True) as fwp:
fwp_id = fwp['firewall_policy']['id']
with self.firewall(firewall_policy_id=fwp_id,
no_delete=True) as fw:
fw_id = fw['firewall']['id']
req = self.new_delete_request('firewalls', fw_id)
res = req.get_response(self.ext_api)
self.assertEqual(res.status_int, 204)
self.assertRaises(firewall.FirewallNotFound,
self.plugin.get_firewall,
ctx, fw_id)
def test_insert_rule_in_policy_with_prior_rules_added_via_update(self):
attrs = self._get_test_firewall_policy_attrs()
attrs['audited'] = False
attrs['firewall_list'] = []
with self.firewall_policy() as fwp:
fwp_id = fwp['firewall_policy']['id']
attrs['id'] = fwp_id
with contextlib.nested(self.firewall_rule(name='fwr1',
no_delete=True),
self.firewall_rule(name='fwr2',
no_delete=True)) as fr1:
fw_rule_ids = [r['firewall_rule']['id'] for r in fr1]
attrs['firewall_rules'] = fw_rule_ids[:]
data = {'firewall_policy':
{'firewall_rules': fw_rule_ids}}
req = self.new_update_request('firewall_policies', data,
fwp_id)
req.get_response(self.ext_api)
self._rule_action('insert', fwp_id, fw_rule_ids[0],
insert_before=fw_rule_ids[0],
insert_after=None,
expected_code=webob.exc.HTTPConflict.code,
expected_body=None)
with self.firewall_rule(name='fwr3', no_delete=True) as fwr3:
fwr3_id = fwr3['firewall_rule']['id']
attrs['firewall_rules'].insert(0, fwr3_id)
self._rule_action('insert', fwp_id, fwr3_id,
insert_before=fw_rule_ids[0],
insert_after=None,
expected_code=webob.exc.HTTPOk.code,
expected_body=attrs)
def test_insert_rule_in_policy_failures(self):
with self.firewall_policy() as fwp:
fwp_id = fwp['firewall_policy']['id']
with self.firewall_rule(name='fwr1', no_delete=True) as fr1:
fr1_id = fr1['firewall_rule']['id']
fw_rule_ids = [fr1_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)
# test inserting with empty request body
self._rule_action('insert', fwp_id, '123',
expected_code=webob.exc.HTTPBadRequest.code,
expected_body=None, body_data={})
# test inserting when firewall_rule_id is missing in
# request body
insert_data = {'insert_before': '123',
'insert_after': '456'}
self._rule_action('insert', fwp_id, '123',
expected_code=webob.exc.HTTPBadRequest.code,
expected_body=None,
body_data=insert_data)
# test inserting when firewall_rule_id is None
insert_data = {'firewall_rule_id': None,
'insert_before': '123',
'insert_after': '456'}
self._rule_action('insert', fwp_id, '123',
expected_code=webob.exc.HTTPNotFound.code,
expected_body=None,
body_data=insert_data)
# test inserting when firewall_policy_id is incorrect
self._rule_action('insert', '123', fr1_id,
expected_code=webob.exc.HTTPNotFound.code,
expected_body=None)
# test inserting when firewall_policy_id is None
self._rule_action('insert', None, fr1_id,
expected_code=webob.exc.HTTPBadRequest.code,
expected_body=None)
def test_insert_rule_in_policy(self):
attrs = self._get_test_firewall_policy_attrs()
attrs['audited'] = False
attrs['firewall_list'] = []
with self.firewall_policy() as fwp:
fwp_id = fwp['firewall_policy']['id']
attrs['id'] = fwp_id
with contextlib.nested(self.firewall_rule(name='fwr0',
no_delete=True),
self.firewall_rule(name='fwr1',
no_delete=True),
self.firewall_rule(name='fwr2',
no_delete=True),
self.firewall_rule(name='fwr3',
no_delete=True),
self.firewall_rule(name='fwr4',
no_delete=True),
self.firewall_rule(name='fwr5',
no_delete=True),
self.firewall_rule(name='fwr6',
no_delete=True)) as fwr:
# test insert when rule list is empty
fwr0_id = fwr[0]['firewall_rule']['id']
attrs['firewall_rules'].insert(0, fwr0_id)
self._rule_action('insert', fwp_id, fwr0_id,
insert_before=None,
insert_after=None,
expected_code=webob.exc.HTTPOk.code,
expected_body=attrs)
# test insert at top of rule list, insert_before and
# insert_after not provided
fwr1_id = fwr[1]['firewall_rule']['id']
attrs['firewall_rules'].insert(0, fwr1_id)
insert_data = {'firewall_rule_id': fwr1_id}
self._rule_action('insert', fwp_id, fwr0_id,
expected_code=webob.exc.HTTPOk.code,
expected_body=attrs, body_data=insert_data)
# test insert at top of list above existing rule
fwr2_id = fwr[2]['firewall_rule']['id']
attrs['firewall_rules'].insert(0, fwr2_id)
self._rule_action('insert', fwp_id, fwr2_id,
insert_before=fwr1_id,
insert_after=None,
expected_code=webob.exc.HTTPOk.code,
expected_body=attrs)
# test insert at bottom of list
fwr3_id = fwr[3]['firewall_rule']['id']
attrs['firewall_rules'].append(fwr3_id)
self._rule_action('insert', fwp_id, fwr3_id,
insert_before=None,
insert_after=fwr0_id,
expected_code=webob.exc.HTTPOk.code,
expected_body=attrs)
# test insert in the middle of the list using
# insert_before
fwr4_id = fwr[4]['firewall_rule']['id']
attrs['firewall_rules'].insert(1, fwr4_id)
self._rule_action('insert', fwp_id, fwr4_id,
insert_before=fwr1_id,
insert_after=None,
expected_code=webob.exc.HTTPOk.code,
expected_body=attrs)
# test insert in the middle of the list using
# insert_after
fwr5_id = fwr[5]['firewall_rule']['id']
attrs['firewall_rules'].insert(1, fwr5_id)
self._rule_action('insert', fwp_id, fwr5_id,
insert_before=None,
insert_after=fwr2_id,
expected_code=webob.exc.HTTPOk.code,
expected_body=attrs)
# test insert when both insert_before and
# insert_after are set
fwr6_id = fwr[6]['firewall_rule']['id']
attrs['firewall_rules'].insert(1, fwr6_id)
self._rule_action('insert', fwp_id, fwr6_id,
insert_before=fwr5_id,
insert_after=fwr5_id,
expected_code=webob.exc.HTTPOk.code,
expected_body=attrs)
def test_remove_rule_from_policy(self):
attrs = self._get_test_firewall_policy_attrs()
attrs['audited'] = False
attrs['firewall_list'] = []
with self.firewall_policy() as fwp:
fwp_id = fwp['firewall_policy']['id']
attrs['id'] = fwp_id
with contextlib.nested(self.firewall_rule(name='fwr1',
no_delete=True),
self.firewall_rule(name='fwr2',
no_delete=True),
self.firewall_rule(name='fwr3',
no_delete=True)) as fr1:
fw_rule_ids = [r['firewall_rule']['id'] for r in fr1]
attrs['firewall_rules'] = fw_rule_ids[:]
data = {'firewall_policy':
{'firewall_rules': fw_rule_ids}}
req = self.new_update_request('firewall_policies', data,
fwp_id)
req.get_response(self.ext_api)
# test removing a rule from a policy that does not exist
self._rule_action('remove', '123', fw_rule_ids[1],
expected_code=webob.exc.HTTPNotFound.code,
expected_body=None)
# test removing a rule in the middle of the list
attrs['firewall_rules'].remove(fw_rule_ids[1])
self._rule_action('remove', fwp_id, fw_rule_ids[1],
expected_body=attrs)
# test removing a rule at the top of the list
attrs['firewall_rules'].remove(fw_rule_ids[0])
self._rule_action('remove', fwp_id, fw_rule_ids[0],
expected_body=attrs)
# test removing remaining rule in the list
attrs['firewall_rules'].remove(fw_rule_ids[2])
self._rule_action('remove', fwp_id, fw_rule_ids[2],
expected_body=attrs)
# test removing rule that is not associated with the policy
self._rule_action('remove', fwp_id, fw_rule_ids[2],
expected_code=webob.exc.HTTPBadRequest.code,
expected_body=None)
def test_remove_rule_from_policy_failures(self):
with self.firewall_policy() as fwp:
fwp_id = fwp['firewall_policy']['id']
with self.firewall_rule(name='fwr1', no_delete=True) as fr1:
fw_rule_ids = [fr1['firewall_rule']['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)
# test removing rule that does not exist
self._rule_action('remove', fwp_id, '123',
expected_code=webob.exc.HTTPNotFound.code,
expected_body=None)
# test removing rule with bad request
self._rule_action('remove', fwp_id, '123',
expected_code=webob.exc.HTTPBadRequest.code,
expected_body=None, body_data={})
# test removing rule with firewall_rule_id set to None
self._rule_action('remove', fwp_id, '123',
expected_code=webob.exc.HTTPNotFound.code,
expected_body=None,
body_data={'firewall_rule_id': None})
class TestFirewallDBPluginXML(TestFirewallDBPlugin):
fmt = 'xml'

View File

@ -0,0 +1,15 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2013 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.

View File

@ -0,0 +1,353 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2013 Big Switch Networks, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the spec
#
# @author: Sumit Naiksatam, sumitnaiksatam@gmail.com, Big Switch Networks, Inc.
import contextlib
import mock
from neutron import context
from neutron.extensions import firewall
from neutron.plugins.common import constants as const
from neutron.services.firewall import fwaas_plugin
from neutron.tests import base
from neutron.tests.unit.db.firewall import test_db_firewall
FW_PLUGIN_KLASS = (
"neutron.services.firewall.fwaas_plugin.FirewallPlugin"
)
class TestFirewallCallbacks(test_db_firewall.FirewallPluginDbTestCase):
def setUp(self):
super(TestFirewallCallbacks,
self).setUp(fw_plugin=FW_PLUGIN_KLASS)
self.callbacks = self.plugin.callbacks
def test_set_firewall_status(self):
ctx = context.get_admin_context()
with self.firewall_policy(no_delete=True) 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) as fw:
fw_id = fw['firewall']['id']
res = self.callbacks.set_firewall_status(ctx, fw_id,
const.ACTIVE,
host='dummy')
fw_db = self.plugin.get_firewall(ctx, fw_id)
self.assertEqual(fw_db['status'], const.ACTIVE)
self.assertTrue(res)
res = self.callbacks.set_firewall_status(ctx, fw_id,
const.ERROR)
fw_db = self.plugin.get_firewall(ctx, fw_id)
self.assertEqual(fw_db['status'], const.ERROR)
self.assertFalse(res)
def test_firewall_deleted(self):
ctx = context.get_admin_context()
with self.firewall_policy(no_delete=True) 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,
no_delete=True) as fw:
fw_id = fw['firewall']['id']
with ctx.session.begin(subtransactions=True):
fw_db = self.plugin._get_firewall(ctx, fw_id)
fw_db['status'] = const.PENDING_DELETE
ctx.session.flush()
res = self.callbacks.firewall_deleted(ctx, fw_id,
host='dummy')
self.assertTrue(res)
self.assertRaises(firewall.FirewallNotFound,
self.plugin.get_firewall,
ctx, fw_id)
def test_firewall_deleted_error(self):
ctx = context.get_admin_context()
with self.firewall_policy(no_delete=True) 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,
no_delete=True) as fw:
fw_id = fw['firewall']['id']
res = self.callbacks.firewall_deleted(ctx, fw_id,
host='dummy')
self.assertFalse(res)
fw_db = self.plugin._get_firewall(ctx, fw_id)
self.assertEqual(fw_db['status'], const.ERROR)
def test_get_firewall_for_tenant(self):
tenant_id = 'test-tenant'
ctx = context.Context('', tenant_id)
with self.firewall_policy(tenant_id=tenant_id, no_delete=True) as fwp:
fwp_id = fwp['firewall_policy']['id']
with contextlib.nested(self.firewall_rule(name='fwr1',
tenant_id=tenant_id,
no_delete=True),
self.firewall_rule(name='fwr2',
tenant_id=tenant_id,
no_delete=True),
self.firewall_rule(name='fwr3',
tenant_id=tenant_id,
no_delete=True)) as fr:
fw_rule_ids = [r['firewall_rule']['id'] for r in fr]
data = {'firewall_policy':
{'firewall_rules': fw_rule_ids}}
req = self.new_update_request('firewall_policies', data,
fwp_id)
res = req.get_response(self.ext_api)
attrs = self._get_test_firewall_attrs()
attrs['firewall_policy_id'] = fwp_id
with self.firewall(firewall_policy_id=fwp_id,
tenant_id=tenant_id,
admin_state_up=
test_db_firewall.ADMIN_STATE_UP,
no_delete=True) as fw:
fw_id = fw['firewall']['id']
res = self.callbacks.get_firewalls_for_tenant(ctx,
host='dummy')
fw_rules = (
self.plugin._make_firewall_dict_with_rules(ctx,
fw_id)
)
self.assertEqual(res[0], fw_rules)
self._compare_firewall_rule_lists(
fwp_id, fr, res[0]['firewall_rule_list'])
def test_get_firewall_for_tenant_without_rules(self):
tenant_id = 'test-tenant'
ctx = context.Context('', tenant_id)
with self.firewall_policy(tenant_id=tenant_id, no_delete=True) as fwp:
fwp_id = fwp['firewall_policy']['id']
attrs = self._get_test_firewall_attrs()
attrs['firewall_policy_id'] = fwp_id
with contextlib.nested(self.firewall(
firewall_policy_id=fwp_id,
tenant_id=tenant_id,
admin_state_up=test_db_firewall.ADMIN_STATE_UP,
no_delete=True), self.firewall(
firewall_policy_id=fwp_id,
tenant_id=tenant_id,
admin_state_up=test_db_firewall.ADMIN_STATE_UP,
no_delete=True)) as fws:
fw_list = [fw['firewall'] for fw in fws]
f = self.callbacks.get_firewalls_for_tenant_without_rules
res = f(ctx, host='dummy')
for fw in res:
del fw['shared']
self.assertEqual(res, fw_list)
class TestFirewallAgentApi(base.BaseTestCase):
def setUp(self):
super(TestFirewallAgentApi, self).setUp()
self.addCleanup(mock.patch.stopall)
self.api = fwaas_plugin.FirewallAgentApi('topic', 'host')
self.mock_fanoutcast = mock.patch.object(self.api,
'fanout_cast').start()
self.mock_msg = mock.patch.object(self.api, 'make_msg').start()
def test_init(self):
self.assertEqual(self.api.topic, 'topic')
self.assertEqual(self.api.host, 'host')
def _call_test_helper(self, method_name):
rv = getattr(self.api, method_name)(mock.sentinel.context, 'test')
self.assertEqual(rv, self.mock_fanoutcast.return_value)
self.mock_fanoutcast.assert_called_once_with(
mock.sentinel.context,
self.mock_msg.return_value,
topic='topic'
)
self.mock_msg.assert_called_once_with(
method_name,
firewall='test',
host='host'
)
def test_create_firewall(self):
self._call_test_helper('create_firewall')
def test_update_firewall(self):
self._call_test_helper('update_firewall')
def test_delete_firewall(self):
self._call_test_helper('delete_firewall')
class TestFirewallPluginBase(test_db_firewall.TestFirewallDBPlugin):
def setUp(self):
super(TestFirewallPluginBase, self).setUp(fw_plugin=FW_PLUGIN_KLASS)
self.callbacks = self.plugin.callbacks
def test_update_firewall(self):
ctx = context.get_admin_context()
name = "new_firewall1"
attrs = self._get_test_firewall_attrs(name)
with self.firewall_policy(no_delete=True) 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,
const.ACTIVE)
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,
const.PENDING_CREATE,
const.PENDING_UPDATE)
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(no_delete=True) 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, 409)
def test_update_firewall_policy_fails_when_firewall_pending(self):
name = "new_firewall1"
attrs = self._get_test_firewall_attrs(name)
with self.firewall_policy(no_delete=True) 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, 409)
def test_update_firewall_rule_fails_when_firewall_pending(self):
with self.firewall_policy(no_delete=True) as fwp:
fwp_id = fwp['firewall_policy']['id']
with self.firewall_rule(name='fwr1', no_delete=True) as fr:
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,
no_delete=True):
data = {'firewall_rule': {'protocol': 'udp'}}
req = self.new_update_request('firewall_rules',
data, fr_id)
res = req.get_response(self.ext_api)
self.assertEqual(res.status_int, 409)
def test_delete_firewall(self):
ctx = context.get_admin_context()
attrs = self._get_test_firewall_attrs()
with self.firewall_policy(no_delete=True) 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)
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)
def test_delete_firewall_after_agent_delete(self):
ctx = context.get_admin_context()
with self.firewall_policy(no_delete=True) as fwp:
fwp_id = fwp['firewall_policy']['id']
with self.firewall(firewall_policy_id=fwp_id,
no_delete=True) as fw:
fw_id = fw['firewall']['id']
with ctx.session.begin(subtransactions=True):
req = self.new_delete_request('firewalls', fw_id)
res = req.get_response(self.ext_api)
self.assertEqual(res.status_int, 204)
self.plugin.callbacks.firewall_deleted(ctx, fw_id)
self.assertRaises(firewall.FirewallNotFound,
self.plugin.get_firewall,
ctx, fw_id)
def test_make_firewall_dict_with_in_place_rules(self):
ctx = context.get_admin_context()
with self.firewall_policy(no_delete=True) as fwp:
fwp_id = fwp['firewall_policy']['id']
with contextlib.nested(self.firewall_rule(name='fwr1',
no_delete=True),
self.firewall_rule(name='fwr2',
no_delete=True),
self.firewall_rule(name='fwr3',
no_delete=True)) as fr:
fw_rule_ids = [r['firewall_rule']['id'] for r in fr]
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 = self._get_test_firewall_attrs()
attrs['firewall_policy_id'] = fwp_id
with self.firewall(firewall_policy_id=fwp_id,
admin_state_up=
test_db_firewall.ADMIN_STATE_UP,
no_delete=True) as fw:
fw_id = fw['firewall']['id']
fw_rules = (
self.plugin._make_firewall_dict_with_rules(ctx,
fw_id)
)
self.assertEqual(fw_rules['id'], fw_id)
self._compare_firewall_rule_lists(
fwp_id, fr, fw_rules['firewall_rule_list'])
def test_make_firewall_dict_with_in_place_rules_no_policy(self):
ctx = context.get_admin_context()
with self.firewall(no_delete=True) as fw:
fw_id = fw['firewall']['id']
fw_rules = self.plugin._make_firewall_dict_with_rules(ctx, fw_id)
self.assertEquals(fw_rules['firewall_rule_list'], [])

View File

@ -0,0 +1,552 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2013 Big Switch Networks, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the spec
#
# @author: Sumit Naiksatam, sumitnaiksatam@gmail.com, Big Switch Networks, Inc.
import copy
import mock
from oslo.config import cfg
from webob import exc
import webtest
from neutron.api import extensions
from neutron.api.v2 import attributes
from neutron.common import config
from neutron.extensions import firewall
from neutron import manager
from neutron.openstack.common import uuidutils
from neutron.plugins.common import constants
from neutron.tests import base
from neutron.tests.unit import test_api_v2
from neutron.tests.unit import test_extensions
from neutron.tests.unit import testlib_api
_uuid = uuidutils.generate_uuid
_get_path = test_api_v2._get_path
class FirewallTestExtensionManager(object):
def get_resources(self):
# Add the resources to the global attribute map
# This is done here as the setup process won't
# initialize the main API router which extends
# the global attribute map
attributes.RESOURCE_ATTRIBUTE_MAP.update(
firewall.RESOURCE_ATTRIBUTE_MAP)
return firewall.Firewall.get_resources()
def get_actions(self):
return []
def get_request_extensions(self):
return []
class FirewallExtensionTestCase(testlib_api.WebTestCase):
fmt = 'json'
def setUp(self):
super(FirewallExtensionTestCase, self).setUp()
plugin = 'neutron.extensions.firewall.FirewallPluginBase'
# Ensure 'stale' patched copies of the plugin are never returned
manager.NeutronManager._instance = None
# Ensure existing ExtensionManager is not used
extensions.PluginAwareExtensionManager._instance = None
# Create the default configurations
args = ['--config-file', test_api_v2.etcdir('neutron.conf.test')]
config.parse(args)
# Stubbing core plugin with Firewall plugin
cfg.CONF.set_override('core_plugin', plugin)
cfg.CONF.set_override('service_plugins', [plugin])
self._plugin_patcher = mock.patch(plugin, autospec=True)
self.plugin = self._plugin_patcher.start()
instance = self.plugin.return_value
instance.get_plugin_type.return_value = constants.FIREWALL
ext_mgr = FirewallTestExtensionManager()
self.ext_mdw = test_extensions.setup_extensions_middleware(ext_mgr)
self.api = webtest.TestApp(self.ext_mdw)
super(FirewallExtensionTestCase, self).setUp()
def tearDown(self):
self._plugin_patcher.stop()
self.api = None
self.plugin = None
cfg.CONF.reset()
super(FirewallExtensionTestCase, self).tearDown()
def _test_entity_delete(self, entity):
"""Does the entity deletion based on naming convention."""
entity_id = _uuid()
path_prefix = 'fw/'
if entity == 'firewall_policy':
entity_plural = 'firewall_policies'
else:
entity_plural = entity + 's'
res = self.api.delete(_get_path(path_prefix + entity_plural,
id=entity_id, fmt=self.fmt))
delete_entity = getattr(self.plugin.return_value, "delete_" + entity)
delete_entity.assert_called_with(mock.ANY, entity_id)
self.assertEqual(res.status_int, exc.HTTPNoContent.code)
def test_create_firewall(self):
fw_id = _uuid()
data = {'firewall': {'description': 'descr_firewall1',
'name': 'firewall1',
'admin_state_up': True,
'firewall_policy_id': _uuid(),
'shared': False,
'tenant_id': _uuid()}}
return_value = copy.copy(data['firewall'])
return_value.update({'id': fw_id})
# since 'shared' is hidden
del return_value['shared']
instance = self.plugin.return_value
instance.create_firewall.return_value = return_value
res = self.api.post(_get_path('fw/firewalls', fmt=self.fmt),
self.serialize(data),
content_type='application/%s' % self.fmt)
instance.create_firewall.assert_called_with(mock.ANY,
firewall=data)
self.assertEqual(res.status_int, exc.HTTPCreated.code)
res = self.deserialize(res)
self.assertIn('firewall', res)
self.assertEqual(res['firewall'], return_value)
def test_firewall_list(self):
fw_id = _uuid()
return_value = [{'tenant_id': _uuid(),
'id': fw_id}]
instance = self.plugin.return_value
instance.get_firewalls.return_value = return_value
res = self.api.get(_get_path('fw/firewalls', fmt=self.fmt))
instance.get_firewalls.assert_called_with(mock.ANY,
fields=mock.ANY,
filters=mock.ANY)
self.assertEqual(res.status_int, exc.HTTPOk.code)
def test_firewall_get(self):
fw_id = _uuid()
return_value = {'tenant_id': _uuid(),
'id': fw_id}
instance = self.plugin.return_value
instance.get_firewall.return_value = return_value
res = self.api.get(_get_path('fw/firewalls',
id=fw_id, fmt=self.fmt))
instance.get_firewall.assert_called_with(mock.ANY,
fw_id,
fields=mock.ANY)
self.assertEqual(res.status_int, exc.HTTPOk.code)
res = self.deserialize(res)
self.assertIn('firewall', res)
self.assertEqual(res['firewall'], return_value)
def test_firewall_update(self):
fw_id = _uuid()
update_data = {'firewall': {'name': 'new_name'}}
return_value = {'tenant_id': _uuid(),
'id': fw_id}
instance = self.plugin.return_value
instance.update_firewall.return_value = return_value
res = self.api.put(_get_path('fw/firewalls', id=fw_id,
fmt=self.fmt),
self.serialize(update_data))
instance.update_firewall.assert_called_with(mock.ANY, fw_id,
firewall=update_data)
self.assertEqual(res.status_int, exc.HTTPOk.code)
res = self.deserialize(res)
self.assertIn('firewall', res)
self.assertEqual(res['firewall'], return_value)
def test_firewall_delete(self):
self._test_entity_delete('firewall')
def _test_create_firewall_rule(self, src_port, dst_port):
rule_id = _uuid()
data = {'firewall_rule': {'description': 'descr_firewall_rule1',
'name': 'rule1',
'shared': False,
'protocol': 'tcp',
'ip_version': 4,
'source_ip_address': '192.168.0.1',
'destination_ip_address': '127.0.0.1',
'source_port': src_port,
'destination_port': dst_port,
'action': 'allow',
'enabled': True,
'tenant_id': _uuid()}}
expected_ret_val = copy.copy(data['firewall_rule'])
expected_ret_val['source_port'] = str(src_port)
expected_ret_val['destination_port'] = str(dst_port)
expected_call_args = copy.copy(expected_ret_val)
expected_ret_val['id'] = rule_id
instance = self.plugin.return_value
instance.create_firewall_rule.return_value = expected_ret_val
res = self.api.post(_get_path('fw/firewall_rules', fmt=self.fmt),
self.serialize(data),
content_type='application/%s' % self.fmt)
instance.create_firewall_rule.assert_called_with(mock.ANY,
firewall_rule=
{'firewall_rule':
expected_call_args})
self.assertEqual(res.status_int, exc.HTTPCreated.code)
res = self.deserialize(res)
self.assertIn('firewall_rule', res)
self.assertEqual(res['firewall_rule'], expected_ret_val)
def test_create_firewall_rule_with_integer_ports(self):
self._test_create_firewall_rule(1, 10)
def test_create_firewall_rule_with_string_ports(self):
self._test_create_firewall_rule('1', '10')
def test_create_firewall_rule_with_port_range(self):
self._test_create_firewall_rule('1:20', '30:40')
def test_firewall_rule_list(self):
rule_id = _uuid()
return_value = [{'tenant_id': _uuid(),
'id': rule_id}]
instance = self.plugin.return_value
instance.get_firewall_rules.return_value = return_value
res = self.api.get(_get_path('fw/firewall_rules', fmt=self.fmt))
instance.get_firewall_rules.assert_called_with(mock.ANY,
fields=mock.ANY,
filters=mock.ANY)
self.assertEqual(res.status_int, exc.HTTPOk.code)
def test_firewall_rule_get(self):
rule_id = _uuid()
return_value = {'tenant_id': _uuid(),
'id': rule_id}
instance = self.plugin.return_value
instance.get_firewall_rule.return_value = return_value
res = self.api.get(_get_path('fw/firewall_rules',
id=rule_id, fmt=self.fmt))
instance.get_firewall_rule.assert_called_with(mock.ANY,
rule_id,
fields=mock.ANY)
self.assertEqual(res.status_int, exc.HTTPOk.code)
res = self.deserialize(res)
self.assertIn('firewall_rule', res)
self.assertEqual(res['firewall_rule'], return_value)
def test_firewall_rule_update(self):
rule_id = _uuid()
update_data = {'firewall_rule': {'action': 'deny'}}
return_value = {'tenant_id': _uuid(),
'id': rule_id}
instance = self.plugin.return_value
instance.update_firewall_rule.return_value = return_value
res = self.api.put(_get_path('fw/firewall_rules', id=rule_id,
fmt=self.fmt),
self.serialize(update_data))
instance.update_firewall_rule.assert_called_with(mock.ANY,
rule_id,
firewall_rule=
update_data)
self.assertEqual(res.status_int, exc.HTTPOk.code)
res = self.deserialize(res)
self.assertIn('firewall_rule', res)
self.assertEqual(res['firewall_rule'], return_value)
def test_firewall_rule_delete(self):
self._test_entity_delete('firewall_rule')
def test_create_firewall_policy(self):
policy_id = _uuid()
data = {'firewall_policy': {'description': 'descr_firewall_policy1',
'name': 'new_fw_policy1',
'shared': False,
'firewall_rules': [_uuid(), _uuid()],
'audited': False,
'tenant_id': _uuid()}}
return_value = copy.copy(data['firewall_policy'])
return_value.update({'id': policy_id})
instance = self.plugin.return_value
instance.create_firewall_policy.return_value = return_value
res = self.api.post(_get_path('fw/firewall_policies',
fmt=self.fmt),
self.serialize(data),
content_type='application/%s' % self.fmt)
instance.create_firewall_policy.assert_called_with(mock.ANY,
firewall_policy=
data)
self.assertEqual(res.status_int, exc.HTTPCreated.code)
res = self.deserialize(res)
self.assertIn('firewall_policy', res)
self.assertEqual(res['firewall_policy'], return_value)
def test_firewall_policy_list(self):
policy_id = _uuid()
return_value = [{'tenant_id': _uuid(),
'id': policy_id}]
instance = self.plugin.return_value
instance.get_firewall_policies.return_value = return_value
res = self.api.get(_get_path('fw/firewall_policies',
fmt=self.fmt))
instance.get_firewall_policies.assert_called_with(mock.ANY,
fields=mock.ANY,
filters=mock.ANY)
self.assertEqual(res.status_int, exc.HTTPOk.code)
def test_firewall_policy_get(self):
policy_id = _uuid()
return_value = {'tenant_id': _uuid(),
'id': policy_id}
instance = self.plugin.return_value
instance.get_firewall_policy.return_value = return_value
res = self.api.get(_get_path('fw/firewall_policies',
id=policy_id, fmt=self.fmt))
instance.get_firewall_policy.assert_called_with(mock.ANY,
policy_id,
fields=mock.ANY)
self.assertEqual(res.status_int, exc.HTTPOk.code)
res = self.deserialize(res)
self.assertIn('firewall_policy', res)
self.assertEqual(res['firewall_policy'], return_value)
def test_firewall_policy_update(self):
policy_id = _uuid()
update_data = {'firewall_policy': {'audited': True}}
return_value = {'tenant_id': _uuid(),
'id': policy_id}
instance = self.plugin.return_value
instance.update_firewall_policy.return_value = return_value
res = self.api.put(_get_path('fw/firewall_policies',
id=policy_id,
fmt=self.fmt),
self.serialize(update_data))
instance.update_firewall_policy.assert_called_with(mock.ANY,
policy_id,
firewall_policy=
update_data)
self.assertEqual(res.status_int, exc.HTTPOk.code)
res = self.deserialize(res)
self.assertIn('firewall_policy', res)
self.assertEqual(res['firewall_policy'], return_value)
def test_firewall_policy_delete(self):
self._test_entity_delete('firewall_policy')
def test_firewall_policy_insert_rule(self):
firewall_policy_id = _uuid()
firewall_rule_id = _uuid()
ref_firewall_rule_id = _uuid()
insert_data = {'firewall_rule_id': firewall_rule_id,
'insert_before': ref_firewall_rule_id,
'insert_after': None}
return_value = {'firewall_policy':
{'tenant_id': _uuid(),
'id': firewall_policy_id,
'firewall_rules': [ref_firewall_rule_id,
firewall_rule_id]}}
instance = self.plugin.return_value
instance.insert_rule.return_value = return_value
path = _get_path('fw/firewall_policies', id=firewall_policy_id,
action="insert_rule",
fmt=self.fmt)
res = self.api.put(path, self.serialize(insert_data))
instance.insert_rule.assert_called_with(mock.ANY, firewall_policy_id,
insert_data)
self.assertEqual(res.status_int, exc.HTTPOk.code)
res = self.deserialize(res)
self.assertEqual(res, return_value)
def test_firewall_policy_remove_rule(self):
firewall_policy_id = _uuid()
firewall_rule_id = _uuid()
remove_data = {'firewall_rule_id': firewall_rule_id}
return_value = {'firewall_policy':
{'tenant_id': _uuid(),
'id': firewall_policy_id,
'firewall_rules': []}}
instance = self.plugin.return_value
instance.remove_rule.return_value = return_value
path = _get_path('fw/firewall_policies', id=firewall_policy_id,
action="remove_rule",
fmt=self.fmt)
res = self.api.put(path, self.serialize(remove_data))
instance.remove_rule.assert_called_with(mock.ANY, firewall_policy_id,
remove_data)
self.assertEqual(res.status_int, exc.HTTPOk.code)
res = self.deserialize(res)
self.assertEqual(res, return_value)
class FirewallExtensionTestCaseXML(FirewallExtensionTestCase):
fmt = 'xml'
class TestFirewallAttributeValidators(base.BaseTestCase):
def test_validate_port_range(self):
msg = firewall._validate_port_range(None)
self.assertIsNone(msg)
msg = firewall._validate_port_range('10')
self.assertIsNone(msg)
msg = firewall._validate_port_range(10)
self.assertIsNone(msg)
msg = firewall._validate_port_range(-1)
self.assertEqual(msg, "Invalid port '-1'")
msg = firewall._validate_port_range('66000')
self.assertEqual(msg, "Invalid port '66000'")
msg = firewall._validate_port_range('10:20')
self.assertIsNone(msg)
msg = firewall._validate_port_range('1:65535')
self.assertIsNone(msg)
msg = firewall._validate_port_range('0:65535')
self.assertEqual(msg, "Invalid port '0'")
msg = firewall._validate_port_range('1:65536')
self.assertEqual(msg, "Invalid port '65536'")
msg = firewall._validate_port_range('abc:efg')
self.assertEqual(msg, "Port 'abc' is not a valid number")
msg = firewall._validate_port_range('1:efg')
self.assertEqual(msg, "Port 'efg' is not a valid number")
msg = firewall._validate_port_range('-1:10')
self.assertEqual(msg, "Invalid port '-1'")
msg = firewall._validate_port_range('66000:10')
self.assertEqual(msg, "Invalid port '66000'")
msg = firewall._validate_port_range('10:66000')
self.assertEqual(msg, "Invalid port '66000'")
msg = firewall._validate_port_range('1:-10')
self.assertEqual(msg, "Invalid port '-10'")
def test_validate_ip_or_subnet_or_none(self):
msg = firewall._validate_ip_or_subnet_or_none(None)
self.assertIsNone(msg)
msg = firewall._validate_ip_or_subnet_or_none('1.1.1.1')
self.assertIsNone(msg)
msg = firewall._validate_ip_or_subnet_or_none('1.1.1.0/24')
self.assertIsNone(msg)
ip_addr = '1111.1.1.1'
msg = firewall._validate_ip_or_subnet_or_none(ip_addr)
self.assertEqual(msg, ("'%s' is not a valid IP address and "
"'%s' is not a valid IP subnet") % (ip_addr,
ip_addr))
ip_addr = '1.1.1.1 has whitespace'
msg = firewall._validate_ip_or_subnet_or_none(ip_addr)
self.assertEqual(msg, ("'%s' is not a valid IP address and "
"'%s' is not a valid IP subnet") % (ip_addr,
ip_addr))
ip_addr = '111.1.1.1\twhitespace'
msg = firewall._validate_ip_or_subnet_or_none(ip_addr)
self.assertEqual(msg, ("'%s' is not a valid IP address and "
"'%s' is not a valid IP subnet") % (ip_addr,
ip_addr))
ip_addr = '111.1.1.1\nwhitespace'
msg = firewall._validate_ip_or_subnet_or_none(ip_addr)
self.assertEqual(msg, ("'%s' is not a valid IP address and "
"'%s' is not a valid IP subnet") % (ip_addr,
ip_addr))
# Valid - IPv4
cidr = "10.0.2.0/24"
msg = firewall._validate_ip_or_subnet_or_none(cidr, None)
self.assertIsNone(msg)
# Valid - IPv6 without final octets
cidr = "fe80::/24"
msg = firewall._validate_ip_or_subnet_or_none(cidr, None)
self.assertIsNone(msg)
# Valid - IPv6 with final octets
cidr = "fe80::0/24"
msg = firewall._validate_ip_or_subnet_or_none(cidr, None)
self.assertEqual(msg, ("'%s' is not a valid IP address and "
"'%s' isn't a recognized IP subnet cidr,"
" 'fe80::/24' is recommended") % (cidr,
cidr))
cidr = "fe80::"
msg = firewall._validate_ip_or_subnet_or_none(cidr, None)
self.assertIsNone(msg)
# Invalid - IPv6 with final octets, missing mask
cidr = "fe80::0"
msg = firewall._validate_ip_or_subnet_or_none(cidr, None)
self.assertIsNone(msg)
# Invalid - Address format error
cidr = 'invalid'
msg = firewall._validate_ip_or_subnet_or_none(cidr, None)
self.assertEqual(msg, ("'%s' is not a valid IP address and "
"'%s' is not a valid IP subnet") % (cidr,
cidr))