"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",
AGENT = 'q-agent-notifier'
PLUGIN = 'q-plugin'
DHCP = 'q-dhcp-notifer'
+FIREWALL_PLUGIN = 'q-firewall-plugin'
L3_AGENT = 'l3_agent'
DHCP_AGENT = 'dhcp_agent'
--- /dev/null
+# 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.
--- /dev/null
+# 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)
--- /dev/null
+# 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'))
--- /dev/null
+# 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
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
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"
--- /dev/null
+# 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.
--- /dev/null
+# 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
--- /dev/null
+# 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.
--- /dev/null
+# 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'
--- /dev/null
+# 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.
--- /dev/null
+# 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'], [])
--- /dev/null
+# 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))