Retire Packaging Deb project repos

This commit is part of a series to retire the Packaging Deb
project. Step 2 is to remove all content from the project
repos, replacing it with a README notification where to find
ongoing work, and how to recover the repo if needed at some
future point (as in
https://docs.openstack.org/infra/manual/drivers.html#retiring-a-project).

Change-Id: Ia93782af86d6fdf7efa5e32b516788925733fc1e
This commit is contained in:
Tony Breeds 2017-09-12 15:44:00 -06:00
parent 46972c1654
commit 34c32dca3f
192 changed files with 14 additions and 21261 deletions

View File

@ -1,7 +0,0 @@
[run]
branch = True
source = neutron_fwaas
# omit = neutron_fwaas/tests/*
[report]
ignore_errors = True

34
.gitignore vendored
View File

@ -1,34 +0,0 @@
AUTHORS
build/*
build-stamp
ChangeLog
cover/
covhtml/
dist/
doc/build
doc/source/contributor/api/
etc/*.sample
*.DS_Store
*.pyc
neutron.egg-info/
neutron_fwaas.egg-info/
neutron/vcsversion.py
neutron/versioninfo
pbr*.egg/
run_tests.err.log
run_tests.log
setuptools*.egg/
subunit.log
*.mo
*.sw?
*~
/.*
!/.coveragerc
!/.gitignore
!/.gitreview
!/.mailmap
!/.pylintrc
!/.testr.conf
# Files created by releasenotes build
releasenotes/build

View File

@ -1,4 +0,0 @@
[gerrit]
host=review.openstack.org
port=29418
project=openstack/neutron-fwaas.git

View File

@ -1,11 +0,0 @@
# Format is:
# <preferred e-mail> <other e-mail 1>
# <preferred e-mail> <other e-mail 2>
lawrancejing <lawrancejing@gmail.com> <liuqing@windawn.com>
Jiajun Liu <jiajun@unitedstack.com> <iamljj@gmail.com>
Zhongyue Luo <zhongyue.nah@intel.com> <lzyeval@gmail.com>
Kun Huang <gareth@unitedstack.com> <academicgareth@gmail.com>
Zhenguo Niu <zhenguo@unitedstack.com> <Niu.ZGlinux@gmail.com>
Isaku Yamahata <isaku.yamahata@intel.com> <isaku.yamahata@gmail.com>
Isaku Yamahata <isaku.yamahata@intel.com> <yamahata@private.email.ne.jp>
Morgan Fainberg <morgan.fainberg@gmail.com> <m@metacloud.com>

130
.pylintrc
View File

@ -1,130 +0,0 @@
# The format of this file isn't really documented; just use --generate-rcfile
[MASTER]
# Add <file or directory> to the black list. It should be a base name, not a
# path. You may set this option multiple times.
#
ignore=.git,tests
[MESSAGES CONTROL]
# NOTE(gus): This is a long list. A number of these are important and
# should be re-enabled once the offending code is fixed (or marked
# with a local disable)
disable=
# "F" Fatal errors that prevent further processing
import-error,
# "I" Informational noise
locally-disabled,
# "E" Error for important programming issues (likely bugs)
access-member-before-definition,
bad-super-call,
maybe-no-member,
no-member,
no-method-argument,
no-self-argument,
not-callable,
no-value-for-parameter,
super-on-old-class,
too-few-format-args,
# "W" Warnings for stylistic problems or minor programming issues
abstract-method,
anomalous-backslash-in-string,
anomalous-unicode-escape-in-string,
arguments-differ,
attribute-defined-outside-init,
bad-builtin,
bad-indentation,
broad-except,
dangerous-default-value,
deprecated-lambda,
duplicate-key,
expression-not-assigned,
fixme,
global-statement,
global-variable-not-assigned,
logging-not-lazy,
no-init,
non-parent-init-called,
pointless-string-statement,
protected-access,
redefined-builtin,
redefined-outer-name,
redefine-in-handler,
signature-differs,
star-args,
super-init-not-called,
unnecessary-lambda,
unnecessary-pass,
unpacking-non-sequence,
unreachable,
unused-argument,
unused-import,
unused-variable,
# TODO(dougwig) - disable nonstandard-exception while we have neutron_lib shims
nonstandard-exception,
# "C" Coding convention violations
bad-continuation,
invalid-name,
missing-docstring,
old-style-class,
superfluous-parens,
# "R" Refactor recommendations
abstract-class-little-used,
abstract-class-not-used,
duplicate-code,
interface-not-implemented,
no-self-use,
too-few-public-methods,
too-many-ancestors,
too-many-arguments,
too-many-branches,
too-many-instance-attributes,
too-many-lines,
too-many-locals,
too-many-public-methods,
too-many-return-statements,
too-many-statements
[BASIC]
# Variable names can be 1 to 31 characters long, with lowercase and underscores
variable-rgx=[a-z_][a-z0-9_]{0,30}$
# Argument names can be 2 to 31 characters long, with lowercase and underscores
argument-rgx=[a-z_][a-z0-9_]{1,30}$
# Method names should be at least 3 characters long
# and be lowercased with underscores
method-rgx=([a-z_][a-z0-9_]{2,}|setUp|tearDown)$
# Module names matching neutron-* are ok (files in bin/)
module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+)|(neutron-[a-z0-9_-]+))$
# Don't require docstrings on tests.
no-docstring-rgx=((__.*__)|([tT]est.*)|setUp|tearDown)$
[FORMAT]
# Maximum number of characters on a single line.
max-line-length=79
[VARIABLES]
# List of additional names supposed to be defined in builtins. Remember that
# you should avoid to define new builtins when possible.
# _ is used by our localization
additional-builtins=_
[CLASSES]
# List of interface methods to ignore, separated by a comma.
ignore-iface-methods=
[IMPORTS]
# Deprecated modules which should not be used, separated by a comma
deprecated-modules=
# should use oslo_serialization.jsonutils
json
[TYPECHECK]
# List of module names for which member attributes should not be checked
ignored-modules=six.moves,_MovedItems
[REPORTS]
# Tells whether to display a full report or only the messages
reports=no

View File

@ -1,4 +0,0 @@
[DEFAULT]
test_command=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 OS_LOG_CAPTURE=1 ${PYTHON:-python} -m subunit.run discover -t ./ ${OS_TEST_PATH:-./neutron_fwaas/tests/unit} $LISTOPT $IDOPTION
test_id_option=--load-list $IDFILE
test_list_option=--list

View File

@ -1,4 +0,0 @@
Please see the Neutron CONTRIBUTING.rst file for how to contribute to
neutron-fwaas:
`Neutron CONTRIBUTING.rst <http://git.openstack.org/cgit/openstack/neutron/tree/CONTRIBUTING.rst>`_

View File

@ -1,7 +0,0 @@
Neutron FWaaS Style Commandments
================================
Please see the Neutron HACKING.rst file for style commandments for
neutron-fwaas:
`Neutron HACKING.rst <http://git.openstack.org/cgit/openstack/neutron/tree/HACKING.rst>`_

176
LICENSE
View File

@ -1,176 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.

View File

@ -1,11 +0,0 @@
include AUTHORS
include README.rst
include ChangeLog
include LICENSE
include neutron_fwaas/db/migration/alembic_migrations/script.py.mako
recursive-include neutron_fwaas/db/migration/alembic_migrations/versions *
exclude .gitignore
exclude .gitreview
global-exclude *.pyc

14
README Normal file
View File

@ -0,0 +1,14 @@
This project is no longer maintained.
The contents of this repository are still available in the Git
source code management system. To see the contents of this
repository before it reached its end of life, please check out the
previous commit with "git checkout HEAD^1".
For ongoing work on maintaining OpenStack packages in the Debian
distribution, please see the Debian OpenStack packaging team at
https://wiki.debian.org/OpenStack/.
For any further questions, please email
openstack-dev@lists.openstack.org or join #openstack-dev on
Freenode.

View File

@ -1,27 +0,0 @@
========================
Team and repository tags
========================
.. image:: https://governance.openstack.org/badges/neutron-fwaas.svg
:target: https://governance.openstack.org/reference/tags/index.html
.. Change things from this point on
Welcome!
========
This package contains the code for the Neutron Firewall as a Service
(FWaaS) service. This package requires Neutron to run.
External Resources:
===================
The homepage for Neutron is: https://launchpad.net/neutron. Use this
site for asking for help, and filing bugs. We use a single Launchpad
page for all Neutron projects.
Code is available on git.openstack.org at:
<https://git.openstack.org/cgit/openstack/neutron-fwaas>.
Please refer to Neutron documentation for more information:
`Neutron README.rst <https://git.openstack.org/cgit/openstack/neutron/tree/README.rst>`_

View File

@ -1,12 +0,0 @@
Testing Neutron FWaaS
=====================
Please see the TESTING.rst file for the Neutron project itself. This will have
the latest up to date instructions for how to test Neutron, and will
be applicable to neutron-fwaas as well:
`Neutron TESTING.rst <http://git.openstack.org/cgit/openstack/neutron/tree/TESTING.rst>`_
For instructions on how to use FWaaS with devstack, look at:
`Neutron-FWaaS DevStack <http://git.openstack.org/cgit/openstack/neutron-fwaas/tree/devstack/README.rst>`_

View File

@ -1,2 +0,0 @@
[python: **.py]

View File

@ -1,54 +0,0 @@
=========================
neutron-fwaas in DevStack
=========================
This is setup as a DevStack plugin. For more information on DevStack plugins,
see the `DevStack Plugins documentation
<https://docs.openstack.org/developer/devstack/plugins.html>`_.
Please note that the old 'q-fwaas' keyword still exists, and will run FWaaS V1.
This default will be changed during the Ocata cycle. The introduction of two
new keywords, 'q-fwaas-v1' and 'q-fwaas-v2' allow you to explicitly select the
version you with to run.
How to run FWaaS V2 in DevStack
===============================
Add the following to the localrc section of your local.conf to configure FWaaS v2.
.. code-block:: none
[[local|localrc]]
enable_plugin neutron-fwaas https://git.openstack.org/openstack/neutron-fwaas
enable_service q-fwaas-v2
To check a specific patchset that is currently under development, use a form
like the below example, which is checking out change 214350 patch set 14 for
testing.
.. code-block:: none
[[local|localrc]]
enable_plugin neutron-fwaas https://review.openstack.org/p/openstack/neutron-fwaas refs/changes/50/214350/14
enable_service q-fwaas-v2
How to run FWaaS V1 in DevStack
===============================
Add the following to the localrc section of your local.conf to configure FWaaS v1.
.. code-block:: none
[[local|localrc]]
enable_plugin neutron-fwaas https://git.openstack.org/openstack/neutron-fwaas
enable_service q-fwaas-v1
To check a specific patchset that is currently under development, use a form
like the below example, which is checking out change 214350 patch set 14 for
testing.
.. code-block:: none
[[local|localrc]]
enable_plugin neutron-fwaas https://review.openstack.org/p/openstack/neutron-fwaas refs/changes/50/214350/14
enable_service q-fwaas-v1

View File

@ -1,16 +0,0 @@
# This file was shamelessly stolen from the neutron repository here:
# http://git.openstack.org/cgit/openstack/neutron/tree/devstack/lib/l2_agent
function plugin_agent_add_l2_agent_extension {
local l2_agent_extension=$1
if [[ -z "$L2_AGENT_EXTENSIONS" ]]; then
L2_AGENT_EXTENSIONS=$l2_agent_extension
elif [[ ! ,${L2_AGENT_EXTENSIONS}, =~ ,${l2_agent_extension}, ]]; then
L2_AGENT_EXTENSIONS+=",$l2_agent_extension"
fi
}
function configure_l2_agent {
iniset /$Q_PLUGIN_CONF_FILE agent extensions "$L2_AGENT_EXTENSIONS"
}

View File

@ -1,16 +0,0 @@
# This file is completely based on one in the neutron repository here:
# http://git.openstack.org/cgit/openstack/neutron/tree/devstack/lib/l2_agent
function plugin_agent_add_l3_agent_extension {
local l3_agent_extension=$1
if [[ -z "$L3_AGENT_EXTENSIONS" ]]; then
L3_AGENT_EXTENSIONS=$l3_agent_extension
elif [[ ! ,${L3_AGENT_EXTENSIONS}, =~ ,${l3_agent_extension}, ]]; then
L3_AGENT_EXTENSIONS+=",$l3_agent_extension"
fi
}
function configure_l3_agent {
iniset $Q_L3_CONF_FILE AGENT extensions "$L3_AGENT_EXTENSIONS"
}

View File

@ -1,135 +0,0 @@
#!/bin/bash
# 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.
# Dependencies:
#
# ``functions`` file
# ``DEST`` must be defined
# Save trace setting
XTRACE=$(set +o | grep xtrace)
set +o xtrace
# Source in L2 and L3 agent extension management
LIBDIR=$DEST/neutron-fwaas/devstack/lib
source $LIBDIR/l2_agent
source $LIBDIR/l3_agent
function install_fwaas() {
# Install the service.
:
setup_develop $DEST/neutron-fwaas
}
function configure_fwaas_v1() {
cp $NEUTRON_FWAAS_DIR/etc/neutron_fwaas.conf.sample $NEUTRON_FWAAS_CONF
neutron_fwaas_configure_driver fwaas
iniset_multiline $Q_L3_CONF_FILE fwaas agent_version v1
iniset_multiline $Q_L3_CONF_FILE fwaas conntrack_driver conntrack
}
function configure_fwaas_v2() {
# Add conf file
cp $NEUTRON_FWAAS_DIR/etc/neutron_fwaas.conf.sample $NEUTRON_FWAAS_CONF
neutron_fwaas_configure_driver fwaas_v2
iniset_multiline $Q_L3_CONF_FILE fwaas agent_version v2
}
function neutron_fwaas_generate_config_files {
(cd $NEUTRON_FWAAS_DIR && exec ./tools/generate_config_file_samples.sh)
}
function init_fwaas() {
# Initialize and start the service.
:
if [ ! -d /etc/neutron/policy.d ]; then
mkdir /etc/neutron/policy.d
fi
cp $DEST/neutron-fwaas/etc/neutron/policy.d/neutron-fwaas.json /etc/neutron/policy.d/neutron-fwaas.json
# Using sudo to gain the root privilege to be able to copy file to rootwrap.d
sudo cp $DEST/neutron-fwaas/etc/neutron/rootwrap.d/fwaas-privsep.filters /etc/neutron/rootwrap.d/fwaas-privsep.filters
}
function shutdown_fwaas() {
# Shut the service down.
:
}
function cleanup_fwaas() {
# Cleanup the service.
:
}
function neutron_fwaas_configure_common {
if is_service_enabled q-fwaas-v1 neutron-fwaas-v1; then
neutron_service_plugin_class_add $FWAAS_PLUGIN_V1
elif is_service_enabled q-fwaas-v2 neutron-fwaas-v2; then
neutron_service_plugin_class_add $FWAAS_PLUGIN_V2
else
neutron_service_plugin_class_add $FWAAS_PLUGIN_V1
fi
}
function neutron_fwaas_configure_driver {
plugin_agent_add_l3_agent_extension $1
configure_l3_agent
iniset_multiline $Q_L3_CONF_FILE fwaas enabled True
iniset_multiline $Q_L3_CONF_FILE fwaas driver $FWAAS_DRIVER
}
# check for service enabled
if is_service_enabled q-svc neutron-api && is_service_enabled q-fwaas q-fwaas-v1 q-fwaas-v2 neutron-fwaas-v1 neutron-fwaas-v2; then
if [[ "$1" == "stack" && "$2" == "install" ]]; then
# Perform installation of service source
echo_summary "Installing neutron-fwaas"
install_fwaas
elif [[ "$1" == "stack" && "$2" == "post-config" ]]; then
# Configure after the other layer 1 and 2 services have been configured
neutron_fwaas_configure_common
neutron_fwaas_generate_config_files
if is_service_enabled q-fwaas-v1 neutron-fwaas-v1; then
echo_summary "Configuring neutron-fwaas for FWaaS v1"
configure_fwaas_v1
elif is_service_enabled q-fwaas-v2 neutron-fwaas-v2; then
echo_summary "Configuring neutron-fwaas for FWaaS v2"
configure_fwaas_v2
else
echo_summary "Configuring neutron-fwaas for FWaaS v1"
configure_fwaas_v1
fi
elif [[ "$1" == "stack" && "$2" == "extra" ]]; then
# Initialize and start the neutron-fwaas service
echo_summary "Initializing neutron-fwaas"
init_fwaas
fi
if [[ "$1" == "unstack" ]]; then
# Shut down neutron-fwaas services
# no-op
shutdown_fwaas
fi
if [[ "$1" == "clean" ]]; then
# Remove state and transient data
# Remember clean.sh first calls unstack.sh
# no-op
cleanup_fwaas
fi
fi
# Restore xtrace
$XTRACE

View File

@ -1,9 +0,0 @@
FWAAS_DRIVER=${FWAAS_DRIVER:-iptables}
FWAAS_PLUGIN_V1=${FWAAS_PLUGIN:-neutron_fwaas.services.firewall.fwaas_plugin.FirewallPlugin}
FWAAS_PLUGIN_V2=${FWAAS_PLUGIN:-neutron_fwaas.services.firewall.fwaas_plugin_v2.FirewallPluginV2}
NEUTRON_FWAAS_DIR=$DEST/neutron-fwaas
NEUTRON_FWAAS_CONF_FILE=neutron_fwaas.conf
NEUTRON_FWAAS_CONF=$NEUTRON_CONF_DIR/$NEUTRON_FWAAS_CONF_FILE
neutron_server_config_add $NEUTRON_FWAAS_CONF

View File

@ -1,244 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2010 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.
#
# Keystone documentation build configuration file, created by
# sphinx-quickstart on Tue May 18 13:50:15 2010.
#
# This file is execfile()'d with the current directory set to it's containing
# dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import os
import sys
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
ROOT_DIR = os.path.abspath(os.path.join(BASE_DIR, "..", ".."))
sys.path.insert(0, ROOT_DIR)
# -- General configuration ---------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc',
'sphinx.ext.coverage',
'sphinx.ext.ifconfig',
'sphinx.ext.graphviz',
'sphinx.ext.todo',
'openstackdocstheme',]
todo_include_todos = True
# Add any paths that contain templates here, relative to this directory.
templates_path = []
if os.getenv('HUDSON_PUBLISH_DOCS'):
templates_path = ['_ga', '_templates']
else:
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'Neutron FWaaS'
copyright = u'2011-present, OpenStack Foundation.'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# Version info
from neutron_fwaas.version import version_info as neutron_fwaas_version
release = neutron_fwaas_version.release_string()
# The short X.Y version.
version = neutron_fwaas_version.version_string()
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of documents that shouldn't be included in the build.
# unused_docs = []
# List of directories, relative to source directory, that shouldn't be searched
# for source files.
exclude_trees = []
# The reST default role (for this markup: `text`) to use for all documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
show_authors = True
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
modindex_common_prefix = ['neutron_fwaas.']
# -- Options for man page output --------------------------------------------
# Grouping the document tree for man pages.
# List of tuples 'sourcefile', 'target', u'title', u'Authors name', 'manual'
#man_pages = [
# ('man/neutron-server', 'neutron-server', u'Neutron Server',
# [u'OpenStack'], 1)
#]
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. Major themes that come with
# Sphinx are currently 'default' and 'sphinxdoc'.
# html_theme_path = ["."]
# html_theme = '_theme'
html_theme = 'openstackdocs'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = ['_theme']
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
# html_static_path = ['_static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
# html_last_updated_fmt = '%b %d, %Y'
html_last_updated_fmt = '%Y-%m-%d %H:%M'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_use_modindex = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = ''
# Output file base name for HTML help builder.
#htmlhelp_basename = 'neutrondoc'
# -- Options for LaTeX output ------------------------------------------------
# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'
# The font size ('10pt', '11pt' or '12pt').
#latex_font_size = '10pt'
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author,
# documentclass [howto/manual]).
#latex_documents = [
# ('index', 'Neutron.tex', u'Neutron Documentation',
# u'Neutron development team', 'manual'),
#]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# Additional stuff for the LaTeX preamble.
#latex_preamble = ''
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_use_modindex = True
# -- Options for openstackdocstheme -------------------------------------------
repository_name = 'openstack/neutron-fwaas'
bug_project = 'neutron'
bug_tag = 'doc'

View File

@ -1,28 +0,0 @@
=============================
Contributing to neutron-fwaas
=============================
If you would like to contribute to the development of OpenStack, you must
follow the steps documented at:
https://docs.openstack.org/infra/manual/developers.html
Once those steps have been completed, changes to OpenStack should be submitted
for review via the Gerrit tool, following the workflow documented at:
https://docs.openstack.org/infra/manual/developers.html#development-workflow
Pull requests submitted through GitHub will be ignored.
Bugs should be filed on Launchpad in the 'neutron' project:
https://bugs.launchpad.net/neutron
To get in touch with the neutron-fwaas community,
look at the following resources:
- Join the #openstack-fwaas IRC channel on Freenode. This is where the
FireWall-as-a-Service team is available for discussion.
- Join the `FireWall-as-a-Service weekly IRC meeting
<http://eavesdrop.openstack.org/#Firewall_as_a_Service_(FWaaS)_Team_Meeting>`_
where the status of new initiatives and bugs is discussed.
These are a great places to get recommendations on where to start contributing
to neutron-fwaas.

View File

@ -1 +0,0 @@
.. include:: ../../../devstack/README.rst

View File

@ -1,7 +0,0 @@
FireWall as a Service V2
========================
The `FireWall as a Service API V2
<https://specs.openstack.org/openstack/neutron-specs/specs/newton/fwaas-api-2.0.html>`_
specification lists the changes that together compose FWaaS V2. These changes
are not fully implemented.

View File

@ -1,17 +0,0 @@
=================
Contributor Guide
=================
.. toctree::
:maxdepth: 2
contributing
fwaas_v2
devstack
.. API reference contains a lot of sections, toctree with maxdepth 1 is used.
.. toctree::
:glob:
:maxdepth: 1
modules

View File

@ -1,9 +0,0 @@
================
Module Reference
================
.. toctree::
:maxdepth: 1
:glob:
api/*

View File

@ -1,18 +0,0 @@
===========================
neutron-fwaas documentation
===========================
General Information and Other Project References:
.. toctree::
:glob:
:maxdepth: 2
install/index
contributor/index
.. rubric:: Indices and tables
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

View File

@ -1,39 +0,0 @@
..
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.
Convention for heading levels in Neutron devref:
======= Heading 0 (reserved for the title in a document)
------- Heading 1
~~~~~~~ Heading 2
+++++++ Heading 3
''''''' Heading 4
(Avoid deeper levels because they do not render well.)
============
Installation
============
At the command line::
$ pip install neutron-fwaas
Or, if you have virtualenvwrapper installed::
$ mkvirtualenv neutron-fwaas
$ pip install neutron-fwaas
For information on what to do with FWaaS once it is installed, please check the
Networking Guide `Firewall-as-a-Service (FWaaS) v2 scenario <https://docs.openstack.org/newton/networking-guide/fwaas-v2-scenario.html>`_ or
the `Firewall-as-a-Service (FWaaS) v1 scenario <https://docs.openstack.org/newton/networking-guide/fwaas-v1-scenario.html>`_.

View File

@ -1,9 +0,0 @@
To generate the sample neutron-fwaas configuration files, run the following
command from the top level of the neutron-fwaas directory:
tox -e genconfig
If a 'tox' environment is unavailable, then you can run the following script
instead to generate the configuration files:
./tools/generate_config_file_samples.sh

View File

@ -1,52 +0,0 @@
{
"shared_firewalls": "field:firewalls:shared=True",
"shared_firewall_policies": "field:firewall_policies:shared=True",
"shared_firewall_rules": "field:firewall_rules:shared=True",
"create_firewall": "",
"update_firewall": "rule:admin_or_owner",
"delete_firewall": "rule:admin_or_owner",
"create_firewall:shared": "rule:admin_only",
"update_firewall:shared": "rule:admin_only",
"delete_firewall:shared": "rule:admin_only",
"get_firewall": "rule:admin_or_owner or rule:shared_firewalls",
"shared_firewall_groups": "field:firewall_groups:shared=True",
"shared_firewall_policies": "field:firewall_policies:shared=True",
"shared_firewall_rules": "field:firewall_rules:shared=True",
"create_firewall_group": "",
"update_firewall_group": "rule:admin_or_owner",
"delete_firewall_group": "rule:admin_or_owner",
"create_firewall_group:shared": "rule:admin_only",
"update_firewall_group:shared": "rule:admin_only",
"delete_firewall_group:shared": "rule:admin_only",
"get_firewall_group": "rule:admin_or_owner or rule:shared_firewall_groups",
"create_firewall_policy": "",
"update_firewall_policy": "rule:admin_or_owner",
"delete_firewall_policy": "rule:admin_or_owner",
"create_firewall_policy:shared": "rule:admin_only",
"update_firewall_policy:shared": "rule:admin_only",
"delete_firewall_policy:shared": "rule:admin_only",
"get_firewall_policy": "rule:admin_or_owner or rule:shared_firewall_policies",
"insert_rule": "rule:admin_or_owner",
"remove_rule": "rule:admin_or_owner",
"create_firewall_rule": "",
"update_firewall_rule": "rule:admin_or_owner",
"delete_firewall_rule": "rule:admin_or_owner",
"create_firewall_rule:shared": "rule:admin_only",
"update_firewall_rule:shared": "rule:admin_only",
"delete_firewall_rule:shared": "rule:admin_only",
"get_firewall_rule": "rule:admin_or_owner or rule:shared_firewall_rules"
}

View File

@ -1,7 +0,0 @@
# neutron-fwaas privsep filters
# This file should be owned by (and only-writeable by) the root user
[Filters]
privsep-rootwrap: PathFilter, privsep-helper, root, privsep-helper, --config-file, /etc/(?!\.\.).*, --privsep_context, neutron_fwaas.privileged.default

View File

@ -1,5 +0,0 @@
[DEFAULT]
output_file = etc/fwaas_driver.ini.sample
wrap_width = 79
namespace = firewall.agent

View File

@ -1,6 +0,0 @@
[DEFAULT]
output_file = etc/neutron_fwaas.conf.sample
wrap_width = 79
namespace = neutron.fwaas

View File

@ -1,24 +0,0 @@
# Copyright 2011 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.
import gettext
import six
if six.PY2:
gettext.install('neutron', unicode=1)
else:
gettext.install('neutron')

View File

@ -1,42 +0,0 @@
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import oslo_i18n
DOMAIN = "neutron_fwaas"
_translators = oslo_i18n.TranslatorFactory(domain=DOMAIN)
# The primary translation function using the well-known name "_"
_ = _translators.primary
# The contextual translation function using the name "_C"
_C = _translators.contextual_form
# The plural translation function using the name "_P"
_P = _translators.plural_form
# Translators for log levels.
#
# The abbreviated names are meant to reflect the usual use of a short
# name like '_'. The "L" is for "log" and the other letter comes from
# the level.
_LI = _translators.log_info
_LW = _translators.log_warning
_LE = _translators.log_error
_LC = _translators.log_critical
def get_available_languages():
return oslo_i18n.get_available_languages(DOMAIN)

View File

@ -1,22 +0,0 @@
# Copyright 2015 Cisco Systems, Inc
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
FIREWALL = 'FIREWALL'
# Constants for "topics"
FIREWALL_PLUGIN = 'q-firewall-plugin'
L3_AGENT = 'l3_agent'
FW_AGENT = 'firewall_agent'
FIREWALL_RULE_LIST = 'firewall_rule_list'

View File

@ -1,17 +0,0 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from neutron_fwaas.db.firewall.v2 import firewall_db_v2
FIREWALL_GROUP = firewall_db_v2.FirewallGroup
FIREWALL_POLICY = firewall_db_v2.FirewallPolicy
FIREWALL_RULE = firewall_db_v2.FirewallRuleV2

View File

@ -1,657 +0,0 @@
# 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.
import operator
from neutron.db import common_db_mixin as base_db
from neutron.db.models import agent as agent_model
from neutron.db.models import l3agent as l3agent_model
from neutron_lib.callbacks import events
from neutron_lib.callbacks import registry
from neutron_lib.callbacks import resources
from neutron_lib import constants as nl_constants
from neutron_lib.db import model_base
from neutron_lib.exceptions import firewall_v1 as f_exc
from neutron_lib.exceptions import l3
from neutron_lib.plugins import directory
from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import uuidutils
import sqlalchemy as sa
from sqlalchemy.ext.orderinglist import ordering_list
from sqlalchemy import orm
from sqlalchemy.orm import exc
import netaddr
from neutron_fwaas.common import fwaas_constants
from neutron_fwaas.db.firewall import firewall_router_insertion_db \
as fw_r_ins_db
from neutron_fwaas.extensions import firewall as fw_ext
LOG = logging.getLogger(__name__)
class FirewallRule(model_base.BASEV2, model_base.HasId, model_base.HasProject):
"""Represents a Firewall rule."""
__tablename__ = 'firewall_rules'
__table_args__ = ({'mysql_collate': 'utf8_bin'})
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', 'reject',
name='firewallrules_action'))
enabled = sa.Column(sa.Boolean)
position = sa.Column(sa.Integer)
class Firewall(model_base.BASEV2, model_base.HasId, model_base.HasProject):
"""Represents a Firewall resource."""
__tablename__ = 'firewalls'
__table_args__ = ({'mysql_collate': 'utf8_bin'})
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, model_base.HasId,
model_base.HasProject):
"""Represents a Firewall Policy resource."""
__tablename__ = 'firewall_policies'
__table_args__ = ({'mysql_collate': 'utf8_bin'})
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(fw_ext.FirewallPluginBase, base_db.CommonDbMixin):
"""Mixin class for Firewall DB implementation."""
@property
def _core_plugin(self):
return directory.get_plugin()
def _get_firewall(self, context, id):
try:
return self._get_by_id(context, Firewall, id)
except exc.NoResultFound:
raise f_exc.FirewallNotFound(firewall_id=id)
def _get_firewall_policy(self, context, id):
try:
return self._get_by_id(context, FirewallPolicy, id)
except exc.NoResultFound:
raise f_exc.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 f_exc.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 _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_rules_list = self.get_firewall_rules(
context, filters={'firewall_policy_id': [fw_policy_id]})
fw_rules_list = sorted(
fw_rules_list, key=operator.itemgetter('position'))
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 _check_firewall_rule_conflict(self, fwr_db, fwp_db):
if not fwr_db['shared']:
if fwr_db['tenant_id'] != fwp_db['tenant_id']:
raise f_exc.FirewallRuleConflict(
firewall_rule_id=fwr_db['id'],
project_id=fwr_db['tenant_id'])
def _set_rules_for_policy(self, context, firewall_policy_db, fwp):
rule_id_list = fwp['firewall_rules']
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 f_exc.FirewallRuleNotFound(
firewall_rule_id=fwrule_id)
elif rules_dict[fwrule_id]['firewall_policy_id']:
if (rules_dict[fwrule_id]['firewall_policy_id'] !=
fwp_db['id']):
raise f_exc.FirewallRuleInUse(
firewall_rule_id=fwrule_id)
if 'shared' in fwp:
if fwp['shared'] and not rules_dict[fwrule_id]['shared']:
raise f_exc.FirewallRuleSharingConflict(
firewall_rule_id=fwrule_id,
firewall_policy_id=fwp_db['id'])
elif fwp_db['shared'] and not rules_dict[fwrule_id]['shared']:
raise f_exc.FirewallRuleSharingConflict(
firewall_rule_id=fwrule_id,
firewall_policy_id=fwp_db['id'])
for fwr_db in rules_in_db:
self._check_firewall_rule_conflict(fwr_db, fwp_db)
# 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.firewall_rules.reorder()
fwp_db.audited = False
def _check_unshared_rules_for_policy(self, fwp_db, fwp):
if fwp['shared']:
rules_in_db = fwp_db['firewall_rules']
for fwr_db in rules_in_db:
if not fwr_db['shared']:
raise f_exc.FirewallPolicySharingConflict(
firewall_rule_id=fwr_db['id'],
firewall_policy_id=fwp_db['id'])
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]
min_port, sep, max_port = port_range.partition(":")
if not max_port:
max_port = min_port
self._validate_fwr_port_range(min_port, max_port)
return [int(min_port), int(max_port)]
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)
self._validate_fwr_port_range(min_port, max_port)
return '%s:%s' % (min_port, max_port)
def _validate_fw_parameters(self, context, fw, fw_tenant_id):
if 'firewall_policy_id' not in fw:
return
fwp_id = fw['firewall_policy_id']
fwp = self._get_firewall_policy(context, fwp_id)
if fw_tenant_id != fwp['tenant_id'] and not fwp['shared']:
raise f_exc.FirewallPolicyConflict(firewall_policy_id=fwp_id)
def _validate_fwr_src_dst_ip_version(self, fwr):
src_version = dst_version = None
if fwr.get('source_ip_address', None):
src_version = netaddr.IPNetwork(fwr['source_ip_address']).version
if fwr.get('destination_ip_address', None):
dst_version = netaddr.IPNetwork(
fwr['destination_ip_address']).version
rule_ip_version = fwr.get('ip_version', None)
if ((src_version and src_version != rule_ip_version) or
(dst_version and dst_version != rule_ip_version)):
raise f_exc.FirewallIpAddressConflict()
def _validate_fwr_port_range(self, min_port, max_port):
if int(min_port) > int(max_port):
port_range = '%s:%s' % (min_port, max_port)
raise f_exc.FirewallRuleInvalidPortValue(port=port_range)
def _validate_fwr_protocol_parameters(self, fwr):
protocol = fwr.get('protocol', None)
if protocol not in (nl_constants.PROTO_NAME_TCP,
nl_constants.PROTO_NAME_UDP):
if (fwr.get('source_port', None) or
fwr.get('destination_port', None)):
raise f_exc.FirewallRuleInvalidICMPParameter(
param="Source, destination port")
def create_firewall(self, context, firewall, status=None):
LOG.debug("create_firewall() called")
fw = firewall['firewall']
tenant_id = fw['tenant_id']
# distributed routers may required a more complex state machine;
# the introduction of a new 'CREATED' state allows this, whilst
# keeping a backward compatible behavior of the logical resource.
if not status:
status = (nl_constants.CREATED if cfg.CONF.router_distributed
else nl_constants.PENDING_CREATE)
with context.session.begin(subtransactions=True):
self._validate_fw_parameters(context, fw, tenant_id)
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=status)
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_db = self.get_firewall(context, id)
self._validate_fw_parameters(context, fw, fw_db['tenant_id'])
count = context.session.query(Firewall).filter_by(id=id).update(fw)
if not count:
raise f_exc.FirewallNotFound(firewall_id=id)
return self.get_firewall(context, id)
def update_firewall_status(self, context, id, status, not_in=None):
"""Conditionally update firewall status.
Status transition is performed only if firewall is not in the specified
states as defined by 'not_in' list.
"""
# filter in_ wants iterable objects, None isn't.
not_in = not_in or []
with context.session.begin(subtransactions=True):
return (context.session.query(Firewall).
filter(Firewall.id == id).
filter(~Firewall.status.in_(not_in)).
update({'status': status}, synchronize_session=False))
def delete_firewall(self, context, id):
LOG.debug("delete_firewall() called")
with context.session.begin(subtransactions=True):
# Note: Plugin should ensure that it's okay to delete if the
# firewall is active
count = context.session.query(Firewall).filter_by(id=id).delete()
if not count:
raise f_exc.FirewallNotFound(firewall_id=id)
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']
with context.session.begin(subtransactions=True):
fwp_db = FirewallPolicy(id=uuidutils.generate_uuid(),
tenant_id=fwp['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)
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)
# check tenant ids are same for fw and fwp or not
if not fwp.get('shared', True) and fwp_db.firewalls:
for fw in fwp_db['firewalls']:
if fwp_db['tenant_id'] != fw['tenant_id']:
raise f_exc.FirewallPolicyInUse(
firewall_policy_id=id)
# check any existing rules are not shared
if 'shared' in fwp and 'firewall_rules' not in fwp:
self._check_unshared_rules_for_policy(fwp_db, fwp)
elif 'firewall_rules' in fwp:
self._set_rules_for_policy(context, fwp_db, fwp)
del fwp['firewall_rules']
if 'audited' not in fwp:
fwp['audited'] = False
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 f_exc.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']
self._validate_fwr_protocol_parameters(fwr)
self._validate_fwr_src_dst_ip_version(fwr)
if not fwr['protocol'] and (fwr['source_port'] or
fwr['destination_port']):
raise f_exc.FirewallRuleWithPortWithoutProtocolInvalid()
src_port_min, src_port_max = self._get_min_max_ports_from_range(
fwr['source_port'])
dst_port_min, dst_port_max = self._get_min_max_ports_from_range(
fwr['destination_port'])
with context.session.begin(subtransactions=True):
fwr_db = FirewallRule(
id=uuidutils.generate_uuid(),
tenant_id=fwr['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']
self._validate_fwr_protocol_parameters(fwr)
self._validate_fwr_src_dst_ip_version(fwr)
fwr_db = self._get_firewall_rule(context, id)
if fwr_db.firewall_policy_id:
fwp_db = self._get_firewall_policy(context,
fwr_db.firewall_policy_id)
if 'shared' in fwr and not fwr['shared']:
if fwr_db['tenant_id'] != fwp_db['tenant_id']:
raise f_exc.FirewallRuleInUse(firewall_rule_id=id)
if 'source_port' in fwr:
src_port_min, src_port_max = self._get_min_max_ports_from_range(
fwr['source_port'])
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):
protocol = fwr.get('protocol', fwr_db['protocol'])
if not protocol:
sport = fwr.get('source_port_range_min',
fwr_db['source_port_range_min'])
dport = fwr.get('destination_port_range_min',
fwr_db['destination_port_range_min'])
if sport or dport:
raise f_exc.FirewallRuleWithPortWithoutProtocolInvalid()
fwr_db.update(fwr)
if 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 f_exc.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 f_exc.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 f_exc.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)
fwp_db = self._get_firewall_policy(context, id)
if fwr_db.firewall_policy_id:
raise f_exc.FirewallRuleInUse(firewall_rule_id=fwr_db['id'])
self._check_firewall_rule_conflict(fwr_db, fwp_db)
if ref_firewall_rule_id:
# If reference_firewall_rule_id is set, the new rule
# 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 ref_fwr_db.firewall_policy_id != id:
raise f_exc.FirewallRuleNotAssociatedWithPolicy(
firewall_rule_id=ref_fwr_db['id'],
firewall_policy_id=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 f_exc.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 f_exc.FirewallRuleNotAssociatedWithPolicy(
firewall_rule_id=fwr_db['id'],
firewall_policy_id=id)
return self._process_rule_for_policy(context, id, fwr_db, None)
def get_firewall_tenant_ids_on_host(self, context, host):
query = context.session.query(Firewall.tenant_id)
query = query.join(fw_r_ins_db.FirewallRouterAssociation)
query = query.join(l3agent_model.RouterL3AgentBinding,
l3agent_model.RouterL3AgentBinding.router_id ==
fw_r_ins_db.FirewallRouterAssociation.router_id)
query = query.join(agent_model.Agent)
query = query.filter(agent_model.Agent.host == host)
query = query.distinct()
return [item[0] for item in query]
def migration_callback(resource, event, trigger, **kwargs):
context = kwargs['context']
router = kwargs['router']
fw_plugin = directory.get_plugin(fwaas_constants.FIREWALL)
if fw_plugin:
tenant_firewalls = fw_plugin.get_firewalls(
context, filters={'tenant_id': [router['tenant_id']]})
if tenant_firewalls:
raise l3.RouterInUse(router_id=router['id'])
def subscribe():
registry.subscribe(
migration_callback, resources.ROUTER, events.BEFORE_UPDATE)
# NOTE(armax): multiple FW service plugins (potentially out of tree) may
# inherit from firewall_db and may need the callbacks to be processed. Having
# an implicit subscription (through the module import) preserves the existing
# behavior, and at the same time it avoids fixing it manually in each and
# every fw plugin out there. That said, The subscription is also made
# explicitly in the reference fw plugin. The subscription operation is
# idempotent so there is no harm in registering the same callback multiple
# times.
subscribe()

View File

@ -1,98 +0,0 @@
# Copyright 2015 Cisco Systems Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from neutron_lib.db import model_base
from neutron_lib.exceptions import firewall_v1 as fwrtrins
from oslo_log import helpers as log_helpers
from oslo_log import log as logging
import sqlalchemy as sa
LOG = logging.getLogger(__name__)
class FirewallRouterAssociation(model_base.BASEV2):
"""Tracks FW Router Association"""
__tablename__ = 'firewall_router_associations'
fw_id = sa.Column(sa.String(36),
sa.ForeignKey('firewalls.id', ondelete="CASCADE"),
primary_key=True)
router_id = sa.Column(sa.String(36),
sa.ForeignKey('routers.id', ondelete="CASCADE"),
primary_key=True)
class FirewallRouterInsertionDbMixin(object):
"""Access methods for the firewall_router_associations table."""
@log_helpers.log_method_call
def set_routers_for_firewall(self, context, fw):
"""Sets the routers associated with the fw."""
with context.session.begin(subtransactions=True):
for r_id in fw['router_ids']:
fw_rtr_db = FirewallRouterAssociation(fw_id=fw['fw_id'],
router_id=r_id)
context.session.add(fw_rtr_db)
@log_helpers.log_method_call
def get_firewall_routers(self, context, fwid):
"""Gets all routers associated with a firewall."""
with context.session.begin(subtransactions=True):
fw_rtr_qry = context.session.query(
FirewallRouterAssociation.router_id)
fw_rtr_rows = fw_rtr_qry.filter_by(fw_id=fwid)
fw_rtrs = [entry.router_id for entry in fw_rtr_rows]
LOG.debug("get_firewall_routers(): fw_rtrs: %s", fw_rtrs)
return fw_rtrs
@log_helpers.log_method_call
def validate_firewall_routers_not_in_use(
self, context, router_ids, fwid=None):
"""Validate if router-ids not associated with any firewall.
If any of the router-ids in the list is already associated with
a firewall, raise an exception else just return.
"""
fw_rtr_qry = context.session.query(FirewallRouterAssociation.router_id)
fw_rtrs = fw_rtr_qry.filter(
FirewallRouterAssociation.router_id.in_(router_ids),
FirewallRouterAssociation.fw_id != fwid).all()
if fw_rtrs:
router_ids = [entry.router_id for entry in fw_rtrs]
raise fwrtrins.FirewallRouterInUse(router_ids=router_ids)
@log_helpers.log_method_call
def update_firewall_routers(self, context, fw):
"""Update the firewall with new routers.
This involves removing existing router associations and replacing
it with the new router associations provided in the update method.
"""
with context.session.begin(subtransactions=True):
fw_rtr_qry = context.session.query(FirewallRouterAssociation)
fw_rtr_qry.filter_by(fw_id=fw['fw_id']).delete()
if fw['router_ids']:
self.set_routers_for_firewall(context, fw)
# TODO(sridar): Investigate potential corner case if rpc failure
# happens on PENDING_UPDATE and agent did not restart. Evaluate
# complexity vs benefit of holding on to old entries until ack
# from agent.
return fw

View File

@ -1,816 +0,0 @@
# Copyright (c) 2016
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from neutron.db import common_db_mixin as base_db
from neutron_lib import constants as nl_constants
from neutron_lib.db import model_base
from neutron_lib.exceptions import firewall_v2 as f_exc
from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import uuidutils
import sqlalchemy as sa
from sqlalchemy.ext.orderinglist import ordering_list
from sqlalchemy import orm
from sqlalchemy.orm import exc
import netaddr
from neutron_fwaas.extensions import firewall_v2 as fw_ext
LOG = logging.getLogger(__name__)
class HasName(object):
name = sa.Column(sa.String(255))
class HasDescription(object):
description = sa.Column(sa.String(1024))
class FirewallRuleV2(model_base.BASEV2, model_base.HasId, HasName,
HasDescription, model_base.HasProject):
__tablename__ = "firewall_rules_v2"
shared = sa.Column(sa.Boolean)
protocol = sa.Column(sa.String(40))
ip_version = sa.Column(sa.Integer)
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', 'reject',
name='firewallrules_action'))
enabled = sa.Column(sa.Boolean)
class FirewallGroup(model_base.BASEV2, model_base.HasId, HasName,
HasDescription, model_base.HasProject):
__tablename__ = 'firewall_groups_v2'
ports = orm.relationship(
'FirewallGroupPortAssociation',
backref=orm.backref('firewall_group_port_associations_v2',
cascade='all, delete'))
name = sa.Column(sa.String(255))
description = sa.Column(sa.String(1024))
ingress_firewall_policy_id = sa.Column(sa.String(36),
sa.ForeignKey(
'firewall_policies_v2.id'))
egress_firewall_policy_id = sa.Column(sa.String(36),
sa.ForeignKey(
'firewall_policies_v2.id'))
admin_state_up = sa.Column(sa.Boolean)
status = sa.Column(sa.String(16))
shared = sa.Column(sa.Boolean)
class FirewallGroupPortAssociation(model_base.BASEV2):
__tablename__ = 'firewall_group_port_associations_v2'
firewall_group_id = sa.Column(sa.String(36),
sa.ForeignKey('firewall_groups_v2.id',
ondelete="CASCADE"),
primary_key=True)
port_id = sa.Column(sa.String(36),
sa.ForeignKey('ports.id', ondelete="CASCADE"),
primary_key=True)
class FirewallPolicyRuleAssociation(model_base.BASEV2):
"""Tracks FW Policy and Rule(s) Association"""
__tablename__ = 'firewall_policy_rule_associations_v2'
firewall_policy_id = sa.Column(sa.String(36),
sa.ForeignKey('firewall_policies_v2.id',
ondelete="CASCADE"),
primary_key=True)
firewall_rule_id = sa.Column(sa.String(36),
sa.ForeignKey('firewall_rules_v2.id',
ondelete="CASCADE"),
primary_key=True)
position = sa.Column(sa.Integer)
class FirewallPolicy(model_base.BASEV2, model_base.HasId, HasName,
HasDescription, model_base.HasProject):
__tablename__ = 'firewall_policies_v2'
name = sa.Column(sa.String(255))
description = sa.Column(sa.String(1024))
rule_count = sa.Column(sa.Integer)
audited = sa.Column(sa.Boolean)
rule_associations = orm.relationship(
FirewallPolicyRuleAssociation,
backref=orm.backref('firewall_policies_v2', cascade='all, delete'),
order_by='FirewallPolicyRuleAssociation.position',
collection_class=ordering_list('position', count_from=1))
shared = sa.Column(sa.Boolean)
class Firewall_db_mixin_v2(fw_ext.Firewallv2PluginBase, base_db.CommonDbMixin):
def _get_firewall_group(self, context, id):
try:
return self._get_by_id(context, FirewallGroup, id)
except exc.NoResultFound:
raise f_exc.FirewallGroupNotFound(firewall_id=id)
def _get_firewall_policy(self, context, id):
try:
return self._get_by_id(context, FirewallPolicy, id)
except exc.NoResultFound:
raise f_exc.FirewallPolicyNotFound(firewall_policy_id=id)
def _get_firewall_rule(self, context, id):
try:
return self._get_by_id(context, FirewallRuleV2, id)
except exc.NoResultFound:
raise f_exc.FirewallRuleNotFound(firewall_rule_id=id)
def _validate_fwr_protocol_parameters(self, fwr, fwr_db=None):
protocol = fwr.get('protocol', None)
if fwr_db and not protocol:
protocol = fwr_db.protocol
if protocol not in (nl_constants.PROTO_NAME_TCP,
nl_constants.PROTO_NAME_UDP):
if (fwr.get('source_port', None) or
fwr.get('destination_port', None)):
raise f_exc.FirewallRuleInvalidICMPParameter(
param="Source, destination port")
def _validate_fwr_src_dst_ip_version(self, fwr, fwr_db=None):
src_version = dst_version = None
if fwr.get('source_ip_address', None):
src_version = netaddr.IPNetwork(fwr['source_ip_address']).version
if fwr.get('destination_ip_address', None):
dst_version = netaddr.IPNetwork(
fwr['destination_ip_address']).version
rule_ip_version = fwr.get('ip_version', None)
if not rule_ip_version and fwr_db:
rule_ip_version = fwr_db.ip_version
if ((src_version and src_version != rule_ip_version) or
(dst_version and dst_version != rule_ip_version)):
raise f_exc.FirewallIpAddressConflict()
def _validate_fwr_port_range(self, min_port, max_port):
if int(min_port) > int(max_port):
port_range = '%s:%s' % (min_port, max_port)
raise f_exc.FirewallRuleInvalidPortValue(port=port_range)
def _get_min_max_ports_from_range(self, port_range):
if not port_range:
return [None, None]
min_port, sep, max_port = port_range.partition(":")
if not max_port:
max_port = min_port
self._validate_fwr_port_range(min_port, max_port)
return [int(min_port), int(max_port)]
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)
self._validate_fwr_port_range(min_port, max_port)
return '%s:%s' % (min_port, max_port)
def _make_firewall_rule_dict(self, firewall_rule, fields=None):
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'],
'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'],
'enabled': firewall_rule['enabled'],
'shared': firewall_rule['shared']}
return self._fields(res, fields)
def _make_firewall_policy_dict(self, firewall_policy, fields=None):
fw_rules = [
rule_association.firewall_rule_id
for rule_association in firewall_policy['rule_associations']]
res = {'id': firewall_policy['id'],
'tenant_id': firewall_policy['tenant_id'],
'name': firewall_policy['name'],
'description': firewall_policy['description'],
'audited': firewall_policy['audited'],
'firewall_rules': fw_rules,
'shared': firewall_policy['shared']}
return self._fields(res, fields)
def _make_firewall_group_dict(self, firewall_group, fields=None):
fwg_ports = [
port_assoc.port_id for port_assoc in firewall_group['ports']
]
res = {'id': firewall_group['id'],
'tenant_id': firewall_group['tenant_id'],
'name': firewall_group['name'],
'description': firewall_group['description'],
'ingress_firewall_policy_id':
firewall_group['ingress_firewall_policy_id'],
'egress_firewall_policy_id':
firewall_group['egress_firewall_policy_id'],
'admin_state_up': firewall_group['admin_state_up'],
'ports': fwg_ports,
'status': firewall_group['status'],
'shared': firewall_group['shared']}
return self._fields(res, fields)
def _get_policy_ordered_rules(self, context, policy_id):
query = (context.session.query(FirewallRuleV2)
.join(FirewallPolicyRuleAssociation)
.filter_by(firewall_policy_id=policy_id)
.order_by(FirewallPolicyRuleAssociation.position))
return [self._make_firewall_rule_dict(rule) for rule in query]
def _make_firewall_group_dict_with_rules(self, context, firewall_group_id):
firewall_group = self.get_firewall_group(context, firewall_group_id)
ingress_policy_id = firewall_group['ingress_firewall_policy_id']
if ingress_policy_id:
firewall_group['ingress_rule_list'] = (
self._get_policy_ordered_rules(context, ingress_policy_id))
else:
firewall_group['ingress_rule_list'] = []
egress_policy_id = firewall_group['egress_firewall_policy_id']
if egress_policy_id:
firewall_group['egress_rule_list'] = (
self._get_policy_ordered_rules(context, egress_policy_id))
else:
firewall_group['egress_rule_list'] = []
return firewall_group
def _check_firewall_rule_conflict(self, fwr_db, fwp_db):
if not fwr_db['shared']:
if fwr_db['tenant_id'] != fwp_db['tenant_id']:
raise f_exc.FirewallRuleConflict(
firewall_rule_id=fwr_db['id'],
project_id=fwr_db['tenant_id'])
def _process_rule_for_policy(self, context, firewall_policy_id,
firewall_rule_id, position, association_db):
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.rule_associations.insert(
position - 1,
FirewallPolicyRuleAssociation(
firewall_rule_id=firewall_rule_id))
else:
fwp_db.rule_associations.remove(association_db)
context.session.delete(association_db)
fwp_db.rule_associations.reorder()
fwp_db.audited = False
return self._make_firewall_policy_dict(fwp_db)
def _get_policy_rule_association_query(self, context, firewall_policy_id,
firewall_rule_id):
fwpra_query = context.session.query(FirewallPolicyRuleAssociation)
return fwpra_query.filter_by(firewall_policy_id=firewall_policy_id,
firewall_rule_id=firewall_rule_id)
def _ensure_rule_not_already_associated(self, context, firewall_policy_id,
firewall_rule_id):
"""Checks that a rule is not already associated with a particular
policy. If it is the function will throw an exception.
"""
try:
self._get_policy_rule_association_query(
context, firewall_policy_id, firewall_rule_id).one()
raise f_exc.FirewallRuleAlreadyAssociated(
firewall_rule_id=firewall_rule_id,
firewall_policy_id=firewall_policy_id)
except exc.NoResultFound:
return
def _get_policy_rule_association(self, context, firewall_policy_id,
firewall_rule_id):
"""Returns the association between a firewall rule and a firewall
policy. Throws an exception if the assocaition does not exist.
"""
try:
return self._get_policy_rule_association_query(
context, firewall_policy_id, firewall_rule_id).one()
except exc.NoResultFound:
raise f_exc.FirewallRuleNotAssociatedWithPolicy(
firewall_rule_id=firewall_rule_id,
firewall_policy_id=firewall_policy_id)
def create_firewall_rule(self, context, firewall_rule):
LOG.debug("create_firewall_rule() called")
fwr = firewall_rule['firewall_rule']
self._validate_fwr_protocol_parameters(fwr)
self._validate_fwr_src_dst_ip_version(fwr)
if not fwr['protocol'] and (fwr['source_port'] or
fwr['destination_port']):
raise f_exc.FirewallRuleWithPortWithoutProtocolInvalid()
src_port_min, src_port_max = self._get_min_max_ports_from_range(
fwr['source_port'])
dst_port_min, dst_port_max = self._get_min_max_ports_from_range(
fwr['destination_port'])
with context.session.begin(subtransactions=True):
fwr_db = FirewallRuleV2(
id=uuidutils.generate_uuid(),
tenant_id=fwr['tenant_id'],
name=fwr['name'],
description=fwr['description'],
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'],
shared=fwr['shared'])
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']
fwr_db = self._get_firewall_rule(context, id)
self._validate_fwr_protocol_parameters(fwr, fwr_db=fwr_db)
self._validate_fwr_src_dst_ip_version(fwr, fwr_db=fwr_db)
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):
protocol = fwr.get('protocol', fwr_db['protocol'])
if not protocol:
sport = fwr.get('source_port_range_min',
fwr_db['source_port_range_min'])
dport = fwr.get('destination_port_range_min',
fwr_db['destination_port_range_min'])
if sport or dport:
raise f_exc.FirewallRuleWithPortWithoutProtocolInvalid()
fwr_db.update(fwr)
# if the rule on a policy, fix audited flag
fwp_ids = self._get_policies_with_rule(context, id)
for fwp_id in fwp_ids:
fwp_db = self._get_firewall_policy(context, fwp_id)
fwp_db['audited'] = False
return self._make_firewall_rule_dict(fwr_db)
def delete_firewall_rule(self, context, id):
LOG.debug("delete_firewall_rule() called")
with context.session.begin(subtransactions=True):
fwr = self._get_firewall_rule(context, id)
# make sure rule is not associated with any policy
if self._get_policies_with_rule(context, id):
raise f_exc.FirewallRuleInUse(firewall_rule_id=id)
context.session.delete(fwr)
def insert_rule(self, context, id, rule_info):
LOG.debug("insert_rule() called")
self._validate_insert_remove_rule_request(id, rule_info)
firewall_rule_id = rule_info['firewall_rule_id']
# ensure rule is not already assigned to the policy
self._ensure_rule_not_already_associated(context, id, firewall_rule_id)
insert_before = True
ref_firewall_rule_id = None
if not firewall_rule_id:
raise f_exc.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)
fwp_db = self._get_firewall_policy(context, id)
self._check_firewall_rule_conflict(fwr_db, fwp_db)
if ref_firewall_rule_id:
# If reference_firewall_rule_id is set, the new rule
# 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.
fwpra_db = self._get_policy_rule_association(
context, id, ref_firewall_rule_id)
if insert_before:
position = fwpra_db.position
else:
position = fwpra_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, firewall_rule_id,
position, None)
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 f_exc.FirewallRuleNotFound(firewall_rule_id=None)
with context.session.begin(subtransactions=True):
self._get_firewall_rule(context, firewall_rule_id)
fwpra_db = self._get_policy_rule_association(context, id,
firewall_rule_id)
return self._process_rule_for_policy(context, id, firewall_rule_id,
None, fwpra_db)
def get_firewall_rule(self, context, id, fields=None):
LOG.debug("get_firewall_rule() called")
fwr = self._get_firewall_rule(context, id)
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, FirewallRuleV2,
self._make_firewall_rule_dict,
filters=filters, fields=fields)
def _validate_insert_remove_rule_request(self, id, rule_info):
if not rule_info or 'firewall_rule_id' not in rule_info:
raise f_exc.FirewallRuleInfoMissing()
def _delete_rules_in_policy(self, context, firewall_policy_id):
"""Delete the rules in the firewall policy."""
with context.session.begin(subtransactions=True):
fw_pol_rule_qry = context.session.query(
FirewallPolicyRuleAssociation)
fw_pol_rule_qry.filter_by(
firewall_policy_id=firewall_policy_id).delete()
return
def _get_rules_in_policy(self, context, fwpid):
"""Gets rules in a firewall policy"""
with context.session.begin(subtransactions=True):
fw_pol_rule_qry = context.session.query(
FirewallPolicyRuleAssociation).filter_by(
firewall_policy_id=fwpid)
fwp_rules = [entry.firewall_rule_id for entry in fw_pol_rule_qry]
return fwp_rules
def _get_policies_with_rule(self, context, fwrid):
"""Gets rules in a firewall policy"""
with context.session.begin(subtransactions=True):
fw_pol_rule_qry = context.session.query(
FirewallPolicyRuleAssociation).filter_by(
firewall_rule_id=fwrid)
fwps = [entry.firewall_policy_id for entry in fw_pol_rule_qry]
return fwps
def _set_rules_in_policy_rule_assoc(self, context, fwp_db, fwp):
# Pull the rules and add it to policy - rule association table
# Set the position (this can be used in the making the dict)
# might be good to track the last position
rule_id_list = fwp['firewall_rules']
if not rule_id_list:
return
position = 0
with context.session.begin(subtransactions=True):
for rule_id in rule_id_list:
fw_pol_rul_db = FirewallPolicyRuleAssociation(
firewall_policy_id=fwp_db['id'],
firewall_rule_id=rule_id,
position=position)
context.session.add(fw_pol_rul_db)
position += 1
def _check_rules_for_policy_is_valid(self, context, fwp, fwp_db,
rule_id_list, filters):
rules_in_fwr_db = self._get_collection_query(context, FirewallRuleV2,
filters=filters)
rules_dict = dict((fwr_db['id'], fwr_db) for fwr_db in rules_in_fwr_db)
for fwrule_id in rule_id_list:
if fwrule_id not in rules_dict:
# Bail as soon as we find an invalid rule.
raise f_exc.FirewallRuleNotFound(
firewall_rule_id=fwrule_id)
if 'shared' in fwp:
if fwp['shared'] and not rules_dict[fwrule_id]['shared']:
raise f_exc.FirewallRuleSharingConflict(
firewall_rule_id=fwrule_id,
firewall_policy_id=fwp_db['id'])
elif fwp_db['shared'] and not rules_dict[fwrule_id]['shared']:
raise f_exc.FirewallRuleSharingConflict(
firewall_rule_id=fwrule_id,
firewall_policy_id=fwp_db['id'])
else:
# the policy is not shared, the rule and policy should be in
# the same project if the rule is not shared.
if not rules_dict[fwrule_id]['shared']:
if (rules_dict[fwrule_id]['tenant_id'] != fwp_db[
'tenant_id']):
raise f_exc.FirewallRuleConflict(
firewall_rule_id=fwrule_id,
project_id=rules_dict[fwrule_id]['tenant_id'])
def _check_if_rules_shared_for_policy_shared(self, context, fwp_db, fwp):
if fwp['shared']:
rules_in_db = fwp_db.rule_associations
for entry in rules_in_db:
fwr_db = self._get_firewall_rule(context,
entry.firewall_rule_id)
if not fwp_db['shared']:
raise f_exc.FirewallPolicySharingConflict(
firewall_rule_id=fwr_db['id'],
firewall_policy_id=fwp_db['id'])
def _get_fwgs_with_policy(self, context, fwp_id):
with context.session.begin(subtransactions=True):
fwg_ing_pol_qry = context.session.query(
FirewallGroup).filter_by(
ingress_firewall_policy_id=fwp_id)
ing_fwg_ids = [entry.id for entry in fwg_ing_pol_qry]
fwg_eg_pol_qry = context.session.query(
FirewallGroup).filter_by(
egress_firewall_policy_id=fwp_id)
eg_fwg_ids = [entry.id for entry in fwg_eg_pol_qry]
return ing_fwg_ids, eg_fwg_ids
def _check_fwgs_associated_with_policy_in_same_project(self, context,
fwp_id,
fwp_tenant_id):
filters = {'ingress_firewall_rule_id': [fwp_id],
'ingress_firewall_rule_id': [fwp_id]}
with context.session.begin(subtransactions=True):
fwg_with_fwp_id_db = self._get_collection_query(
context,
FirewallGroup,
filters=filters)
for entry in fwg_with_fwp_id_db:
if entry.tenant_id != fwp_tenant_id:
raise f_exc.FirewallPolicyInUse(
firewall_policy_id=fwp_id)
def _set_rules_for_policy(self, context, firewall_policy_db, fwp):
rule_id_list = fwp['firewall_rules']
fwp_db = firewall_policy_db
with context.session.begin(subtransactions=True):
if not rule_id_list:
for rule_id in [rule_assoc.firewall_rule_id
for rule_assoc in fwp_db['rule_associations']]:
fwpra_db = self._get_policy_rule_association(
context, fwp_db['id'], rule_id)
fwp_db.rule_associations.remove(fwpra_db)
context.session.delete(fwpra_db)
fwp_db.rule_associations = []
return
# We will first check if the new list of rules is valid
filters = {'firewall_rule_id': [r_id for r_id in rule_id_list]}
# Run a validation on the Firewall Rules table
self._check_rules_for_policy_is_valid(context, fwp, fwp_db,
rule_id_list, filters)
# new rules are valid, lets delete the old association
self._delete_rules_in_policy(context, fwp_db['id'])
# and add in the new association
self._set_rules_in_policy_rule_assoc(context, fwp_db, fwp)
# we need care about the associations related with this policy
# and its rules only.
filters['firewall_policy_id'] = [fwp_db['id']]
rules_in_fpol_rul_db = self._get_collection_query(
context,
FirewallPolicyRuleAssociation,
filters=filters)
rules_dict = dict((fpol_rul_db['firewall_rule_id'], fpol_rul_db)
for fpol_rul_db in rules_in_fpol_rul_db)
fwp_db.rule_associations = []
for fwrule_id in rule_id_list:
fwp_db.rule_associations.append(rules_dict[fwrule_id])
fwp_db.rule_associations.reorder()
def create_firewall_policy(self, context, firewall_policy):
LOG.debug("create_firewall_policy() called")
fwp = firewall_policy['firewall_policy']
with context.session.begin(subtransactions=True):
fwp_db = FirewallPolicy(
id=uuidutils.generate_uuid(),
tenant_id=fwp['tenant_id'],
name=fwp['name'],
description=fwp['description'],
audited=fwp['audited'],
shared=fwp['shared'])
context.session.add(fwp_db)
self._set_rules_for_policy(context, fwp_db, fwp)
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 not fwp.get('shared', True):
# an update is setting shared to False, make sure associated
# firewall groups are in the same project.
self._check_fwgs_associated_with_policy_in_same_project(
context, id, fwp_db['tenant_id'])
if 'shared' in fwp and 'firewall_rules' not in fwp:
self._check_if_rules_shared_for_policy_shared(
context, fwp_db, fwp)
if 'firewall_rules' in fwp:
self._set_rules_for_policy(context, fwp_db, fwp)
del fwp['firewall_rules']
if 'audited' not in fwp:
fwp['audited'] = False
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_db = self._get_firewall_policy(context, id)
# check if policy in use
qry = context.session.query(FirewallGroup)
if qry.filter_by(ingress_firewall_policy_id=id).first():
raise f_exc.FirewallPolicyInUse(firewall_policy_id=id)
elif qry.filter_by(egress_firewall_policy_id=id).first():
raise f_exc.FirewallPolicyInUse(firewall_policy_id=id)
else:
# Policy is not being used, delete.
self._delete_rules_in_policy(context, id)
context.session.delete(fwp_db)
def get_firewall_policy(self, context, id, fields=None):
LOG.debug("get_firewall_policy() called")
fwp = self._get_firewall_policy(context, id)
return self._make_firewall_policy_dict(fwp, fields)
def get_firewall_policies(self, context, filters=None, fields=None):
LOG.debug("get_firewall_policies() called")
return self._get_collection(context, FirewallPolicy,
self._make_firewall_policy_dict,
filters=filters, fields=fields)
def _validate_fwg_parameters(self, context, fwg, fwg_tenant_id):
# On updates, all keys will not be present so check and validate.
if 'ingress_firewall_policy_id' in fwg:
fwp_id = fwg['ingress_firewall_policy_id']
if fwp_id is not None:
fwp = self._get_firewall_policy(context, fwp_id)
if fwg_tenant_id != fwp['tenant_id'] and not fwp['shared']:
raise f_exc.FirewallPolicyConflict(
firewall_policy_id=fwp_id)
if 'egress_firewall_policy_id' in fwg:
fwp_id = fwg['egress_firewall_policy_id']
if fwp_id is not None:
fwp = self._get_firewall_policy(context, fwp_id)
if fwg_tenant_id != fwp['tenant_id'] and not fwp['shared']:
raise f_exc.FirewallPolicyConflict(
firewall_policy_id=fwp_id)
return
def _set_ports_for_firewall_group(self, context, fwg_db, fwg):
port_id_list = fwg['ports']
if not port_id_list:
return
with context.session.begin(subtransactions=True):
for port_id in port_id_list:
fwg_port_db = FirewallGroupPortAssociation(
firewall_group_id=fwg_db['id'],
port_id=port_id)
context.session.add(fwg_port_db)
def _get_ports_in_firewall_group(self, context, firewall_group_id):
"""Get the Ports associated with the firewall group."""
with context.session.begin(subtransactions=True):
fw_group_port_qry = context.session.query(
FirewallGroupPortAssociation)
fw_group_port_rows = fw_group_port_qry.filter_by(
firewall_group_id=firewall_group_id)
fw_ports = [entry.port_id for entry in fw_group_port_rows]
return fw_ports
def _delete_ports_in_firewall_group(self, context, firewall_group_id):
"""Delete the Ports associated with the firewall group."""
with context.session.begin(subtransactions=True):
fw_group_port_qry = context.session.query(
FirewallGroupPortAssociation)
fw_group_port_qry.filter_by(
firewall_group_id=firewall_group_id).delete()
return
def _validate_if_firewall_group_on_ports(
self, context, port_ids, fwg_id=None):
"""Validate if ports are not associated with any firewall_group.
If any of the ports in the list is already associated with
a firewall_group, raise an exception else just return.
"""
fwg_port_qry = context.session.query(
FirewallGroupPortAssociation.port_id)
fwg_ports = fwg_port_qry.filter(
FirewallGroupPortAssociation.port_id.in_(port_ids),
FirewallGroupPortAssociation.firewall_group_id != fwg_id).all()
if fwg_ports:
port_ids = [entry.port_id for entry in fwg_ports]
raise f_exc.FirewallGroupPortInUse(port_ids=port_ids)
def create_firewall_group(self, context, firewall_group, status=None):
fwg = firewall_group['firewall_group']
if not status:
status = (nl_constants.CREATED if cfg.CONF.router_distributed
else nl_constants.PENDING_CREATE)
with context.session.begin(subtransactions=True):
self._validate_fwg_parameters(context, fwg, fwg['tenant_id'])
fwg_db = FirewallGroup(id=uuidutils.generate_uuid(),
tenant_id=fwg['tenant_id'],
name=fwg['name'],
description=fwg['description'],
status=status,
ingress_firewall_policy_id=fwg['ingress_firewall_policy_id'],
egress_firewall_policy_id=fwg['egress_firewall_policy_id'],
admin_state_up=fwg['admin_state_up'],
shared=fwg['shared'])
context.session.add(fwg_db)
self._set_ports_for_firewall_group(context, fwg_db, fwg)
return self._make_firewall_group_dict(fwg_db)
def update_firewall_group(self, context, id, firewall_group):
LOG.debug("update_firewall() called")
fwg = firewall_group['firewall_group']
with context.session.begin(subtransactions=True):
fwg_db = self.get_firewall_group(context, id)
self._validate_fwg_parameters(context, fwg, fwg_db['tenant_id'])
if 'ports' in fwg:
LOG.debug("Ports are updated in Firewall Group")
self._delete_ports_in_firewall_group(context, id)
self._set_ports_for_firewall_group(context, fwg_db, fwg)
del fwg['ports']
count = context.session.query(
FirewallGroup).filter_by(id=id).update(fwg)
if not count:
raise f_exc.FirewallGroupNotFound(firewall_id=id)
return self.get_firewall_group(context, id)
def update_firewall_group_status(self, context, id, status, not_in=None):
"""Conditionally update firewall_group status.
Status transition is performed only if firewall is not in the specified
states as defined by 'not_in' list.
"""
# filter in_ wants iterable objects, None isn't.
not_in = not_in or []
with context.session.begin(subtransactions=True):
return (context.session.query(FirewallGroup).
filter(FirewallGroup.id == id).
filter(~FirewallGroup.status.in_(not_in)).
update({'status': status}, synchronize_session=False))
def delete_firewall_group(self, context, id):
LOG.debug("delete_firewall() called")
with context.session.begin(subtransactions=True):
# Note: Plugin should ensure that it's okay to delete if the
# firewall is active
count = context.session.query(
FirewallGroup).filter_by(id=id).delete()
if not count:
raise f_exc.FirewallGroupNotFound(firewall_id=id)
def get_firewall_group(self, context, id, fields=None):
LOG.debug("get_firewall_group() called")
fw = self._get_firewall_group(context, id)
return self._make_firewall_group_dict(fw, fields)
def get_firewall_groups(self, context, filters=None, fields=None):
LOG.debug("get_firewall_groups() called")
return self._get_collection(context, FirewallGroup,
self._make_firewall_group_dict,
filters=filters, fields=fields)

View File

@ -1 +0,0 @@
Generic single-database configuration.

View File

@ -1,86 +0,0 @@
# Copyright 2014 OpenStack Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from logging import config as logging_config
from alembic import context
from neutron_lib.db import model_base
from oslo_config import cfg
from oslo_db.sqlalchemy import session
import sqlalchemy as sa
from sqlalchemy import event
MYSQL_ENGINE = None
FWAAS_VERSION_TABLE = 'alembic_version_fwaas'
config = context.config
neutron_config = config.neutron_config
logging_config.fileConfig(config.config_file_name)
target_metadata = model_base.BASEV2.metadata
def set_mysql_engine():
try:
mysql_engine = neutron_config.command.mysql_engine
except cfg.NoSuchOptError:
mysql_engine = None
global MYSQL_ENGINE
MYSQL_ENGINE = (mysql_engine or
model_base.BASEV2.__table_args__['mysql_engine'])
def run_migrations_offline():
set_mysql_engine()
kwargs = dict()
if neutron_config.database.connection:
kwargs['url'] = neutron_config.database.connection
else:
kwargs['dialect_name'] = neutron_config.database.engine
kwargs['version_table'] = FWAAS_VERSION_TABLE
context.configure(**kwargs)
with context.begin_transaction():
context.run_migrations()
@event.listens_for(sa.Table, 'after_parent_attach')
def set_storage_engine(target, parent):
if MYSQL_ENGINE:
target.kwargs['mysql_engine'] = MYSQL_ENGINE
def run_migrations_online():
set_mysql_engine()
engine = session.create_engine(neutron_config.database.connection)
connection = engine.connect()
context.configure(
connection=connection,
target_metadata=target_metadata,
version_table=FWAAS_VERSION_TABLE
)
try:
with context.begin_transaction():
context.run_migrations()
finally:
connection.close()
engine.dispose()
if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()

View File

@ -1,36 +0,0 @@
# Copyright ${create_date.year} <PUT YOUR NAME/COMPANY HERE>
#
# 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.
#
"""${message}
Revision ID: ${up_revision}
Revises: ${down_revision}
Create Date: ${create_date}
"""
# revision identifiers, used by Alembic.
revision = ${repr(up_revision)}
down_revision = ${repr(down_revision)}
% if branch_labels:
branch_labels = ${repr(branch_labels)}
%endif
from alembic import op
import sqlalchemy as sa
${imports if imports else ""}
def upgrade():
${upgrades if upgrades else "pass"}

View File

@ -1,36 +0,0 @@
# Copyright 2015 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.
#
"""add_index_tenant_id
Revision ID: 4202e3047e47
Revises: start_neutron_fwaas
Create Date: 2015-02-10 17:17:47.846764
"""
# revision identifiers, used by Alembic.
revision = '4202e3047e47'
down_revision = 'start_neutron_fwaas'
from alembic import op
TABLES = ['firewall_rules', 'firewalls', 'firewall_policies']
def upgrade():
for table in TABLES:
op.create_index(op.f('ix_%s_tenant_id' % table),
table, ['tenant_id'], unique=False)

View File

@ -1,62 +0,0 @@
# Copyright 2014 OpenStack Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
"""FWaaS router insertion
Revision ID: 540142f314f4
Revises: 4202e3047e47
Create Date: 2015-02-06 17:02:24.279337
"""
# revision identifiers, used by Alembic.
revision = '540142f314f4'
down_revision = '4202e3047e47'
from alembic import op
import sqlalchemy as sa
from sqlalchemy.engine import reflection
SQL_STATEMENT = (
"insert into firewall_router_associations "
"select "
"f.id as fw_id, r.id as router_id "
"from firewalls f, routers r "
"where "
"f.tenant_id=r.%s"
)
def upgrade():
op.create_table('firewall_router_associations',
sa.Column('fw_id', sa.String(length=36), nullable=False),
sa.Column('router_id', sa.String(length=36), nullable=False),
sa.ForeignKeyConstraint(['fw_id'], ['firewalls.id'],
ondelete='CASCADE'),
sa.ForeignKeyConstraint(['router_id'], ['routers.id'],
ondelete='CASCADE'),
sa.PrimaryKeyConstraint('fw_id', 'router_id'),
)
# Depending on when neutron-fwaas is installed with neutron, this script
# may be run before or after the neutron core tables have had their
# tenant_id columns renamed to project_id. Account for both scenarios.
bind = op.get_bind()
insp = reflection.Inspector.from_engine(bind)
columns = insp.get_columns('routers')
if 'tenant_id' in [c['name'] for c in columns]:
op.execute(SQL_STATEMENT % 'tenant_id')
else:
op.execute(SQL_STATEMENT % 'project_id')

View File

@ -1,46 +0,0 @@
# Copyright 2015 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.
#
"""cisco_csr_fwaas
Revision ID: 796c68dffbb
Revises: 540142f314f4
Create Date: 2015-02-02 13:11:55.184112
"""
# revision identifiers, used by Alembic.
revision = '796c68dffbb'
down_revision = '540142f314f4'
from alembic import op
import sqlalchemy as sa
def upgrade(active_plugins=None, options=None):
op.create_table('cisco_firewall_associations',
sa.Column('fw_id', sa.String(length=36), nullable=False),
sa.Column('port_id', sa.String(length=36), nullable=True),
sa.Column('direction', sa.String(length=16), nullable=True),
sa.Column('acl_id', sa.String(length=36), nullable=True),
sa.Column('router_id', sa.String(length=36), nullable=True),
sa.ForeignKeyConstraint(['fw_id'], ['firewalls.id'],
ondelete='CASCADE'),
sa.ForeignKeyConstraint(['port_id'], ['ports.id'],
ondelete='CASCADE'),
sa.PrimaryKeyConstraint('fw_id')
)

View File

@ -1,29 +0,0 @@
# 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.
#
"""kilo
Revision ID: kilo
Revises: 796c68dffbb
Create Date: 2015-04-16 00:00:00.000000
"""
# revision identifiers, used by Alembic.
revision = 'kilo'
down_revision = '796c68dffbb'
def upgrade():
"""A no-op migration for marking the Kilo release."""
pass

View File

@ -1,38 +0,0 @@
# Copyright 2015 Red Hat Inc.
#
# 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.
#
"""Initial Liberty no-op script.
Revision ID: 67c8e8d61d5
Revises: kilo
Create Date: 2015-07-28 22:18:13.330846
"""
from neutron.db import migration
from neutron_lib.db import constants
# revision identifiers, used by Alembic.
revision = '67c8e8d61d5'
down_revision = 'kilo'
branch_labels = (constants.CONTRACT_BRANCH,)
# milestone identifier, used by neutron-db-manage
neutron_milestone = [migration.LIBERTY]
def upgrade():
pass

View File

@ -1,47 +0,0 @@
# Copyright 2015 NEC Corporation. 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.
"""add reject rule
Revision ID: 4b47ea298795
Revises: c40fbb377ad
Create Date: 2015-04-15 04:19:57.324584
"""
import sqlalchemy as sa
from neutron.db import migration
# revision identifiers, used by Alembic.
revision = '4b47ea298795'
down_revision = 'c40fbb377ad'
# milestone identifier, used by neutron-db-manage
neutron_milestone = [migration.LIBERTY, migration.MITAKA]
new_action = sa.Enum('allow', 'deny', 'reject', name='firewallrules_action')
def upgrade():
# NOTE: postgresql have a builtin ENUM type, so just altering the
# column won't works
# https://bitbucket.org/zzzeek/alembic/issues/270/altering-enum-type
# alter_enum that was already invented for such case in neutron
# https://github.com/openstack/neutron/blob/master/neutron/db/migration/__init__.py
migration.alter_enum(
'firewall_rules', 'action', enum_type=new_action, nullable=True)

View File

@ -1,34 +0,0 @@
# Copyright 2015 Red Hat Inc.
#
# 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.
#
"""Initial Liberty no-op script.
Revision ID: c40fbb377ad
Revises: kilo
Create Date: 2015-07-28 22:18:13.321233
"""
from neutron_lib.db import constants
# revision identifiers, used by Alembic.
revision = 'c40fbb377ad'
down_revision = 'kilo'
branch_labels = (constants.EXPAND_BRANCH,)
def upgrade():
pass

View File

@ -1,49 +0,0 @@
#Copyright 2015 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.
#
"""fw_table_alter script to make <name> column case sensitive
Revision ID: 458aa42b14b
Revises: 67c8e8d61d5
Create Date: 2015-09-16 11:47:43.061649
"""
from alembic import op
from neutron.db import migration
# revision identifiers, used by Alembic.
revision = '458aa42b14b'
down_revision = '67c8e8d61d5'
# milestone identifier, used by neutron-db-manage
neutron_milestone = [migration.MITAKA]
FW_TAB_NAME = ['firewall_rules', 'firewall_policies', 'firewalls']
SQL_STATEMENT_UPDATE_CMD = (
"alter table %s "
"modify name varchar(255) "
"CHARACTER SET utf8 COLLATE utf8_bin"
)
def upgrade():
context = op.get_context()
if context.bind.dialect.name == 'mysql':
for table in FW_TAB_NAME:
op.execute(SQL_STATEMENT_UPDATE_CMD % table)

View File

@ -1,143 +0,0 @@
# Copyright 2016 <PUT YOUR NAME/COMPANY HERE>
#
# 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.
#
"""rename tenant to project
Revision ID: f83a0b2964d0
Revises: 458aa42b14b
Create Date: 2016-07-14 13:11:53.112622
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.engine import reflection
from neutron.db import migration
# revision identifiers, used by Alembic.
revision = 'f83a0b2964d0'
down_revision = '458aa42b14b'
# milestone identifier, used by neutron-db-manage
neutron_milestone = [migration.NEWTON]
_INSPECTOR = None
def get_inspector():
"""Reuse inspector"""
global _INSPECTOR
if _INSPECTOR:
return _INSPECTOR
else:
bind = op.get_bind()
_INSPECTOR = reflection.Inspector.from_engine(bind)
return _INSPECTOR
def get_tables():
"""
Returns hardcoded list of tables which have ``tenant_id`` column.
The list is hardcoded to match the state of the schema when this
upgrade script is run.
"""
tables = [
'firewalls',
'firewall_policies',
'firewall_rules',
]
return tables
def get_columns(table):
"""Returns list of columns for given table."""
inspector = get_inspector()
return inspector.get_columns(table)
def get_data():
"""Returns combined list of tuples: [(table, column)].
The list is built from tables with a tenant_id column.
"""
output = []
tables = get_tables()
for table in tables:
columns = get_columns(table)
for column in columns:
if column['name'] == 'tenant_id':
output.append((table, column))
return output
def alter_column(table, column):
old_name = 'tenant_id'
new_name = 'project_id'
op.alter_column(
table_name=table,
column_name=old_name,
new_column_name=new_name,
existing_type=column['type'],
existing_nullable=column['nullable']
)
def recreate_index(index, table_name):
old_name = index['name']
new_name = old_name.replace('tenant', 'project')
op.drop_index(op.f(old_name), table_name)
op.create_index(new_name, table_name, ['project_id'])
def upgrade():
"""Code reused from
Change-Id: I87a8ef342ccea004731ba0192b23a8e79bc382dc
"""
inspector = get_inspector()
data = get_data()
for table, column in data:
alter_column(table, column)
indexes = inspector.get_indexes(table)
for index in indexes:
if 'tenant_id' in index['name']:
recreate_index(index, table)
def contract_creation_exceptions():
"""Special migration for the blueprint to support Keystone V3.
We drop all tenant_id columns and create project_id columns instead.
"""
return {
sa.Column: ['.'.join([table, 'project_id']) for table in get_tables()],
sa.Index: get_tables()
}

View File

@ -1,113 +0,0 @@
# Copyright 2016 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.
#
"""neutron-fwaas v2.0
Revision ID: d6a12e637e28
Revises: 4b47ea298795
Create Date: 2016-06-08 19:57:13.848855
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
from neutron.db import migration
# revision identifiers, used by Alembic.
revision = 'd6a12e637e28'
down_revision = '4b47ea298795'
# milestone identifier, used by neutron-db-manage
neutron_milestone = [migration.NEWTON]
def get_enum():
engine = op.get_bind().engine
# In PostgreSQL types created separately, so if type was already created in
# 4b47ea298795_add_reject_rule it should be created one time.
# Use parameter create_type=False for that.
if engine.name == 'postgresql':
return postgresql.ENUM('allow', 'deny', 'reject',
name='firewallrules_action',
create_type=False)
else:
return sa.Enum('allow', 'deny', 'reject',
name='firewallrules_action')
def upgrade():
op.create_table(
'firewall_policies_v2',
sa.Column('id', sa.String(length=36), primary_key=True),
sa.Column('name', sa.String(length=255)),
sa.Column('description', sa.String(length=1024)),
sa.Column('project_id', sa.String(length=255), index=True),
sa.Column('audited', sa.Boolean),
sa.Column('public', sa.Boolean),
sa.Column('rule_count', sa.Integer))
op.create_table(
'firewall_rules_v2',
sa.Column('id', sa.String(length=36), primary_key=True),
sa.Column('name', sa.String(length=255)),
sa.Column('description', sa.String(length=1024)),
sa.Column('project_id', sa.String(length=255), index=True),
sa.Column('protocol', sa.String(length=40)),
sa.Column('ip_version', sa.Integer),
sa.Column('source_ip_address', sa.String(length=46)),
sa.Column('destination_ip_address', sa.String(length=46)),
sa.Column('source_port_range_min', sa.Integer),
sa.Column('source_port_range_max', sa.Integer),
sa.Column('destination_port_range_min', sa.Integer),
sa.Column('destination_port_range_max', sa.Integer),
sa.Column('action', get_enum()),
sa.Column('public', sa.Boolean),
sa.Column('enabled', sa.Boolean))
op.create_table(
'firewall_groups_v2',
sa.Column('id', sa.String(length=36), primary_key=True),
sa.Column('name', sa.String(length=255)),
sa.Column('description', sa.String(length=1024)),
sa.Column('project_id', sa.String(length=255), index=True),
sa.Column('status', sa.String(length=16)),
sa.Column('admin_state_up', sa.Boolean),
sa.Column('public', sa.Boolean),
sa.Column('egress_firewall_policy_id', sa.String(length=36),
sa.ForeignKey('firewall_policies_v2.id')),
sa.Column('ingress_firewall_policy_id', sa.String(length=36),
sa.ForeignKey('firewall_policies_v2.id')))
op.create_table(
'firewall_group_port_associations_v2',
sa.Column('firewall_group_id', sa.String(length=36),
sa.ForeignKey('firewall_groups_v2.id', ondelete='CASCADE')),
sa.Column('port_id', sa.String(length=36),
sa.ForeignKey('ports.id', ondelete='CASCADE'))
)
op.create_table(
'firewall_policy_rule_associations_v2',
sa.Column('firewall_policy_id', sa.String(length=36),
sa.ForeignKey('firewall_policies_v2.id', ondelete='CASCADE'),
nullable=False, primary_key=True),
sa.Column('firewall_rule_id', sa.String(length=36),
sa.ForeignKey('firewall_rules_v2.id', ondelete='CASCADE'),
nullable=False, primary_key=True),
sa.Column('position', sa.Integer))

View File

@ -1,37 +0,0 @@
# 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.
#
"""change shared attribute for firewall resource
Revision ID: fd38cd995cc0
Revises: f83a0b2964d0
Create Date: 2017-03-31 14:22:21.063392
"""
# revision identifiers, used by Alembic.
revision = 'fd38cd995cc0'
down_revision = 'f83a0b2964d0'
depends_on = ('d6a12e637e28',)
from alembic import op
import sqlalchemy as sa
def upgrade():
op.alter_column('firewall_rules_v2', 'public', new_column_name='shared',
existing_type=sa.Boolean)
op.alter_column('firewall_groups_v2', 'public', new_column_name='shared',
existing_type=sa.Boolean)
op.alter_column('firewall_policies_v2', 'public', new_column_name='shared',
existing_type=sa.Boolean)

View File

@ -1,30 +0,0 @@
# Copyright 2014 OpenStack Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
"""start neutron-fwaas chain
Revision ID: start_neutron_fwaas
Revises: None
Create Date: 2014-12-09 18:42:08.262632
"""
# revision identifiers, used by Alembic.
revision = 'start_neutron_fwaas'
down_revision = None
def upgrade():
pass

View File

@ -1,21 +0,0 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from neutron_lib.db import model_base
from neutron_fwaas.db.firewall import firewall_db # noqa
from neutron_fwaas.db.firewall import firewall_router_insertion_db # noqa
from neutron_fwaas.db.firewall.v2 import firewall_db_v2 # noqa
def get_metadata():
return model_base.BASEV2.metadata

View File

@ -1,426 +0,0 @@
# 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.
import abc
from debtcollector import moves
from neutron.api.v2 import resource_helper
from neutron_lib.api import converters
from neutron_lib.api import extensions
from neutron_lib.api import validators
from neutron_lib import constants
from neutron_lib.db import constants as db_const
from neutron_lib.exceptions import firewall_v1 as f_exc
from neutron_lib.services import base as service_base
from oslo_config import cfg
from oslo_log import log as logging
import six
from neutron_fwaas._i18n import _
from neutron_fwaas.common import fwaas_constants
LOG = logging.getLogger(__name__)
FirewallNotFound = moves.moved_class(
f_exc.FirewallNotFound, 'FirewallNotFound', __name__)
FirewallInUse = moves.moved_class(
f_exc.FirewallInUse, 'FirewallInUse', __name__)
FirewallPolicyNotFound = moves.moved_class(
f_exc.FirewallPolicyNotFound, 'FirewallPolicyNotFound', __name__)
FirewallPolicyInUse = moves.moved_class(
f_exc.FirewallPolicyInUse, 'FirewallPolicyInUse', __name__)
FirewallPolicyConflict = moves.moved_class(
f_exc.FirewallPolicyConflict, 'FirewallPolicyConflict', __name__)
FirewallRuleSharingConflict = moves.moved_class(
f_exc.FirewallRuleSharingConflict, 'FirewallRuleSharingConflict', __name__)
FirewallPolicySharingConflict = moves.moved_class(
f_exc.FirewallPolicySharingConflict, 'FirewallPolicySharingConflict',
__name__)
FirewallRuleNotFound = moves.moved_class(
f_exc.FirewallRuleNotFound, 'FirewallRuleNotFound', __name__)
FirewallRuleInUse = moves.moved_class(
f_exc.FirewallRuleInUse, 'FirewallRuleInUse', __name__)
FirewallRuleNotAssociatedWithPolicy = moves.moved_class(
f_exc.FirewallRuleNotAssociatedWithPolicy,
'FirewallRuleNotAssociatedWithPolicy',
__name__)
FirewallRuleInvalidProtocol = moves.moved_class(
f_exc.FirewallRuleInvalidProtocol, 'FirewallRuleInvalidProtocol',
__name__)
FirewallRuleInvalidAction = moves.moved_class(
f_exc.FirewallRuleInvalidAction, 'FirewallRuleInvalidAction', __name__)
FirewallRuleInvalidICMPParameter = moves.moved_class(
f_exc.FirewallRuleInvalidICMPParameter,
'FirewallRuleInvalidICMPParameter', __name__)
FirewallRuleWithPortWithoutProtocolInvalid = moves.moved_class(
f_exc.FirewallRuleWithPortWithoutProtocolInvalid,
'FirewallRuleWithPortWithoutProtocolInvalid', __name__)
FirewallRuleInvalidPortValue = moves.moved_class(
f_exc.FirewallRuleInvalidPortValue, 'FirewallRuleInvalidPortValue',
__name__)
FirewallRuleInfoMissing = moves.moved_class(
f_exc.FirewallRuleInfoMissing, 'FirewallRuleInfoMissing', __name__)
FirewallIpAddressConflict = moves.moved_class(
f_exc.FirewallIpAddressConflict, 'FirewallIpAddressConflict', __name__)
FirewallInternalDriverError = moves.moved_class(
f_exc.FirewallInternalDriverError, 'FirewallInternalDriverError', __name__)
FirewallRuleConflict = moves.moved_class(
f_exc.FirewallRuleConflict, 'FirewallRuleConflict', __name__)
# Firewall rule action
FWAAS_ALLOW = "allow"
FWAAS_DENY = "deny"
FWAAS_REJECT = "reject"
# Firewall resource path prefix
FIREWALL_PREFIX = "/fw"
fw_valid_protocol_values = [None, constants.PROTO_NAME_TCP,
constants.PROTO_NAME_UDP,
constants.PROTO_NAME_ICMP]
fw_valid_action_values = [FWAAS_ALLOW, FWAAS_DENY, FWAAS_REJECT]
def convert_protocol(value):
if value is None:
return
if (isinstance(value, six.integer_types) or
(isinstance(value, six.string_types) and value.isdigit())):
val = int(value)
if 0 <= val <= 255:
return val
else:
raise f_exc.FirewallRuleInvalidProtocol(
protocol=value, values=fw_valid_protocol_values)
elif isinstance(value, six.string_types):
if value.lower() in fw_valid_protocol_values:
return value.lower()
raise f_exc.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 = validators.validate_ip_address(data, valid_values)
if not msg_ip:
return
msg_subnet = validators.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}
validators.validators['type:port_range'] = _validate_port_range
validators.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': db_const.NAME_FIELD_SIZE},
'is_visible': True, 'default': ''},
'description': {'allow_post': True, 'allow_put': True,
'validate': {'type:string':
db_const.DESCRIPTION_FIELD_SIZE},
'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': converters.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': converters.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, 'is_visible': True,
'convert_to': converters.convert_to_boolean},
},
'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': db_const.NAME_FIELD_SIZE},
'is_visible': True, 'default': ''},
'description': {'allow_post': True, 'allow_put': True,
'validate': {'type:string':
db_const.DESCRIPTION_FIELD_SIZE},
'is_visible': True, 'default': ''},
'shared': {'allow_post': True, 'allow_put': True,
'default': False, 'enforce_policy': True,
'convert_to': converters.convert_to_boolean,
'is_visible': True, 'required_by_policy': True},
'firewall_rules': {'allow_post': True, 'allow_put': True,
'validate': {'type:uuid_list': None},
'convert_to': converters.convert_none_to_empty_list,
'default': None, 'is_visible': True},
'audited': {'allow_post': True, 'allow_put': True,
'default': False, 'is_visible': True,
'convert_to': converters.convert_to_boolean},
},
'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': db_const.NAME_FIELD_SIZE},
'is_visible': True, 'default': ''},
'description': {'allow_post': True, 'allow_put': True,
'validate': {'type:string':
db_const.DESCRIPTION_FIELD_SIZE},
'is_visible': True, 'default': ''},
'admin_state_up': {'allow_post': True, 'allow_put': True,
'default': True, 'is_visible': True,
'convert_to': converters.convert_to_boolean},
'status': {'allow_post': False, 'allow_put': False,
'is_visible': True},
'shared': {'allow_post': True, 'allow_put': True,
'default': False, 'enforce_policy': True,
'convert_to': converters.convert_to_boolean,
'is_visible': False, 'required_by_policy': True},
'firewall_policy_id': {'allow_post': True, 'allow_put': True,
'validate': {'type:uuid_or_none': None},
'is_visible': True},
},
}
# A tenant may have a unique firewall and policy for each router
# when router insertion is used.
# Set default quotas to align with default l3 quota_router of 10
# though keep as separately controllable.
firewall_quota_opts = [
cfg.IntOpt('quota_firewall',
default=10,
help=_('Number of firewalls allowed per tenant. '
'A negative value means unlimited.')),
cfg.IntOpt('quota_firewall_policy',
default=10,
help=_('Number of firewall policies allowed per tenant. '
'A negative value means unlimited.')),
cfg.IntOpt('quota_firewall_rule',
default=100,
help=_('Number of firewall rules allowed per tenant. '
'A negative value means 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_updated(cls):
return "2013-02-25T10:00:00-00:00"
@classmethod
def get_resources(cls):
special_mappings = {'firewall_policies': 'firewall_policy'}
plural_mappings = resource_helper.build_plural_mappings(
special_mappings, RESOURCE_ATTRIBUTE_MAP)
action_map = {'firewall_policy': {'insert_rule': 'PUT',
'remove_rule': 'PUT'}}
return resource_helper.build_resource_info(plural_mappings,
RESOURCE_ATTRIBUTE_MAP,
fwaas_constants.FIREWALL,
action_map=action_map,
register_quota=True)
@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 {}
@six.add_metaclass(abc.ABCMeta)
class FirewallPluginBase(service_base.ServicePluginBase):
def get_plugin_name(self):
return fwaas_constants.FIREWALL
def get_plugin_type(self):
return fwaas_constants.FIREWALL
def get_plugin_description(self):
return 'Firewall service plugin'
@abc.abstractmethod
def get_firewalls(self, context, filters=None, fields=None):
pass
@abc.abstractmethod
def get_firewall(self, context, id, fields=None):
pass
@abc.abstractmethod
def create_firewall(self, context, firewall):
pass
@abc.abstractmethod
def update_firewall(self, context, id, firewall):
pass
@abc.abstractmethod
def delete_firewall(self, context, id):
pass
@abc.abstractmethod
def get_firewall_rules(self, context, filters=None, fields=None):
pass
@abc.abstractmethod
def get_firewall_rule(self, context, id, fields=None):
pass
@abc.abstractmethod
def create_firewall_rule(self, context, firewall_rule):
pass
@abc.abstractmethod
def update_firewall_rule(self, context, id, firewall_rule):
pass
@abc.abstractmethod
def delete_firewall_rule(self, context, id):
pass
@abc.abstractmethod
def get_firewall_policy(self, context, id, fields=None):
pass
@abc.abstractmethod
def get_firewall_policies(self, context, filters=None, fields=None):
pass
@abc.abstractmethod
def create_firewall_policy(self, context, firewall_policy):
pass
@abc.abstractmethod
def update_firewall_policy(self, context, id, firewall_policy):
pass
@abc.abstractmethod
def delete_firewall_policy(self, context, id):
pass
@abc.abstractmethod
def insert_rule(self, context, id, rule_info):
pass
@abc.abstractmethod
def remove_rule(self, context, id, rule_info):
pass

View File

@ -1,352 +0,0 @@
# Copyright (c) 2016 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import abc
from debtcollector import moves
from neutron.api.v2 import resource_helper
from neutron_lib.api import converters
from neutron_lib.api import extensions
from neutron_lib.db import constants as nl_db_constants
from neutron_lib.exceptions import firewall_v2 as f_exc
from neutron_lib.services import base as service_base
import six
# Import firewall v1 API to get the validators
# TODO(shpadubi): pull the validators out of fwaas v1 into a separate file
from neutron_fwaas.extensions import firewall as fwaas_v1
FIREWALL_PREFIX = '/fwaas'
FIREWALL_CONST = 'FIREWALL_V2'
FirewallGroupNotFound = moves.moved_class(
f_exc.FirewallGroupNotFound, 'FirewallGroupNotFound', __name__)
FirewallGroupInUse = moves.moved_class(
f_exc.FirewallGroupInUse, 'FirewallGroupInUse', __name__)
FirewallGroupInPendingState = moves.moved_class(
f_exc.FirewallGroupInPendingState, 'FirewallGroupInPendingState', __name__)
FirewallGroupPortInvalid = moves.moved_class(
f_exc.FirewallGroupPortInvalid, 'FirewallGroupPortInvalid', __name__)
FirewallGroupPortInvalidProject = moves.moved_class(
f_exc.FirewallGroupPortInvalidProject, 'FirewallGroupPortInvalidProject',
__name__)
FirewallGroupPortInUse = moves.moved_class(
f_exc.FirewallGroupPortInUse, 'FirewallGroupPortInUse', __name__)
FirewallPolicyNotFound = moves.moved_class(
f_exc.FirewallPolicyNotFound, 'FirewallPolicyNotFound', __name__)
FirewallPolicyInUse = moves.moved_class(
f_exc.FirewallPolicyInUse, 'FirewallPolicyInUse', __name__)
FirewallPolicyConflict = moves.moved_class(
f_exc.FirewallPolicyConflict, 'FirewallPolicyConflict', __name__)
FirewallRuleSharingConflict = moves.moved_class(
f_exc.FirewallRuleSharingConflict, 'FirewallRuleSharingConflict',
__name__)
FirewallPolicySharingConflict = moves.moved_class(
f_exc.FirewallPolicySharingConflict, 'FirewallPolicySharingConflict',
__name__)
FirewallRuleNotFound = moves.moved_class(
f_exc.FirewallRuleNotFound, 'FirewallRuleNotFound', __name__)
FirewallRuleInUse = moves.moved_class(
f_exc.FirewallRuleInUse, 'FirewallRuleInUse', __name__)
FirewallRuleNotAssociatedWithPolicy = moves.moved_class(
f_exc.FirewallRuleNotAssociatedWithPolicy,
'FirewallRuleNotAssociatedWithPolicy',
__name__)
FirewallRuleInvalidProtocol = moves.moved_class(
f_exc.FirewallRuleInvalidProtocol, 'FirewallRuleInvalidProtocol',
__name__)
FirewallRuleInvalidAction = moves.moved_class(
f_exc.FirewallRuleInvalidAction, 'FirewallRuleInvalidAction',
__name__)
FirewallRuleInvalidICMPParameter = moves.moved_class(
f_exc.FirewallRuleInvalidICMPParameter,
'FirewallRuleInvalidICMPParameter', __name__)
FirewallRuleWithPortWithoutProtocolInvalid = moves.moved_class(
f_exc.FirewallRuleWithPortWithoutProtocolInvalid,
'FirewallRuleWithPortWithoutProtocolInvalid', __name__)
FirewallRuleInvalidPortValue = moves.moved_class(
f_exc.FirewallRuleInvalidPortValue, 'FirewallRuleInvalidPortValue',
__name__)
FirewallRuleInfoMissing = moves.moved_class(
f_exc.FirewallRuleInfoMissing, 'FirewallRuleInfoMissing', __name__)
FirewallIpAddressConflict = moves.moved_class(
f_exc.FirewallIpAddressConflict, 'FirewallIpAddressConflict', __name__)
FirewallInternalDriverError = moves.moved_class(
f_exc.FirewallInternalDriverError, 'FirewallInternalDriverError', __name__)
FirewallRuleConflict = moves.moved_class(
f_exc.FirewallRuleConflict, 'FirewallRuleConflict', __name__)
FirewallRuleAlreadyAssociated = moves.moved_class(
f_exc.FirewallRuleAlreadyAssociated, 'FirewallRuleAlreadyAssociated',
__name__)
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,
'validate': {'type:string':
nl_db_constants.UUID_FIELD_SIZE},
'is_visible': True},
'name': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': nl_db_constants.NAME_FIELD_SIZE},
'is_visible': True, 'default': ''},
'description': {'allow_post': True, 'allow_put': True,
'validate': {'type:string':
nl_db_constants.DESCRIPTION_FIELD_SIZE},
'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, 'is_visible': True,
'convert_to': converters.convert_to_boolean,
'required_by_policy': True, 'enforce_policy': True},
'protocol': {'allow_post': True, 'allow_put': True,
'is_visible': True, 'default': None,
'convert_to': fwaas_v1.convert_protocol,
'validate': {'type:values':
fwaas_v1.fw_valid_protocol_values}},
'ip_version': {'allow_post': True, 'allow_put': True,
'default': 4, 'convert_to': converters.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': fwaas_v1.convert_port_to_string,
'default': None, 'is_visible': True},
'destination_port': {'allow_post': True, 'allow_put': True,
'validate': {'type:port_range': None},
'convert_to': fwaas_v1.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': fwaas_v1.convert_action_to_case_insensitive,
'validate': {'type:values':
fwaas_v1.fw_valid_action_values},
'is_visible': True, 'default': 'deny'},
'enabled': {'allow_post': True, 'allow_put': True,
'convert_to': converters.convert_to_boolean,
'default': True, 'is_visible': True},
},
'firewall_groups': {
'id': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None},
'is_visible': True,
'primary_key': True},
'name': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': nl_db_constants.NAME_FIELD_SIZE},
'is_visible': True, 'default': ''},
'description': {'allow_post': True, 'allow_put': True,
'validate': {'type:string':
nl_db_constants.DESCRIPTION_FIELD_SIZE},
'is_visible': True, 'default': ''},
'admin_state_up': {'allow_post': True, 'allow_put': True,
'default': True, 'is_visible': True,
'convert_to': converters.convert_to_boolean},
'status': {'allow_post': False, 'allow_put': False,
'is_visible': True},
'shared': {'allow_post': True, 'allow_put': True, 'default': False,
'convert_to': converters.convert_to_boolean,
'is_visible': True, 'required_by_policy': True,
'enforce_policy': True},
'ports': {'allow_post': True, 'allow_put': True,
'validate': {'type:uuid_list': None},
'convert_to': converters.convert_none_to_empty_list,
'default': None, 'is_visible': True},
'tenant_id': {'allow_post': True, 'allow_put': False,
'required_by_policy': True,
'validate': {'type:string':
nl_db_constants.UUID_FIELD_SIZE},
'is_visible': True},
'ingress_firewall_policy_id': {'allow_post': True,
'allow_put': True,
'validate': {'type:uuid_or_none':
None},
'default': None, 'is_visible': True},
'egress_firewall_policy_id': {'allow_post': True,
'allow_put': True,
'validate': {'type:uuid_or_none':
None},
'default': None, '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,
'validate': {'type:string':
nl_db_constants.UUID_FIELD_SIZE},
'is_visible': True},
'name': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': nl_db_constants.NAME_FIELD_SIZE},
'is_visible': True, 'default': ''},
'description': {'allow_post': True, 'allow_put': True,
'validate': {'type:string':
nl_db_constants.DESCRIPTION_FIELD_SIZE},
'is_visible': True, 'default': ''},
'shared': {'allow_post': True, 'allow_put': True, 'default': False,
'convert_to': converters.convert_to_boolean,
'is_visible': True, 'required_by_policy': True,
'enforce_policy': True},
'firewall_rules': {'allow_post': True, 'allow_put': True,
'validate': {'type:uuid_list': None},
'convert_to': converters.convert_none_to_empty_list,
'default': None, 'is_visible': True},
'audited': {'allow_post': True, 'allow_put': True, 'default': False,
'convert_to': converters.convert_to_boolean,
'is_visible': True},
},
}
class Firewall_v2(extensions.ExtensionDescriptor):
@classmethod
def get_name(cls):
return "Firewall service v2"
@classmethod
def get_alias(cls):
return "fwaas_v2"
@classmethod
def get_description(cls):
return "Extension for Firewall service v2"
@classmethod
def get_updated(cls):
return "2016-08-16T00:00:00-00:00"
@classmethod
def get_resources(cls):
special_mappings = {'firewall_policies': 'firewall_policy'}
plural_mappings = resource_helper.build_plural_mappings(
special_mappings, RESOURCE_ATTRIBUTE_MAP)
action_map = {'firewall_policy': {'insert_rule': 'PUT',
'remove_rule': 'PUT'}}
return resource_helper.build_resource_info(plural_mappings,
RESOURCE_ATTRIBUTE_MAP,
FIREWALL_CONST,
action_map=action_map)
@classmethod
def get_plugin_interface(cls):
return Firewallv2PluginBase
def update_attributes_map(self, attributes):
super(Firewall_v2, 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 {}
@six.add_metaclass(abc.ABCMeta)
class Firewallv2PluginBase(service_base.ServicePluginBase):
def get_plugin_name(self):
return FIREWALL_CONST
def get_plugin_type(self):
return FIREWALL_CONST
def get_plugin_description(self):
return 'Firewall Service v2 Plugin'
@abc.abstractmethod
def create_firewall_group(self, context, firewall_group):
pass
@abc.abstractmethod
def delete_firewall_group(self, context, id):
pass
@abc.abstractmethod
def get_firewall_group(self, context, id):
pass
@abc.abstractmethod
def get_firewall_groups(self, context, filters=None, fields=None):
pass
@abc.abstractmethod
def update_firewall_group(self, context, id, firewall_group):
pass
@abc.abstractmethod
def get_firewall_rules(self, context, filters=None, fields=None):
pass
@abc.abstractmethod
def get_firewall_rule(self, context, id, fields=None):
pass
@abc.abstractmethod
def create_firewall_rule(self, context, firewall_rule):
pass
@abc.abstractmethod
def update_firewall_rule(self, context, id, firewall_rule):
pass
@abc.abstractmethod
def delete_firewall_rule(self, context, id):
pass
@abc.abstractmethod
def get_firewall_policy(self, context, id, fields=None):
pass
@abc.abstractmethod
def get_firewall_policies(self, context, filters=None, fields=None):
pass
@abc.abstractmethod
def create_firewall_policy(self, context, firewall_policy):
pass
@abc.abstractmethod
def update_firewall_policy(self, context, id, firewall_policy):
pass
@abc.abstractmethod
def delete_firewall_policy(self, context, id):
pass
@abc.abstractmethod
def insert_rule(self, context, id, rule_info):
pass
@abc.abstractmethod
def remove_rule(self, context, id, rule_info):
pass

View File

@ -1,68 +0,0 @@
# Copyright 2015 Cisco Systems Inc.
# All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from neutron_lib.api import extensions
from neutron_lib import constants
EXTENDED_ATTRIBUTES_2_0 = {
'firewalls': {
'router_ids': {'allow_post': True, 'allow_put': True,
'validate': {'type:uuid_list': None},
'is_visible': True, 'default': constants.ATTR_NOT_SPECIFIED},
}
}
class Firewallrouterinsertion(extensions.ExtensionDescriptor):
"""Extension class supporting Firewall and Router(s) association.
The extension enables providing an option to specify router-ids of
routers where the firewall is to be installed. This is supported in
a manner so that the older version of the API continues to be supported.
On a CREATE, if the router_ids option is not specified then the firewall
is installed on all routers on the tenant. If the router-ids option is
provided with a list of routers then the firewall is installed on the
specified routers. If the router-ids option is provided with an empty
list then the firewall is created but put in an INACTIVE state to reflect
that no routers are associated. This firewall can be updated with a list
of routers which will then drive the state to ACTIVE after the agent
installs and acks back. UPDATE also supports the option in a similar
manner. If the router_ids option is not provided, then there is no change
to the existing association with the routers. When the router_is option is
provided with a list of routers or an empty list - this drives the new
set of routers that the firewall is associated with.
"""
@classmethod
def get_name(cls):
return "Firewall Router insertion"
@classmethod
def get_alias(cls):
return "fwaasrouterinsertion"
@classmethod
def get_description(cls):
return "Firewall Router insertion on specified set of routers"
@classmethod
def get_updated(cls):
return "2015-01-27T10:00:00-00:00"
def get_extended_resources(self, version):
if version == "2.0":
return EXTENDED_ATTRIBUTES_2_0
else:
return {}

View File

@ -1,27 +0,0 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import neutron_fwaas.services.firewall.agents.firewall_agent_api
def list_agent_opts():
return [
('fwaas',
neutron_fwaas.services.firewall.agents.firewall_agent_api.FWaaSOpts)
]
def list_opts():
return [
('quotas',
neutron_fwaas.extensions.firewall.firewall_quota_opts)
]

View File

@ -1,29 +0,0 @@
# Copyright (c) 2017 Thales Services SAS
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_privsep import capabilities as c
from oslo_privsep import priv_context
# It is expected that most (if not all) neutron-fwaas operations can be
# executed with these privileges.
default = priv_context.PrivContext(
__name__,
cfg_section='privsep',
pypath=__name__ + '.default',
# TODO(gus): CAP_SYS_ADMIN is required (only?) for manipulating
# network namespaces. SYS_ADMIN is a lot of scary powers, so
# consider breaking this out into a separate minimal context.
capabilities=[c.CAP_SYS_ADMIN, c.CAP_NET_ADMIN],
)

View File

@ -1,82 +0,0 @@
# Copyright (c) 2017 Fujitsu Limited
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# Some parts are based on python-conntrack:
# Copyright (c) 2009-2011,2015 Andrew Grigorev <andrew@ei-grad.ru>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
import socket
CONNTRACK = 0
NFCT_O_PLAIN = 0
NFCT_OF_TIME_BIT = 1
NFCT_OF_TIME = 1 << NFCT_OF_TIME_BIT
NFCT_Q_DESTROY = 2
NFCT_Q_FLUSH = 4
NFCT_Q_DUMP = 5
NFCT_T_DESTROY_BIT = 2
NFCT_T_DESTROY = 1 << NFCT_T_DESTROY_BIT
ATTR_IPV4_SRC = 0
ATTR_IPV4_DST = 1
ATTR_IPV6_SRC = 4
ATTR_IPV6_DST = 5
ATTR_PORT_SRC = 8
ATTR_PORT_DST = 9
ATTR_ICMP_TYPE = 12
ATTR_ICMP_CODE = 13
ATTR_ICMP_ID = 14
ATTR_L3PROTO = 15
ATTR_L4PROTO = 17
NFCT_T_NEW_BIT = 0
NFCT_T_NEW = 1 << NFCT_T_NEW_BIT
NFCT_T_UPDATE_BIT = 1
NFCT_T_UPDATE = 1 << NFCT_T_UPDATE_BIT
NFCT_T_DESTROY_BIT = 2
NFCT_T_DESTROY = 1 << NFCT_T_DESTROY_BIT
NFCT_T_ALL = NFCT_T_NEW | NFCT_T_UPDATE | NFCT_T_DESTROY
NFCT_CB_CONTINUE = 1
NFCT_CB_FAILURE = -1
NFNL_SUBSYS_CTNETLINK = 0
BUFFER = 1024
IPVERSION_SOCKET = {4: socket.AF_INET, 6: socket.AF_INET6}

View File

@ -1,268 +0,0 @@
# Copyright (c) 2017 Fujitsu Limited
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# Some parts are based on python-conntrack:
# Copyright (c) 2009-2011,2015 Andrew Grigorev <andrew@ei-grad.ru>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
import ctypes
from ctypes import util
from oslo_log import log as logging
from neutron_lib import constants
from neutron_fwaas._i18n import _, _LW
from neutron_fwaas import privileged
from neutron_fwaas.privileged import netlink_constants as nl_constants
from neutron_fwaas.privileged import utils as fwaas_utils
LOG = logging.getLogger(__name__)
nfct = ctypes.CDLL(util.find_library('netfilter_conntrack'))
libc = ctypes.CDLL(util.find_library('libc.so.6'))
IP_VERSIONS = [constants.IP_VERSION_4, constants.IP_VERSION_6]
DATA_CALLBACK = None
ATTR_POSITIONS = {
'icmp': [('type', 6), ('code', 7), ('src', 4), ('dst', 5), ('id', 8)],
'tcp': [('sport', 7), ('dport', 8), ('src', 5), ('dst', 6)],
'udp': [('sport', 6), ('dport', 7), ('src', 4), ('dst', 5)]
}
TARGET = {'src': {4: nl_constants.ATTR_IPV4_SRC,
6: nl_constants.ATTR_IPV6_SRC},
'dst': {4: nl_constants.ATTR_IPV4_DST,
6: nl_constants.ATTR_IPV6_DST},
'ipversion': {4: nl_constants.ATTR_L3PROTO,
6: nl_constants.ATTR_L3PROTO},
'protocol': {4: nl_constants.ATTR_L4PROTO,
6: nl_constants.ATTR_L4PROTO},
'code': {4: nl_constants.ATTR_ICMP_CODE,
6: nl_constants.ATTR_ICMP_CODE},
'type': {4: nl_constants.ATTR_ICMP_TYPE,
6: nl_constants.ATTR_ICMP_TYPE},
'id': {4: nl_constants.ATTR_ICMP_ID,
6: nl_constants.ATTR_ICMP_ID},
'sport': {4: nl_constants.ATTR_PORT_SRC,
6: nl_constants.ATTR_PORT_SRC},
'dport': {4: nl_constants.ATTR_PORT_DST,
6: nl_constants.ATTR_PORT_DST}}
NFCT_CALLBACK = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int,
ctypes.c_void_p, ctypes.c_void_p)
class ConntrackOpenFailedExit(SystemExit):
"""Raised if we fail to open a new conntrack or conntrack handler"""
class ConntrackManager(object):
def __init__(self, family_socket=None):
self.family_socket = family_socket
self.set_functions = {
'src': {4: nfct.nfct_set_attr_u32,
6: nfct.nfct_set_attr_u64},
'dst': {4: nfct.nfct_set_attr_u32,
6: nfct.nfct_set_attr_u64},
'ipversion': {4: nfct.nfct_set_attr_u8,
6: nfct.nfct_set_attr_u8},
'protocol': {4: nfct.nfct_set_attr_u8,
6: nfct.nfct_set_attr_u8},
'type': {4: nfct.nfct_set_attr_u8,
6: nfct.nfct_set_attr_u8},
'code': {4: nfct.nfct_set_attr_u8,
6: nfct.nfct_set_attr_u8},
'id': {4: nfct.nfct_set_attr_u16,
6: nfct.nfct_set_attr_u16},
'sport': {4: nfct.nfct_set_attr_u16,
6: nfct.nfct_set_attr_u16},
'dport': {4: nfct.nfct_set_attr_u16,
6: nfct.nfct_set_attr_u16}, }
self.converters = {'src': libc.inet_addr,
'dst': libc.inet_addr,
'ipversion': nl_constants.IPVERSION_SOCKET.get,
'protocol': constants.IP_PROTOCOL_MAP.get,
'code': int,
'type': int,
'id': libc.htons,
'sport': libc.htons,
'dport': libc.htons, }
def list_entries(self):
entries = []
raw_entry = ctypes.create_string_buffer(nl_constants.BUFFER)
@NFCT_CALLBACK
def callback(type_, conntrack, data):
nfct.nfct_snprintf(raw_entry, nl_constants.BUFFER,
conntrack, type_,
nl_constants.NFCT_O_PLAIN,
nl_constants.NFCT_OF_TIME)
entries.append(raw_entry.value)
return nl_constants.NFCT_CB_CONTINUE
self._callback_register(nl_constants.NFCT_T_ALL,
callback, DATA_CALLBACK)
data_ref = self._get_ref(self.family_socket or
nl_constants.IPVERSION_SOCKET[4])
self._query(nl_constants.NFCT_Q_DUMP, data_ref)
return entries
def delete_entries(self, entries):
conntrack = nfct.nfct_new()
try:
for entry in entries:
self._set_attributes(conntrack, entry)
self._query(nl_constants.NFCT_Q_DESTROY, conntrack)
except Exception as e:
msg = _("Failed to delete conntrack entries %s") % e
LOG.critical(msg)
raise ConntrackOpenFailedExit(msg)
finally:
nfct.nfct_destroy(conntrack)
def flush_entries(self):
data_ref = self._get_ref(self.family_socket or
nl_constants.IPVERSION_SOCKET[4])
self._query(nl_constants.NFCT_Q_FLUSH, data_ref)
def _query(self, query_type, query_data):
result = nfct.nfct_query(self.conntrack_handler, query_type,
query_data)
if result == nl_constants.NFCT_CB_FAILURE:
LOG.warning(_LW("Netlink query failed"))
def _set_attributes(self, conntrack, entry):
ipversion = entry.get('ipversion', 4)
for attr, value in entry.items():
set_function = self.set_functions[attr][ipversion]
target = TARGET[attr][ipversion]
converter = self.converters[attr]
set_function(conntrack, target, converter(value))
def _callback_register(self, message_type, callback_func, data):
nfct.nfct_callback_register(self.conntrack_handler,
message_type, callback_func, data)
def _get_ref(self, data):
return ctypes.byref(ctypes.c_int(data))
def __enter__(self):
self.conntrack_handler = nfct.nfct_open(
nl_constants.CONNTRACK,
nl_constants.NFNL_SUBSYS_CTNETLINK)
if not self.conntrack_handler:
msg = _("Failed to open new conntrack handler")
LOG.critical(msg)
raise ConntrackOpenFailedExit(msg)
return self
def __exit__(self, *args):
nfct.nfct_close(self.conntrack_handler)
def _parse_entry(entry, ipversion):
"""Parse entry from text to Python tuple
:param entry: conntrack entry in text
:param ipversion: ipversion 4 or 6
:return: conntrack entry in Python tuple
example: (4, 'tcp', '1', '2', '1.1.1.1', '2.2.2.2')
The attributes are ordered to be easy to compare with other entries
and compare with firewall rule
"""
protocol = entry[1]
parsed_entry = [ipversion, protocol]
for attr, position in ATTR_POSITIONS[protocol]:
val = entry[position].partition('=')[2]
parsed_entry.append(int(val) if attr in ['sport', 'dport', 'type',
'code', 'id'] else val)
return tuple(parsed_entry)
@privileged.default.entrypoint
def flush_entries(namespace=None):
"""Delete all conntrack entries
:param namespace: namespace to delete conntrack entries
:return: None
"""
with fwaas_utils.in_namespace(namespace):
for ipversion in IP_VERSIONS:
with ConntrackManager(nl_constants.IPVERSION_SOCKET[ipversion]) \
as conntrack:
conntrack.flush_entries()
@privileged.default.entrypoint
def list_entries(namespace=None):
"""List and parse all conntrack entries
:param namespace: namespace to get conntrack entries
:return: sorted list of conntrack entries in Python tuple
example: [(4, 'icmp', '8', '0', '1.1.1.1', '2.2.2.2', '1234'),
(4, 'tcp', '1', '2', '1.1.1.1', '2.2.2.2')]
"""
parsed_entries = []
with fwaas_utils.in_namespace(namespace):
for ipversion in IP_VERSIONS:
with ConntrackManager(nl_constants.IPVERSION_SOCKET[ipversion]) \
as conntrack:
raw_entries = conntrack.list_entries()
for raw_entry in raw_entries:
parsed_entry = _parse_entry(raw_entry.split(), ipversion)
parsed_entries.append(parsed_entry)
return sorted(parsed_entries)
@privileged.default.entrypoint
def delete_entries(entries, namespace=None):
"""Delete selected entries
:param entries: list of parsed (as tuple) entries to delete
:param namespace: namespace to delete conntrack entries
:return: None
"""
entry_args = []
for entry in entries:
entry_arg = {'ipversion': entry[0], 'protocol': entry[1]}
for idx, attr in enumerate(ATTR_POSITIONS[entry_arg['protocol']]):
entry_arg[attr[0]] = entry[idx + 2]
entry_args.append(entry_arg)
with fwaas_utils.in_namespace(namespace):
with ConntrackManager() as conntrack:
conntrack.delete_entries(entry_args)

View File

@ -1,29 +0,0 @@
# Copyright (c) 2017 Thales Services SAS
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_utils import uuidutils
from pyroute2 import netns as pynetns
from neutron_fwaas import privileged
# TODO(cby): move this method in neutron.tests.functional.privileged associated
# to a new privsep context.
@privileged.default.entrypoint
def dummy():
"""This method aim is to validate that we can use privsep in functests."""
namespace = 'dummy-%s' % uuidutils.generate_uuid()
pynetns.create(namespace)
pynetns.remove(namespace)

View File

@ -1,36 +0,0 @@
# Copyright (c) 2017 Thales Services SAS
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os
import re
from neutron_fwaas import privileged
from neutron_fwaas.privileged import utils
def get_my_netns_inode():
link = os.readlink(utils.PROCESS_NETNS)
# NOTE(cby): link respects the format "net:[<inode>]"
return int(re.match('net:\[(\d+)\]', link).group(1))
@privileged.default.entrypoint
def get_in_namespace_netns_inodes(namespace):
before = get_my_netns_inode()
with utils.in_namespace(namespace):
inside = get_my_netns_inode()
after = get_my_netns_inode()
return before, inside, after

View File

@ -1,64 +0,0 @@
# Copyright (c) 2017 Thales Services SAS
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import contextlib
import os
from oslo_log import log as logging
from pyroute2 import netns as pynetns
from neutron_fwaas._i18n import _
PROCESS_NETNS = '/proc/self/ns/net'
LOG = logging.getLogger(__name__)
class BackInNamespaceExit(SystemExit):
"""Raised if we fail to moved back process in its original namespace."""
@contextlib.contextmanager
def in_namespace(namespace):
"""Move current process in a specific namespace.
This contextmanager moves current process in a specific namespace and
ensures to move it back in original namespace or kills it if we fail to
move back in original namespace.
"""
if not namespace:
yield
return
org_netns_fd = os.open(PROCESS_NETNS, os.O_RDONLY)
try:
new_netns_fd = pynetns.setns(namespace)
try:
try:
yield
finally:
try:
# NOTE(cby): this code is not executed only if we fail to
# move in target namespace
pynetns.setns(org_netns_fd)
except Exception as e:
msg = _('Failed to move back in original netns: %s') % e
LOG.critical(msg)
raise BackInNamespaceExit(msg)
finally:
os.close(new_netns_fd)
finally:
os.close(org_netns_fd)

View File

@ -1,86 +0,0 @@
# Copyright (c) 2013 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from neutron.common import rpc as n_rpc
from oslo_config import cfg
import oslo_messaging
from neutron_fwaas._i18n import _
FWAAS_V1 = "v1"
FWAAS_V2 = "v2"
FWaaSOpts = [
cfg.StrOpt(
'driver',
default='',
help=_("Name of the FWaaS Driver")),
cfg.BoolOpt(
'enabled',
default=False,
help=_("Enable FWaaS")),
cfg.StrOpt(
'agent_version',
default=FWAAS_V1,
help=_("Firewall agent class")),
cfg.StrOpt(
'conntrack_driver',
default='conntrack',
help=_("Name of the FWaaS Conntrack Driver")),
]
cfg.CONF.register_opts(FWaaSOpts, 'fwaas')
class FWaaSPluginApiMixin(object):
"""Agent side of the FWaaS agent to FWaaS Plugin RPC API."""
def __init__(self, topic, host):
self.host = host
target = oslo_messaging.Target(topic=topic, version='1.0')
self.client = n_rpc.get_client(target)
def set_firewall_status(self, context, firewall_id, status):
"""Make a RPC to set the status of a firewall."""
cctxt = self.client.prepare()
return cctxt.call(context, 'set_firewall_status', host=self.host,
firewall_id=firewall_id, status=status)
def firewall_deleted(self, context, firewall_id):
"""Make a RPC to indicate that the firewall resources are deleted."""
cctxt = self.client.prepare()
return cctxt.call(context, 'firewall_deleted', host=self.host,
firewall_id=firewall_id)
class FWaaSAgentRpcCallbackMixin(object):
"""Mixin for FWaaS agent Implementations."""
def __init__(self, host):
super(FWaaSAgentRpcCallbackMixin, self).__init__(host)
def create_firewall(self, context, firewall, host):
"""Handle RPC cast from plugin to create a firewall."""
pass
def update_firewall(self, context, firewall, host):
"""Handle RPC cast from plugin to update a firewall."""
pass
def delete_firewall(self, context, firewall, host):
"""Handle RPC cast from plugin to delete a firewall."""
pass

View File

@ -1,44 +0,0 @@
# Copyright 2014 OpenStack Foundation.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from neutron.services import provider_configuration as provconf
from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import importutils
from neutron_fwaas._i18n import _
LOG = logging.getLogger(__name__)
FIREWALL_DRIVERS = 'firewall_drivers'
class FirewallService(object):
"""Firewall Service observer."""
def load_device_drivers(self):
"""Loads a single device driver for FWaaS."""
device_driver = provconf.get_provider_driver_class(
cfg.CONF.fwaas.driver, FIREWALL_DRIVERS)
try:
driver = importutils.import_object(device_driver)
LOG.debug('Loaded FWaaS device driver: %s', device_driver)
return driver
except ImportError:
msg = _('Error importing FWaaS device driver: %s')
raise ImportError(msg % device_driver)
except ValueError:
msg = _('Configuration error - no FWaaS device_driver specified')
raise ValueError(msg)

View File

@ -1,440 +0,0 @@
# Copyright (c) 2013 OpenStack Foundation.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from neutron.common import rpc as n_rpc
from oslo_config import cfg
from oslo_log import helpers as log_helpers
from oslo_log import log as logging
from neutron_fwaas._i18n import _, _LE
from neutron_fwaas.common import fwaas_constants
from neutron_fwaas.common import resources as f_resources
from neutron_fwaas.extensions import firewall as fw_ext
from neutron_fwaas.services.firewall.agents import firewall_agent_api as api
from neutron_fwaas.services.firewall.agents import firewall_service
from neutron_lib.agent import l3_extension
from neutron_lib import constants as nl_constants
from neutron_lib import context
LOG = logging.getLogger(__name__)
#TODO(njohnston): There needs to be some extrapolation of the common code
# between this module and firewall_l3_agent_v2.py.
class FWaaSL3PluginApi(api.FWaaSPluginApiMixin):
"""Agent side of the FWaaS agent to FWaaS Plugin RPC API."""
def __init__(self, topic, host):
super(FWaaSL3PluginApi, self).__init__(topic, host)
def get_firewalls_for_tenant(self, context, **kwargs):
"""Get the Firewalls with rules from the Plugin to send to driver."""
LOG.debug("Retrieve Firewall with rules from Plugin")
cctxt = self.client.prepare()
return cctxt.call(context, 'get_firewalls_for_tenant', host=self.host)
def get_tenants_with_firewalls(self, context, **kwargs):
"""Get all Tenants that have Firewalls configured from plugin."""
LOG.debug("Retrieve Tenants with Firewalls configured from Plugin")
cctxt = self.client.prepare()
return cctxt.call(context,
'get_tenants_with_firewalls', host=self.host)
class FWaaSL3AgentExtension(l3_extension.L3AgentExtension):
"""FWaaS Agent support to be used by Neutron L3 agent."""
SUPPORTED_RESOURCE_TYPES = [f_resources.FIREWALL_GROUP,
f_resources.FIREWALL_POLICY,
f_resources.FIREWALL_RULE]
def initialize(self, connection, driver_type):
self._register_rpc_consumers(connection)
def consume_api(self, agent_api):
LOG.debug("FWaaS consume_api call occurred with %s", agent_api)
self.agent_api = agent_api
def _register_rpc_consumers(self, connection):
#TODO(njohnston): Add RPC consumer connection loading here.
pass
def start_rpc_listeners(self, conf):
self.endpoints = [self]
self.conn = n_rpc.create_connection()
self.conn.create_consumer(
fwaas_constants.FW_AGENT, self.endpoints, fanout=False)
return self.conn.consume_in_threads()
def __init__(self, host, conf):
LOG.debug("Initializing firewall agent")
self.agent_api = None
self.neutron_service_plugins = None
self.conf = conf
self.fwaas_enabled = cfg.CONF.fwaas.enabled
self.start_rpc_listeners(conf)
# None means l3-agent has no information on the server
# configuration due to the lack of RPC support.
if self.neutron_service_plugins is not None:
fwaas_plugin_configured = (fwaas_constants.FIREWALL
in self.neutron_service_plugins)
if fwaas_plugin_configured and not self.fwaas_enabled:
msg = _("FWaaS plugin is configured in the server side, but "
"FWaaS is disabled in L3-agent.")
LOG.error(msg)
raise SystemExit(1)
self.fwaas_enabled = self.fwaas_enabled and fwaas_plugin_configured
if self.fwaas_enabled:
# NOTE: Temp location for creating service and loading driver
self.fw_service = firewall_service.FirewallService()
self.fwaas_driver = self.fw_service.load_device_drivers()
self.services_sync_needed = False
# setup RPC to msg fwaas plugin
self.fwplugin_rpc = FWaaSL3PluginApi(fwaas_constants.FIREWALL_PLUGIN,
host)
def _has_router_insertion_fields(self, fw):
return 'add-router-ids' in fw
def _get_router_ids_for_fw(self, context, fw, to_delete=False):
"""Return the router_ids either from fw dict or tenant routers."""
if self._has_router_insertion_fields(fw):
# it is a new version of plugin
return (fw['del-router-ids'] if to_delete
else fw['add-router-ids'])
else:
return [router['id'] for router in
self.agent_api.get_routers_in_project(fw['tenant_id'])]
def _get_routers_in_project(self, project_id):
if self.agent_api is None:
LOG.exception(_LE("FWaaS RPC call failed; L3 agent_api failure"))
router_info = self.agent_api._router_info
if project_id:
return [ri for ri in router_info.values()
if ri.router['tenant_id'] == project_id]
else:
return []
def _get_router_info_list_for_tenant(self, router_ids, tenant_id):
"""Returns the list of router info objects on which to apply the fw."""
return [ri for ri in self._get_routers_in_project(tenant_id)
if ri.router_id in router_ids and
self.agent_api.is_router_in_namespace(ri.router_id)]
def _invoke_driver_for_sync_from_plugin(self, ctx, router_info_list, fw):
"""Invoke the delete driver method for status of PENDING_DELETE and
update method for all other status to (re)apply on driver which is
Idempotent.
"""
if fw['status'] == nl_constants.PENDING_DELETE:
try:
self.fwaas_driver.delete_firewall(
self.conf.agent_mode,
router_info_list,
fw)
self.fwplugin_rpc.firewall_deleted(
ctx,
fw['id'])
except fw_ext.FirewallInternalDriverError:
LOG.error(_LE("Firewall Driver Error on fw state %(fwmsg)s "
"for fw: %(fwid)s"),
{'fwmsg': fw['status'], 'fwid': fw['id']})
self.fwplugin_rpc.set_firewall_status(
ctx,
fw['id'],
nl_constants.ERROR)
else:
# PENDING_UPDATE, PENDING_CREATE, ...
try:
self.fwaas_driver.update_firewall(
self.conf.agent_mode,
router_info_list,
fw)
if fw['admin_state_up']:
status = nl_constants.ACTIVE
else:
status = nl_constants.DOWN
except fw_ext.FirewallInternalDriverError:
LOG.error(_LE("Firewall Driver Error on fw state %(fwmsg)s "
"for fw: %(fwid)s"),
{'fwmsg': fw['status'], 'fwid': fw['id']})
status = nl_constants.ERROR
self.fwplugin_rpc.set_firewall_status(
ctx,
fw['id'],
status)
def _process_router_add(self, router):
"""On router add, get fw with rules from plugin and update driver."""
LOG.debug("Process router add, router_id: '%s'", router['id'])
router_ids = router['id']
router_info_list = self._get_router_info_list_for_tenant(
[router_ids],
router['tenant_id'])
if router_info_list:
# Get the firewall with rules
# for the tenant the router is on.
ctx = context.Context('', router['tenant_id'])
fw_list = self.fwplugin_rpc.get_firewalls_for_tenant(ctx)
for fw in fw_list:
if self._has_router_insertion_fields(fw):
# if router extension present apply only if router in fw
if (not (router_ids in fw['add-router-ids']) and
not (router_ids in fw['del-router-ids'])):
continue
self._invoke_driver_for_sync_from_plugin(
ctx,
router_info_list,
fw)
# router can be present only on one fw
return
def add_router(self, context, new_router):
"""On router add, get fw with rules from plugin and update driver.
Handles agent restart, when a router is added, query the plugin to
check if this router is in the router list for any firewall. If so
install firewall rules on this router.
"""
# avoid msg to plugin when fwaas is not configured
if not self.fwaas_enabled:
return
try:
self._process_router_add(new_router)
except Exception:
LOG.exception(
_LE("FWaaS RPC info call failed for '%s'."),
new_router['id'])
self.services_sync_needed = True
def update_router(self, context, updated_router):
"""The update_router method is just a synonym for add_router"""
self.add_router(context, updated_router)
def delete_router(self, context, new_router):
"""Handles router deletion. There is basically nothing to do for this
in the context of FWaaS with an IPTables driver; the namespace will
already have been deleted, taking the IPTables rules with it.
"""
#TODO(njohnston): When another firewall driver is implemented, look at
# expanding this out so that the driver can handle deletion calls.
pass
def process_services_sync(self, ctx):
if not self.services_sync_needed:
return
"""On RPC issues sync with plugin and apply the sync data."""
# avoid msg to plugin when fwaas is not configured
if not self.fwaas_enabled:
return
try:
# get the list of tenants with firewalls configured
# from the plugin
tenant_ids = self.fwplugin_rpc.get_tenants_with_firewalls(ctx)
LOG.debug("Tenants with Firewalls: '%s'", tenant_ids)
for tenant_id in tenant_ids:
ctx = context.Context('', tenant_id)
fw_list = self.fwplugin_rpc.get_firewalls_for_tenant(ctx)
for fw in fw_list:
if fw['status'] == nl_constants.PENDING_DELETE:
self.delete_firewall(ctx, fw, self.host)
# no need to apply sync data for ACTIVE fw
elif fw['status'] != nl_constants.ACTIVE:
self.update_firewall(ctx, fw, self.host)
self.services_sync_needed = False
except Exception:
LOG.exception(_LE("Failed fwaas process services sync"))
self.services_sync_needed = True
@log_helpers.log_method_call
def create_firewall(self, context, firewall, host):
"""Handle Rpc from plugin to create a firewall."""
router_ids = self._get_router_ids_for_fw(context, firewall)
if not router_ids:
return
router_info_list = self._get_router_info_list_for_tenant(
router_ids,
firewall['tenant_id'])
LOG.debug("Create: Add firewall on Router List: '%s'",
[ri.router['id'] for ri in router_info_list])
# call into the driver
try:
self.fwaas_driver.create_firewall(
self.conf.agent_mode,
router_info_list,
firewall)
if firewall['admin_state_up']:
status = nl_constants.ACTIVE
else:
status = nl_constants.DOWN
except fw_ext.FirewallInternalDriverError:
LOG.error(_LE("Firewall Driver Error for create_firewall "
"for firewall: %(fwid)s"),
{'fwid': firewall['id']})
status = nl_constants.ERROR
try:
# send status back to plugin
self.fwplugin_rpc.set_firewall_status(
context,
firewall['id'],
status)
except Exception:
LOG.exception(
_LE("FWaaS RPC failure in create_firewall "
"for firewall: %(fwid)s"),
{'fwid': firewall['id']})
self.services_sync_needed = True
@log_helpers.log_method_call
def update_firewall(self, context, firewall, host):
"""Handle Rpc from plugin to update a firewall."""
status = ""
if self._has_router_insertion_fields(firewall):
# with the router_ids extension, we may need to delete and add
# based on the list of routers. On the older version, we just
# update (add) all routers on the tenant - delete not needed.
router_ids = self._get_router_ids_for_fw(
context, firewall, to_delete=True)
if router_ids:
router_info_list = self._get_router_info_list_for_tenant(
router_ids,
firewall['tenant_id'])
# remove the firewall from this set of routers
# but no ack sent yet, check if we need to add
LOG.debug("Update: Delete firewall on Router List: '%s'",
[ri.router['id'] for ri in router_info_list])
try:
self.fwaas_driver.delete_firewall(
self.conf.agent_mode,
router_info_list,
firewall)
if firewall['last-router']:
status = nl_constants.INACTIVE
elif firewall['admin_state_up']:
status = nl_constants.ACTIVE
else:
status = nl_constants.DOWN
except fw_ext.FirewallInternalDriverError:
LOG.error(_LE("Firewall Driver Error for "
"update_firewall for firewall: "
"%(fwid)s"),
{'fwid': firewall['id']})
status = nl_constants.ERROR
# handle the add router and/or rule, policy, firewall
# attribute updates
if status not in (nl_constants.ERROR, nl_constants.INACTIVE):
router_ids = self._get_router_ids_for_fw(context, firewall)
if router_ids or firewall['router_ids']:
router_info_list = self._get_router_info_list_for_tenant(
router_ids + firewall['router_ids'],
firewall['tenant_id'])
LOG.debug("Update: Add firewall on Router List: '%s'",
[ri.router['id'] for ri in router_info_list])
# call into the driver
try:
self.fwaas_driver.update_firewall(
self.conf.agent_mode,
router_info_list,
firewall)
if firewall['admin_state_up']:
status = nl_constants.ACTIVE
else:
status = nl_constants.DOWN
except fw_ext.FirewallInternalDriverError:
LOG.error(_LE("Firewall Driver Error for "
"update_firewall for firewall: "
"%(fwid)s"),
{'fwid': firewall['id']})
status = nl_constants.ERROR
else:
status = nl_constants.INACTIVE
try:
# send status back to plugin
self.fwplugin_rpc.set_firewall_status(
context,
firewall['id'],
status)
except Exception:
LOG.exception(
_LE("FWaaS RPC failure in update_firewall "
"for firewall: %(fwid)s"),
{'fwid': firewall['id']})
self.services_sync_needed = True
@log_helpers.log_method_call
def delete_firewall(self, context, firewall, host):
"""Handle Rpc from plugin to delete a firewall."""
router_ids = self._get_router_ids_for_fw(
context, firewall, to_delete=True)
if router_ids:
router_info_list = self._get_router_info_list_for_tenant(
router_ids,
firewall['tenant_id'])
LOG.debug(
"Delete firewall %(fw)s on routers: '%(routers)s'",
{'fw': firewall['id'],
'routers': [ri.router['id'] for ri in router_info_list]})
# call into the driver
try:
self.fwaas_driver.delete_firewall(
self.conf.agent_mode,
router_info_list,
firewall)
if firewall['admin_state_up']:
status = nl_constants.ACTIVE
else:
status = nl_constants.DOWN
except fw_ext.FirewallInternalDriverError:
LOG.error(_LE("Firewall Driver Error for delete_firewall "
"for firewall: %(fwid)s"),
{'fwid': firewall['id']})
status = nl_constants.ERROR
try:
# send status back to plugin
if status in [nl_constants.ACTIVE, nl_constants.DOWN]:
self.fwplugin_rpc.firewall_deleted(context, firewall['id'])
else:
self.fwplugin_rpc.set_firewall_status(
context,
firewall['id'],
status)
except Exception:
LOG.exception(
_LE("FWaaS RPC failure in delete_firewall "
"for firewall: %(fwid)s"),
{'fwid': firewall['id']})
self.services_sync_needed = True
class L3WithFWaaS(FWaaSL3AgentExtension):
def __init__(self, conf=None):
if conf:
self.conf = conf
else:
self.conf = cfg.CONF
super(L3WithFWaaS, self).__init__(host=self.conf.host, conf=self.conf)

View File

@ -1,512 +0,0 @@
# Copyright (c) 2016
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from neutron.agent.linux import ip_lib
from neutron.common import rpc as n_rpc
from neutron_lib import constants as nl_constants
from neutron_lib import context
from oslo_config import cfg
from oslo_log import helpers as log_helpers
from oslo_log import log as logging
from neutron_fwaas._i18n import _, _LE
from neutron_fwaas.common import fwaas_constants
from neutron_fwaas.common import resources as f_resources
from neutron_fwaas.extensions import firewall as fw_ext
from neutron_fwaas.services.firewall.agents import firewall_agent_api as api
from neutron_fwaas.services.firewall.agents import firewall_service
from neutron_lib.agent import l3_extension
LOG = logging.getLogger(__name__)
class FWaaSL3PluginApi(api.FWaaSPluginApiMixin):
"""Agent side of the FWaaS agent-to-plugin RPC API."""
def __init__(self, topic, host):
super(FWaaSL3PluginApi, self).__init__(topic, host)
def get_firewall_groups_for_project(self, context, **kwargs):
"""Fetches a project's firewall groups from the plugin."""
LOG.debug("Fetch firewall groups from plugin")
cctxt = self.client.prepare()
return cctxt.call(context, 'get_firewall_groups_for_project',
host=self.host)
def get_projects_with_firewall_groups(self, context, **kwargs):
"""Fetches from the plugin all projects that have firewall groups
configured.
"""
LOG.debug("Fetch from plugin projects that have firewall groups "
"configured")
cctxt = self.client.prepare()
return cctxt.call(context,
'get_projects_with_firewall_groups', host=self.host)
def firewall_group_deleted(self, context, fwg_id, **kwargs):
"""Notifies the plugin that a firewall group has been deleted."""
LOG.debug("Notify plugin that firewall group has been deleted")
cctxt = self.client.prepare()
return cctxt.call(context, 'firewall_group_deleted', fwg_id=fwg_id,
host=self.host)
def set_firewall_group_status(self, context, fwg_id, status, **kwargs):
"""Sets firewall group's status on the plugin."""
LOG.debug("Set firewall groups from plugin")
cctxt = self.client.prepare()
return cctxt.call(context, 'set_firewall_group_status',
fwg_id=fwg_id, status=status, host=self.host)
class FWaaSL3AgentExtension(l3_extension.L3AgentExtension):
"""FWaaS agent extension."""
SUPPORTED_RESOURCE_TYPES = [f_resources.FIREWALL_GROUP,
f_resources.FIREWALL_POLICY,
f_resources.FIREWALL_RULE]
def initialize(self, connection, driver_type):
self._register_rpc_consumers(connection)
def consume_api(self, agent_api):
LOG.debug("FWaaS consume_api call occurred with %s", agent_api)
self.agent_api = agent_api
def _register_rpc_consumers(self, connection):
#TODO(njohnston): Add RPC consumer connection loading here.
pass
def start_rpc_listeners(self, host, conf):
self.endpoints = [self]
self.conn = n_rpc.create_connection()
self.conn.create_consumer(
fwaas_constants.FW_AGENT, self.endpoints, fanout=False)
return self.conn.consume_in_threads()
def __init__(self, host, conf):
LOG.debug("Initializing firewall group agent")
self.agent_api = None
self.neutron_service_plugins = None
self.conf = conf
self.fwaas_enabled = cfg.CONF.fwaas.enabled
self.start_rpc_listeners(host, conf)
# None means l3-agent has no information on the server
# configuration due to the lack of RPC support.
if self.neutron_service_plugins is not None:
fwaas_plugin_configured = (fwaas_constants.FIREWALL
in self.neutron_service_plugins)
if fwaas_plugin_configured and not self.fwaas_enabled:
msg = _("FWaaS plugin is configured in the server side, but "
"FWaaS is disabled in L3-agent.")
LOG.error(msg)
raise SystemExit(1)
self.fwaas_enabled = self.fwaas_enabled and fwaas_plugin_configured
if self.fwaas_enabled:
# NOTE: Temp location for creating service and loading driver
self.fw_service = firewall_service.FirewallService()
self.fwaas_driver = self.fw_service.load_device_drivers()
self.services_sync_needed = False
self.fwplugin_rpc = FWaaSL3PluginApi(fwaas_constants.FIREWALL_PLUGIN,
host)
super(FWaaSL3AgentExtension, self).__init__()
@property
def _local_namespaces(self):
root_ip = ip_lib.IPWrapper()
local_ns_list = root_ip.get_namespaces()
return local_ns_list
def _has_port_insertion_fields(self, firewall_group):
"""The presence of the 'add-port-ids' key in the firewall group dict
shows we are using the current version of the plugin. If this key
is absent, we are in an upgrade and message is from an older
version of the plugin.
"""
return 'add-port-ids' in firewall_group
def _get_firewall_group_ports(self, context, firewall_group,
to_delete=False, require_new_plugin=False):
"""Returns in-namespace ports, either from firewall group dict if newer
version of plugin or from project routers otherwise.
NOTE: Vernacular move from "tenant" to "project" doesn't yet appear
as a key in router or firewall group objects.
"""
fwg_port_ids = []
if self._has_port_insertion_fields(firewall_group):
if to_delete:
fwg_port_ids = firewall_group['del-port-ids']
else:
fwg_port_ids = firewall_group['add-port-ids']
elif not require_new_plugin:
routers = self.agent_api.get_routers_in_project(
firewall_group['tenant_id'])
for router in routers:
if router.router['tenant_id'] == firewall_group['tenant_id']:
fwg_port_ids.extend([p['id'] for p in
router.internal_ports])
# Return in-namespace port objects.
return self._get_in_ns_ports(fwg_port_ids)
def _get_in_ns_ports(self, port_ids):
"""Returns port objects in the local namespace, along with their
router_info.
"""
in_ns_ports = {} # This will be converted to a list later.
if port_ids and self.agent_api:
for port_id in port_ids:
# This fetched router_info is guaranteed to be in_namespace.
router_info = self.agent_api.get_router_hosting_port(port_id)
if router_info:
if router_info in in_ns_ports:
in_ns_ports[router_info].append(port_id)
else:
in_ns_ports[router_info] = [port_id]
return list(in_ns_ports.items())
def _invoke_driver_for_sync_from_plugin(self, ctx, port, firewall_group):
"""Calls the FWaaS driver's delete_firewall_group method if firewall
group has status of PENDING_DELETE; calls driver's
update_firewall_group method for all other statuses. Both of these
methods are idempotent.
"""
port_list = self._get_in_ns_ports([port['id']])
if firewall_group['status'] == nl_constants.PENDING_DELETE:
try:
self.fwaas_driver.delete_firewall_group(
self.conf.agent_mode, port_list, firewall_group)
self.fwplugin_rpc.firewall_group_deleted(
ctx, firewall_group['id'])
except fw_ext.FirewallInternalDriverError:
msg = _LE("FWaaS driver error on %(status)s "
"for firewall group: %(fwg_id)s")
LOG.exception(msg, {'status': firewall_group['status'],
'fwg_id': firewall_group['id']})
self.fwplugin_rpc.set_firewall_group_status(
ctx, firewall_group['id'], nl_constants.ERROR)
else: # PENDING_UPDATE, PENDING_CREATE, ...
# Prepare firewall group status to return to plugin; may be
# overwritten if call to driver fails.
if firewall_group['admin_state_up']:
status = nl_constants.ACTIVE
else:
status = nl_constants.DOWN
# Call the driver.
try:
self.fwaas_driver.update_firewall_group(
self.conf.agent_mode, port_list, firewall_group)
except fw_ext.FirewallInternalDriverError:
msg = _LE("FWaaS driver error on %(status)s for firewall "
"group: "
"%(fwg_id)s")
LOG.exception(msg, {'status': firewall_group['status'],
'fwg_id': firewall_group['id']})
status = nl_constants.ERROR
# Notify the plugin of firewall group's status.
self.fwplugin_rpc.set_firewall_group_status(
ctx, firewall_group['id'], status)
def _process_router_update(self, updated_router):
"""If a new or existing router in the local namespace is updated,
queries the plugin to get the firewall groups for the project in
question and then sees if the router has any ports for any firewall
group that is configured for that project. If so, installs firewall
group rules on the requested ports on this router.
"""
LOG.debug("Process router update, router_id: %s tenant: %s.",
updated_router['id'], updated_router['tenant_id'])
router_id = updated_router['id']
if not self.agent_api.is_router_in_namespace(router_id):
return
# Get the firewall groups for the new router's project.
# NOTE: Vernacular move from "tenant" to "project" doesn't yet appear
# as a key in router or firewall group objects.
ctx = context.Context('', updated_router['tenant_id'])
fwg_list = self.fwplugin_rpc.get_firewall_groups_for_project(ctx)
# Apply a firewall group, as requested, to ports on the new router.
if nl_constants.INTERFACE_KEY in updated_router:
for port in updated_router[nl_constants.INTERFACE_KEY]:
for firewall_group in fwg_list:
if (self._has_port_insertion_fields(firewall_group) and
(port['id'] in firewall_group['add-port-ids'] or
port['id'] in firewall_group['del-port-ids'])):
self._invoke_driver_for_sync_from_plugin(ctx, port,
firewall_group)
# A port can have at most one firewall group.
break
def add_router(self, context, new_router):
"""Handles agent restart and router add. Fetches firewall groups from
plugin and updates driver.
"""
if not self.fwaas_enabled:
return
try:
self._process_router_update(new_router)
except Exception:
LOG.exception(_LE("FWaaS router add RPC info call failed for %s"),
new_router['id'])
self.services_sync_needed = True
def update_router(self, context, updated_router):
"""Handles agent restart and router add. Fetches firewall groups from
plugin and updates driver.
"""
if not self.fwaas_enabled:
return
try:
self._process_router_update(updated_router)
except Exception:
#TODO(njohnston): This repr should be replaced.
LOG.exception(
_LE("FWaaS router update RPC info call failed for %s"),
repr(updated_router))
self.services_sync_needed = True
def delete_router(self, context, new_router):
"""Handles router deletion. There is basically nothing to do for this
in the context of FWaaS with an IPTables driver; the namespace will
already have been deleted, taking the IPTables rules with it.
"""
#TODO(njohnston): When another firewall driver is implemented, look at
# expanding this out so that the driver can handle deletion calls.
pass
def process_services_sync(self, ctx):
"""Syncs with plugin and applies the sync data.
"""
if not self.services_sync_needed or not self.fwaas_enabled:
return
try:
# Fetch from the plugin the list of projects with firewall groups.
project_ids = \
self.fwplugin_rpc.get_projects_with_firewall_groups(ctx)
LOG.debug("Projects with firewall groups: %s",
', '.join(project_ids))
for project_id in project_ids:
ctx = context.Context('', project_id)
fwg_list = \
self.fwplugin_rpc.get_firewall_groups_for_project(ctx)
for firewall_group in fwg_list:
if firewall_group['status'] == nl_constants.PENDING_DELETE:
self.delete_firewall_group(ctx, firewall_group,
self.host)
# No need to apply sync data for ACTIVE firewall group.
elif firewall_group['status'] != nl_constants.ACTIVE:
self.update_firewall_group(ctx, firewall_group,
self.host)
self.services_sync_needed = False
except Exception:
LOG.exception(_LE("Failed FWaaS process services sync."))
self.services_sync_needed = True
@log_helpers.log_method_call
def create_firewall_group(self, context, firewall_group, host):
"""Handles RPC from plugin to create a firewall group.
"""
# Get the in-namespace ports to which to add the firewall group.
ports_for_fwg = self._get_firewall_group_ports(context, firewall_group)
if not ports_for_fwg:
return
LOG.debug("Create firewall group %(fwg_id)s on ports: %(ports)s",
{'fwg_id': firewall_group['id'],
'ports': ', '.join([p for ri_ports in ports_for_fwg
for p in ri_ports[1]])})
# Set firewall group status; will be overwritten if call to driver
# fails.
if firewall_group['admin_state_up']:
status = nl_constants.ACTIVE
else:
status = nl_constants.DOWN
# Call the driver.
try:
self.fwaas_driver.create_firewall_group(self.conf.agent_mode,
ports_for_fwg,
firewall_group)
except fw_ext.FirewallInternalDriverError:
msg = _LE("FWaaS driver error in create_firewall_group "
"for firewall group: %(fwg_id)s")
LOG.exception(msg, {'fwg_id': firewall_group['id']})
status = nl_constants.ERROR
# Send firewall group's status to plugin.
try:
self.fwplugin_rpc.set_firewall_group_status(context,
firewall_group['id'], status)
except Exception:
msg = _LE("FWaaS RPC failure in create_firewall_group "
"for firewall group: %(fwg_id)s")
LOG.exception(msg, {'fwg_id': firewall_group['id']})
self.services_sync_needed = True
@log_helpers.log_method_call
def update_firewall_group(self, context, firewall_group, host):
"""Handles RPC from plugin to update a firewall group.
"""
# Initialize firewall group status.
status = ""
# Get the list of in-namespace ports from which to delete the firewall
# group.
ports_for_fwg = self._get_firewall_group_ports(context, firewall_group,
to_delete=True, require_new_plugin=True)
# Remove firewall group from ports if requested.
if ports_for_fwg:
fw_ports = [p for ri_ports in ports_for_fwg for p in ri_ports[1]]
LOG.debug("Update (delete) firewall group %(fwg_id)s on ports: "
"%(ports)s",
{'fwg_id': firewall_group['id'],
'ports': ', '.join(fw_ports)})
# Set firewall group's status; will be overwritten if call to
# driver fails.
if firewall_group['admin_state_up']:
status = nl_constants.ACTIVE
if firewall_group['last-port']:
status = nl_constants.INACTIVE
else:
status = nl_constants.DOWN
# Call the driver.
try:
self.fwaas_driver.delete_firewall_group(self.conf.agent_mode,
ports_for_fwg,
firewall_group)
except fw_ext.FirewallInternalDriverError:
msg = _LE("FWaaS driver error in update_firewall_group "
"(add) for firewall group: %s")
LOG.exception(msg, firewall_group['id'])
status = nl_constants.ERROR
# Handle the add router and/or rule, policy, firewall group attribute
# updates.
if status not in (nl_constants.ERROR, nl_constants.INACTIVE):
ports_for_fwg = self._get_firewall_group_ports(context,
firewall_group)
if ports_for_fwg:
fw_ports = [p for ri_ports in ports_for_fwg
for p in ri_ports[1]]
LOG.debug("Update (create) firewall group %(fwg_id)s on "
"ports: %(ports)s",
{'fwg_id': firewall_group['id'],
'ports': ', '.join(fw_ports)})
# Set firewall group status, which will be overwritten if call
# to driver fails.
if firewall_group['admin_state_up']:
status = nl_constants.ACTIVE
else:
status = nl_constants.DOWN
# Call the driver.
try:
self.fwaas_driver.update_firewall_group(
self.conf.agent_mode, ports_for_fwg,
firewall_group)
except fw_ext.FirewallInternalDriverError:
msg = _LE("FWaaS driver error in update_firewall_group "
"for firewall group: %s")
LOG.exception(msg, firewall_group['id'])
status = nl_constants.ERROR
else:
status = nl_constants.INACTIVE
# Return status to plugin.
try:
self.fwplugin_rpc.set_firewall_group_status(context,
firewall_group['id'], status)
except Exception:
LOG.exception(_LE("FWaaS RPC failure in update_firewall_group "
"for firewall group: %(fwg_id)s"),
{'fwg_id': firewall_group['id']})
self.services_sync_needed = True
@log_helpers.log_method_call
def delete_firewall_group(self, context, firewall_group, host):
"""Handles RPC from plugin to delete a firewall group.
"""
ports_for_fwg = self._get_firewall_group_ports(context, firewall_group,
to_delete=True)
if not ports_for_fwg:
return
fw_ports = [p for ri_ports in ports_for_fwg for p in ri_ports[1]]
LOG.debug("Delete firewall group %(fwg_id)s on ports: %(ports)s",
{'fwg_id': firewall_group['id'],
'ports': ', '.join(fw_ports)})
# Set the firewall group's status to return to plugin; status may be
# overwritten if call to driver fails.
if firewall_group['admin_state_up']:
status = nl_constants.ACTIVE
else:
status = nl_constants.DOWN
try:
self.fwaas_driver.delete_firewall_group(self.conf.agent_mode,
ports_for_fwg,
firewall_group)
# Call the driver.
except fw_ext.FirewallInternalDriverError:
LOG.exception(_LE("FWaaS driver error in delete_firewall_group "
"for firewall group: %(fwg_id)s"),
{'fwg_id': firewall_group['id']})
status = nl_constants.ERROR
# Notify plugin of deletion or return firewall group's status to
# plugin, as appropriate.
try:
if status in [nl_constants.ACTIVE, nl_constants.DOWN]:
self.fwplugin_rpc.firewall_group_deleted(context,
firewall_group['id'])
else:
self.fwplugin_rpc.set_firewall_group_status(context,
firewall_group['id'], status)
except Exception:
LOG.exception(_LE("FWaaS RPC failure in delete_firewall_group "
"for firewall group: %(fwg_id)s"),
{'fwg_id': firewall_group['id']})
self.services_sync_needed = True
class L3WithFWaaS(FWaaSL3AgentExtension):
def __init__(self, conf=None):
if conf:
self.conf = conf
else:
self.conf = cfg.CONF
super(L3WithFWaaS, self).__init__(host=self.conf.host, conf=self.conf)

View File

@ -1,35 +0,0 @@
# Copyright (c) 2017 Fujitsu Limited
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import abc
import six
@six.add_metaclass(abc.ABCMeta)
class ConntrackDriverBase(object):
"""Base Driver for Conntrack"""
@abc.abstractmethod
def initialize(self, *args, **kwargs):
"""Initialize the driver"""
@abc.abstractmethod
def delete_entries(self, rules, namespace):
"""Delete conntrack entries specified by list of rules"""
@abc.abstractmethod
def flush_entries(self, namespace):
"""Delete all conntrack entries within namespace"""

View File

@ -1,120 +0,0 @@
# Copyright 2013 Dell Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import abc
import six
@six.add_metaclass(abc.ABCMeta)
class FwaasDriverBase(object):
"""Firewall as a Service Driver base class.
Using FwaasDriver Class, an instance of L3 perimeter Firewall
can be created. The firewall co-exists with the L3 agent.
One instance is created for each tenant. One firewall policy
is associated with each tenant (in the Havana release).
The Firewall can be visualized as having two zones (in Havana
release), trusted and untrusted.
All the 'internal' interfaces of Neutron Router is treated as trusted. The
interface connected to 'external network' is treated as untrusted.
The policy is applied on traffic ingressing/egressing interfaces on
the trusted zone. This implies that policy will be applied for traffic
passing from
- trusted to untrusted zones
- untrusted to trusted zones
- trusted to trusted zones
Policy WILL NOT be applied for traffic from untrusted to untrusted zones.
This is not a problem in Havana release as there is only one interface
connected to external network.
Since the policy is applied on the internal interfaces, the traffic
will be not be NATed to floating IP. For incoming traffic, the
traffic will get NATed to internal IP address before it hits
the firewall rules. So, while writing the rules, care should be
taken if using rules based on floating IP.
The firewall rule addition/deletion/insertion/update are done by the
management console. When the policy is sent to the driver, the complete
policy is sent and the whole policy has to be applied atomically. The
firewall rules will not get updated individually. This is to avoid problems
related to out-of-order notifications or inconsistent behaviour by partial
application of rules. Argument agent_mode indicates the l3 agent in DVR or
DVR_SNAT or LEGACY mode.
"""
# TODO(Margaret): Remove the first 3 methods and make the second three
# @abc.abstractmethod
def create_firewall(self, agent_mode, apply_list, firewall):
"""Create the Firewall with default (drop all) policy.
The default policy will be applied on all the interfaces of
trusted zone.
"""
pass
def delete_firewall(self, agent_mode, apply_list, firewall):
"""Delete firewall.
Removes all policies created by this instance and frees up
all the resources.
"""
pass
def update_firewall(self, agent_mode, apply_list, firewall):
"""Apply the policy on all trusted interfaces.
Remove previous policy and apply the new policy on all trusted
interfaces.
"""
pass
def create_firewall_group(self, agent_mode, apply_list, firewall):
"""Create the Firewall with default (drop all) policy.
The default policy will be applied on all the interfaces of
trusted zone.
"""
pass
def delete_firewall_group(self, agent_mode, apply_list, firewall):
"""Delete firewall.
Removes all policies created by this instance and frees up
all the resources.
"""
pass
def update_firewall_group(self, agent_mode, apply_list, firewall):
"""Apply the policy on all trusted interfaces.
Remove previous policy and apply the new policy on all trusted
interfaces.
"""
pass
@abc.abstractmethod
def apply_default_policy(self, agent_mode, apply_list, firewall):
"""Apply the default policy on all trusted interfaces.
Remove current policy and apply the default policy on all trusted
interfaces.
"""
pass

View File

@ -1,97 +0,0 @@
# Copyright (c) 2016
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import abc
import six
@six.add_metaclass(abc.ABCMeta)
class FwaasDriverBase(object):
"""Firewall as a Service Driver base class.
Using FwaasDriver Class, an instance of L3 perimeter Firewall
can be created. The firewall co-exists with the L3 agent.
One instance is created for each tenant. One firewall policy
is associated with each tenant (in the Havana release).
The Firewall can be visualized as having two zones (in Havana
release), trusted and untrusted.
All the 'internal' interfaces of Neutron Router is treated as trusted. The
interface connected to 'external network' is treated as untrusted.
The policy is applied on traffic ingressing/egressing interfaces on
the trusted zone. This implies that policy will be applied for traffic
passing from
- trusted to untrusted zones
- untrusted to trusted zones
- trusted to trusted zones
Policy WILL NOT be applied for traffic from untrusted to untrusted zones.
This is not a problem in Havana release as there is only one interface
connected to external network.
Since the policy is applied on the internal interfaces, the traffic
will be not be NATed to floating IP. For incoming traffic, the
traffic will get NATed to internal IP address before it hits
the firewall rules. So, while writing the rules, care should be
taken if using rules based on floating IP.
The firewall rule addition/deletion/insertion/update are done by the
management console. When the policy is sent to the driver, the complete
policy is sent and the whole policy has to be applied atomically. The
firewall rules will not get updated individually. This is to avoid problems
related to out-of-order notifications or inconsistent behaviour by partial
application of rules. Argument agent_mode indicates the l3 agent in DVR or
DVR_SNAT or LEGACY mode.
"""
@abc.abstractmethod
def create_firewall_group(self, agent_mode, apply_list, firewall):
"""Create the Firewall with default (drop all) policy.
The default policy will be applied on all the interfaces of
trusted zone.
"""
pass
@abc.abstractmethod
def delete_firewall_group(self, agent_mode, apply_list, firewall):
"""Delete firewall.
Removes all policies created by this instance and frees up
all the resources.
"""
pass
@abc.abstractmethod
def update_firewall_group(self, agent_mode, apply_list, firewall):
"""Apply the policy on all trusted interfaces.
Remove previous policy and apply the new policy on all trusted
interfaces.
"""
pass
@abc.abstractmethod
def apply_default_policy(self, agent_mode, apply_list, firewall):
"""Apply the default policy on all trusted interfaces.
Remove current policy and apply the default policy on all trusted
interfaces.
"""
pass

View File

@ -1,445 +0,0 @@
# Copyright 2013 Dell Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import excutils
from neutron.agent.linux import iptables_manager
from neutron.common import utils
from neutron_fwaas._i18n import _LE
from neutron_fwaas.common import fwaas_constants as f_const
from neutron_fwaas.services.firewall.drivers import fwaas_base
from neutron_lib.exceptions import firewall_v2 as f_exc
LOG = logging.getLogger(__name__)
FWAAS_DRIVER_NAME = 'Fwaas iptables driver'
FWAAS_DEFAULT_CHAIN = 'fwaas-default-policy'
FWAAS_TO_IPTABLE_ACTION_MAP = {'allow': 'ACCEPT',
'deny': 'DROP',
'reject': 'REJECT'}
INGRESS_DIRECTION = 'ingress'
EGRESS_DIRECTION = 'egress'
CHAIN_NAME_PREFIX = {INGRESS_DIRECTION: 'i',
EGRESS_DIRECTION: 'o'}
""" Firewall rules are applied on internal-interfaces of Neutron router.
The packets ingressing tenant's network will be on the output
direction on internal-interfaces.
"""
IPTABLES_DIR = {INGRESS_DIRECTION: '-o',
EGRESS_DIRECTION: '-i'}
IPV4 = 'ipv4'
IPV6 = 'ipv6'
IP_VER_TAG = {IPV4: 'v4',
IPV6: 'v6'}
INTERNAL_DEV_PREFIX = 'qr-'
SNAT_INT_DEV_PREFIX = 'sg-'
ROUTER_2_FIP_DEV_PREFIX = 'rfp-'
class IptablesFwaasDriver(fwaas_base.FwaasDriverBase):
"""IPTables driver for Firewall As A Service."""
def __init__(self):
LOG.debug("Initializing fwaas iptables driver")
self.pre_firewall = None
conntrack_cls = self._load_firewall_extension_driver(
'neutron_fwaas.services.firewall.drivers.linux',
cfg.CONF.fwaas.conntrack_driver)
self.conntrack = conntrack_cls()
self.conntrack.initialize()
@staticmethod
def _load_firewall_extension_driver(namespace, driver):
"""Loads driver using alias or class name
:param namespace: namespace where alias is defined
:param driver: driver alias or class name
:returns driver that is loaded
:raises ImportError if fails to load driver
"""
try:
return utils.load_class_by_alias_or_classname(namespace,
driver)
except ImportError:
with excutils.save_and_reraise_exception():
LOG.error(_LE("Driver '%s' not found."), driver)
def create_firewall(self, agent_mode, apply_list, firewall):
LOG.debug('Creating firewall %(fw_id)s for tenant %(tid)s',
{'fw_id': firewall['id'], 'tid': firewall['tenant_id']})
try:
if firewall['admin_state_up']:
self._setup_firewall(agent_mode, apply_list, firewall)
self._remove_conntrack_new_firewall(agent_mode,
apply_list, firewall)
self.pre_firewall = dict(firewall)
else:
self.apply_default_policy(agent_mode, apply_list, firewall)
except (LookupError, RuntimeError):
# catch known library exceptions and raise Fwaas generic exception
LOG.exception(_LE("Failed to create firewall: %s"), firewall['id'])
raise f_exc.FirewallInternalDriverError(driver=FWAAS_DRIVER_NAME)
def _get_ipt_mgrs_with_if_prefix(self, agent_mode, router_info):
"""Gets the iptables manager along with the if prefix to apply rules.
With DVR we can have differing namespaces depending on which agent
(on Network or Compute node). Also, there is an associated i/f for
each namespace. The iptables on the relevant namespace and matching
i/f are provided. On the Network node we could have both the snat
namespace and a fip so this is provided back as a list - so in that
scenario rules can be applied on both.
"""
if not router_info.router.get('distributed'):
return [{'ipt': router_info.iptables_manager,
'if_prefix': INTERNAL_DEV_PREFIX}]
ipt_mgrs = []
# TODO(sridar): refactor to get strings to a common location.
if agent_mode == 'dvr_snat':
if router_info.snat_iptables_manager:
ipt_mgrs.append({'ipt': router_info.snat_iptables_manager,
'if_prefix': SNAT_INT_DEV_PREFIX})
if router_info.dist_fip_count:
# handle the fip case on n/w or compute node.
ipt_mgrs.append({'ipt': router_info.iptables_manager,
'if_prefix': ROUTER_2_FIP_DEV_PREFIX})
return ipt_mgrs
def delete_firewall(self, agent_mode, apply_list, firewall):
LOG.debug('Deleting firewall %(fw_id)s for tenant %(tid)s',
{'fw_id': firewall['id'], 'tid': firewall['tenant_id']})
fwid = firewall['id']
try:
for router_info in apply_list:
ipt_if_prefix_list = self._get_ipt_mgrs_with_if_prefix(
agent_mode, router_info)
for ipt_if_prefix in ipt_if_prefix_list:
ipt_mgr = ipt_if_prefix['ipt']
self._remove_chains(fwid, ipt_mgr)
self._remove_default_chains(ipt_mgr)
# apply the changes immediately (no defer in firewall path)
ipt_mgr.defer_apply_off()
self.pre_firewall = None
except (LookupError, RuntimeError):
# catch known library exceptions and raise Fwaas generic exception
LOG.exception(_LE("Failed to delete firewall: %s"), fwid)
raise f_exc.FirewallInternalDriverError(driver=FWAAS_DRIVER_NAME)
def update_firewall(self, agent_mode, apply_list, firewall):
LOG.debug('Updating firewall %(fw_id)s for tenant %(tid)s',
{'fw_id': firewall['id'], 'tid': firewall['tenant_id']})
try:
if firewall['admin_state_up']:
self._setup_firewall(agent_mode, apply_list, firewall)
if self.pre_firewall:
self._remove_conntrack_updated_firewall(agent_mode,
apply_list, self.pre_firewall, firewall)
else:
self._remove_conntrack_new_firewall(agent_mode,
apply_list, firewall)
else:
self.apply_default_policy(agent_mode, apply_list, firewall)
self.pre_firewall = dict(firewall)
except (LookupError, RuntimeError):
# catch known library exceptions and raise Fwaas generic exception
LOG.exception(_LE("Failed to update firewall: %s"), firewall['id'])
raise f_exc.FirewallInternalDriverError(driver=FWAAS_DRIVER_NAME)
def apply_default_policy(self, agent_mode, apply_list, firewall):
LOG.debug('Applying firewall %(fw_id)s for tenant %(tid)s',
{'fw_id': firewall['id'], 'tid': firewall['tenant_id']})
fwid = firewall['id']
try:
for router_info in apply_list:
ipt_if_prefix_list = self._get_ipt_mgrs_with_if_prefix(
agent_mode, router_info)
for ipt_if_prefix in ipt_if_prefix_list:
# the following only updates local memory; no hole in FW
ipt_mgr = ipt_if_prefix['ipt']
self._remove_chains(fwid, ipt_mgr)
self._remove_default_chains(ipt_mgr)
# create default 'DROP ALL' policy chain
self._add_default_policy_chain_v4v6(ipt_mgr)
self._enable_policy_chain(fwid, ipt_if_prefix)
# apply the changes immediately (no defer in firewall path)
ipt_mgr.defer_apply_off()
except (LookupError, RuntimeError):
# catch known library exceptions and raise Fwaas generic exception
LOG.exception(
_LE("Failed to apply default policy on firewall: %s"), fwid)
raise f_exc.FirewallInternalDriverError(driver=FWAAS_DRIVER_NAME)
def _setup_firewall(self, agent_mode, apply_list, firewall):
fwid = firewall['id']
for router_info in apply_list:
ipt_if_prefix_list = self._get_ipt_mgrs_with_if_prefix(
agent_mode, router_info)
for ipt_if_prefix in ipt_if_prefix_list:
ipt_mgr = ipt_if_prefix['ipt']
# the following only updates local memory; no hole in FW
self._remove_chains(fwid, ipt_mgr)
self._remove_default_chains(ipt_mgr)
# create default 'DROP ALL' policy chain
self._add_default_policy_chain_v4v6(ipt_mgr)
#create chain based on configured policy
self._setup_chains(firewall, ipt_if_prefix)
# apply the changes immediately (no defer in firewall path)
ipt_mgr.defer_apply_off()
def _get_chain_name(self, fwid, ver, direction):
return '%s%s%s' % (CHAIN_NAME_PREFIX[direction],
IP_VER_TAG[ver],
fwid)
def _setup_chains(self, firewall, ipt_if_prefix):
"""Create Fwaas chain using the rules in the policy
"""
fw_rules_list = firewall['firewall_rule_list']
fwid = firewall['id']
ipt_mgr = ipt_if_prefix['ipt']
#default rules for invalid packets and established sessions
invalid_rule = self._drop_invalid_packets_rule()
est_rule = self._allow_established_rule()
for ver in [IPV4, IPV6]:
if ver == IPV4:
table = ipt_mgr.ipv4['filter']
else:
table = ipt_mgr.ipv6['filter']
ichain_name = self._get_chain_name(fwid, ver, INGRESS_DIRECTION)
ochain_name = self._get_chain_name(fwid, ver, EGRESS_DIRECTION)
for name in [ichain_name, ochain_name]:
table.add_chain(name)
table.add_rule(name, invalid_rule)
table.add_rule(name, est_rule)
for rule in fw_rules_list:
if not rule['enabled']:
continue
iptbl_rule = self._convert_fwaas_to_iptables_rule(rule)
if rule['ip_version'] == 4:
ver = IPV4
table = ipt_mgr.ipv4['filter']
else:
ver = IPV6
table = ipt_mgr.ipv6['filter']
ichain_name = self._get_chain_name(fwid, ver, INGRESS_DIRECTION)
ochain_name = self._get_chain_name(fwid, ver, EGRESS_DIRECTION)
table.add_rule(ichain_name, iptbl_rule)
table.add_rule(ochain_name, iptbl_rule)
self._enable_policy_chain(fwid, ipt_if_prefix)
def _find_changed_rules(self, pre_firewall, firewall):
"""Find the rules changed between the current firewall
and the updating rule
"""
changed_rules = []
fw_rules_list = firewall[f_const.FIREWALL_RULE_LIST]
pre_fw_rules_list = pre_firewall[f_const.FIREWALL_RULE_LIST]
for pre_fw_rule in pre_fw_rules_list:
for fw_rule in fw_rules_list:
if (pre_fw_rule.get('id') == fw_rule.get('id') and
pre_fw_rule != fw_rule):
changed_rules.append(pre_fw_rule)
changed_rules.append(fw_rule)
return changed_rules
def _find_removed_rules(self, pre_firewall, firewall):
fw_rules_list = firewall[f_const.FIREWALL_RULE_LIST]
pre_fw_rules_list = pre_firewall[f_const.FIREWALL_RULE_LIST]
fw_rule_ids = [fw_rule.get('id') for fw_rule in fw_rules_list]
removed_rules = [pre_fw_rule for pre_fw_rule in
pre_fw_rules_list if pre_fw_rule.get('id') not in fw_rule_ids]
return removed_rules
def _find_new_rules(self, pre_firewall, firewall):
return self._find_removed_rules(firewall, pre_firewall)
def _remove_conntrack_new_firewall(self, agent_mode, apply_list, firewall):
"""Remove conntrack when create new firewall"""
routers_list = list(set(apply_list))
for router_info in routers_list:
ipt_if_prefix_list = self._get_ipt_mgrs_with_if_prefix(
agent_mode, router_info)
for ipt_if_prefix in ipt_if_prefix_list:
ipt_mgr = ipt_if_prefix['ipt']
self.conntrack.flush_entries(ipt_mgr.namespace)
def _remove_conntrack_updated_firewall(self, agent_mode,
apply_list, pre_firewall, firewall):
"""Remove conntrack when updated firewall"""
router_list = list(set(apply_list))
for router_info in router_list:
ipt_if_prefix_list = self._get_ipt_mgrs_with_if_prefix(
agent_mode, router_info)
for ipt_if_prefix in ipt_if_prefix_list:
ipt_mgr = ipt_if_prefix['ipt']
ch_rules = self._find_changed_rules(pre_firewall,
firewall)
i_rules = self._find_new_rules(pre_firewall, firewall)
r_rules = self._find_removed_rules(pre_firewall, firewall)
removed_conntrack_rules_list = ch_rules + i_rules + r_rules
self.conntrack.delete_entries(removed_conntrack_rules_list,
ipt_mgr.namespace)
def _remove_default_chains(self, nsid):
"""Remove fwaas default policy chain."""
self._remove_chain_by_name(IPV4, FWAAS_DEFAULT_CHAIN, nsid)
self._remove_chain_by_name(IPV6, FWAAS_DEFAULT_CHAIN, nsid)
def _remove_chains(self, fwid, ipt_mgr):
"""Remove fwaas policy chain."""
for ver in [IPV4, IPV6]:
for direction in [INGRESS_DIRECTION, EGRESS_DIRECTION]:
chain_name = self._get_chain_name(fwid, ver, direction)
self._remove_chain_by_name(ver, chain_name, ipt_mgr)
def _add_default_policy_chain_v4v6(self, ipt_mgr):
ipt_mgr.ipv4['filter'].add_chain(FWAAS_DEFAULT_CHAIN)
ipt_mgr.ipv4['filter'].add_rule(FWAAS_DEFAULT_CHAIN, '-j DROP')
ipt_mgr.ipv6['filter'].add_chain(FWAAS_DEFAULT_CHAIN)
ipt_mgr.ipv6['filter'].add_rule(FWAAS_DEFAULT_CHAIN, '-j DROP')
def _remove_chain_by_name(self, ver, chain_name, ipt_mgr):
if ver == IPV4:
ipt_mgr.ipv4['filter'].remove_chain(chain_name)
else:
ipt_mgr.ipv6['filter'].remove_chain(chain_name)
def _add_rules_to_chain(self, ipt_mgr, ver, chain_name, rules):
if ver == IPV4:
table = ipt_mgr.ipv4['filter']
else:
table = ipt_mgr.ipv6['filter']
for rule in rules:
table.add_rule(chain_name, rule)
def _enable_policy_chain(self, fwid, ipt_if_prefix):
bname = iptables_manager.binary_name
ipt_mgr = ipt_if_prefix['ipt']
if_prefix = ipt_if_prefix['if_prefix']
for (ver, tbl) in [(IPV4, ipt_mgr.ipv4['filter']),
(IPV6, ipt_mgr.ipv6['filter'])]:
for direction in [INGRESS_DIRECTION, EGRESS_DIRECTION]:
chain_name = self._get_chain_name(fwid, ver, direction)
chain_name = iptables_manager.get_chain_name(chain_name)
if chain_name in tbl.chains:
jump_rule = ['%s %s+ -j %s-%s' % (IPTABLES_DIR[direction],
if_prefix, bname, chain_name)]
self._add_rules_to_chain(ipt_mgr,
ver, 'FORWARD', jump_rule)
#jump to DROP_ALL policy
chain_name = iptables_manager.get_chain_name(FWAAS_DEFAULT_CHAIN)
jump_rule = ['-o %s+ -j %s-%s' % (if_prefix, bname, chain_name)]
self._add_rules_to_chain(ipt_mgr, IPV4, 'FORWARD', jump_rule)
self._add_rules_to_chain(ipt_mgr, IPV6, 'FORWARD', jump_rule)
#jump to DROP_ALL policy
chain_name = iptables_manager.get_chain_name(FWAAS_DEFAULT_CHAIN)
jump_rule = ['-i %s+ -j %s-%s' % (if_prefix, bname, chain_name)]
self._add_rules_to_chain(ipt_mgr, IPV4, 'FORWARD', jump_rule)
self._add_rules_to_chain(ipt_mgr, IPV6, 'FORWARD', jump_rule)
def _convert_fwaas_to_iptables_rule(self, rule):
action = FWAAS_TO_IPTABLE_ACTION_MAP[rule.get('action')]
# Output ordering is important here as it must exactly match what
# is returned by iptables-save. If not we risk unnecessarily removing
# and readding rules.
args = []
args += self._protocol_arg(rule.get('protocol'))
args += self._ip_prefix_arg('s', rule.get('source_ip_address'))
args += self._ip_prefix_arg('d', rule.get('destination_ip_address'))
# iptables adds '-m protocol' when any source
# or destination port number is specified
if not((rule.get('source_port') is None)
and (rule.get('destination_port') is None)):
args += self._match_arg(rule.get('protocol'))
args += self._port_arg('sport',
rule.get('protocol'),
rule.get('source_port'))
args += self._port_arg('dport',
rule.get('protocol'),
rule.get('destination_port'))
args += self._action_arg(action)
iptables_rule = ' '.join(args)
return iptables_rule
def _drop_invalid_packets_rule(self):
return '-m state --state INVALID -j DROP'
def _allow_established_rule(self):
return '-m state --state RELATED,ESTABLISHED -j ACCEPT'
def _action_arg(self, action):
if not action:
return []
args = ['-j', action]
return args
def _protocol_arg(self, protocol):
if not protocol:
return []
args = ['-p', protocol]
return args
def _match_arg(self, protocol):
if not protocol:
return []
protocol_modules = {'udp': 'udp', 'tcp': 'tcp',
'icmp': 'icmp', 'ipv6-icmp': 'icmp6'}
# iptables adds '-m protocol' when the port number is specified
args = ['-m', protocol_modules[protocol]]
return args
def _port_arg(self, direction, protocol, port):
if (protocol not in ['udp', 'tcp']
or port is None):
return []
args = ['--%s' % direction, '%s' % port]
return args
def _ip_prefix_arg(self, direction, ip_prefix):
if not(ip_prefix):
return []
args = ['-%s' % direction, '%s' % utils.ip_to_cidr(ip_prefix)]
return args

View File

@ -1,499 +0,0 @@
# Copyright (c) 2016
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from neutron.agent.linux import iptables_manager
from neutron.agent.linux import utils as linux_utils
from oslo_log import log as logging
from neutron.common import utils
from neutron_fwaas._i18n import _LE
from neutron_fwaas.extensions import firewall as fw_ext
from neutron_fwaas.services.firewall.drivers import fwaas_base_v2
LOG = logging.getLogger(__name__)
FWAAS_DRIVER_NAME = 'Fwaas iptables driver'
FWAAS_DEFAULT_CHAIN = 'fwaas-default-policy'
FWAAS_TO_IPTABLE_ACTION_MAP = {'allow': 'ACCEPT',
'deny': 'DROP',
'reject': 'REJECT'}
INGRESS_DIRECTION = 'ingress'
EGRESS_DIRECTION = 'egress'
CHAIN_NAME_PREFIX = {INGRESS_DIRECTION: 'i',
EGRESS_DIRECTION: 'o'}
""" Firewall rules are applied on internal-interfaces of Neutron router.
The packets ingressing tenant's network will be on the output
direction on internal-interfaces.
"""
IPTABLES_DIR = {INGRESS_DIRECTION: '-o',
EGRESS_DIRECTION: '-i'}
IPV4 = 'ipv4'
IPV6 = 'ipv6'
IP_VER_TAG = {IPV4: 'v4',
IPV6: 'v6'}
INTERNAL_DEV_PREFIX = 'qr-'
SNAT_INT_DEV_PREFIX = 'sg-'
ROUTER_2_FIP_DEV_PREFIX = 'rfp-'
MAX_INTF_NAME_LEN = 14
class IptablesFwaasDriver(fwaas_base_v2.FwaasDriverBase):
"""IPTables driver for Firewall As A Service."""
def __init__(self):
LOG.debug("Initializing fwaas iptables driver")
self.pre_firewall = None
def initialize(self):
pass
def _get_intf_name(self, if_prefix, port_id):
_name = "%s%s" % (if_prefix, port_id)
return _name[:MAX_INTF_NAME_LEN]
def create_firewall_group(self, agent_mode, apply_list, firewall):
LOG.debug('Creating firewall %(fw_id)s for tenant %(tid)s',
{'fw_id': firewall['id'], 'tid': firewall['tenant_id']})
try:
if firewall['admin_state_up']:
self._setup_firewall(agent_mode, apply_list, firewall)
self._remove_conntrack_new_firewall(agent_mode,
apply_list, firewall)
self.pre_firewall = dict(firewall)
else:
self.apply_default_policy(agent_mode, apply_list, firewall)
except (LookupError, RuntimeError):
# catch known library exceptions and raise Fwaas generic exception
LOG.exception(_LE("Failed to create firewall: %s"), firewall['id'])
raise fw_ext.FirewallInternalDriverError(driver=FWAAS_DRIVER_NAME)
def _get_ipt_mgrs_with_if_prefix(self, agent_mode, ri):
"""Gets the iptables manager along with the if prefix to apply rules.
With DVR we can have differing namespaces depending on which agent
(on Network or Compute node). Also, there is an associated i/f for
each namespace. The iptables on the relevant namespace and matching
i/f are provided. On the Network node we could have both the snat
namespace and a fip so this is provided back as a list - so in that
scenario rules can be applied on both.
"""
if not ri.router.get('distributed'):
return [{'ipt': ri.iptables_manager,
'if_prefix': INTERNAL_DEV_PREFIX}]
ipt_mgrs = []
# TODO(sridar): refactor to get strings to a common location.
if agent_mode == 'dvr_snat':
if ri.snat_iptables_manager:
ipt_mgrs.append({'ipt': ri.snat_iptables_manager,
'if_prefix': SNAT_INT_DEV_PREFIX})
if ri.dist_fip_count:
# handle the fip case on n/w or compute node.
ipt_mgrs.append({'ipt': ri.iptables_manager,
'if_prefix': ROUTER_2_FIP_DEV_PREFIX})
return ipt_mgrs
def delete_firewall_group(self, agent_mode, apply_list, firewall):
LOG.debug('Deleting firewall %(fw_id)s for tenant %(tid)s',
{'fw_id': firewall['id'], 'tid': firewall['tenant_id']})
fwid = firewall['id']
try:
for ri, router_fw_ports in apply_list:
ipt_if_prefix_list = self._get_ipt_mgrs_with_if_prefix(
agent_mode, ri)
for ipt_if_prefix in ipt_if_prefix_list:
ipt_mgr = ipt_if_prefix['ipt']
self._remove_chains(fwid, ipt_mgr)
self._remove_default_chains(ipt_mgr)
# apply the changes immediately (no defer in firewall path)
ipt_mgr.defer_apply_off()
self.pre_firewall = None
except (LookupError, RuntimeError):
# catch known library exceptions and raise Fwaas generic exception
LOG.exception(_LE("Failed to delete firewall: %s"), fwid)
raise fw_ext.FirewallInternalDriverError(driver=FWAAS_DRIVER_NAME)
def update_firewall_group(self, agent_mode, apply_list, firewall):
LOG.debug('Updating firewall %(fw_id)s for tenant %(tid)s',
{'fw_id': firewall['id'], 'tid': firewall['tenant_id']})
try:
if firewall['admin_state_up']:
self._setup_firewall(agent_mode, apply_list, firewall)
if self.pre_firewall:
self._remove_conntrack_updated_firewall(agent_mode,
apply_list, self.pre_firewall, firewall)
else:
self._remove_conntrack_new_firewall(agent_mode,
apply_list, firewall)
else:
self.apply_default_policy(agent_mode, apply_list, firewall)
self.pre_firewall = dict(firewall)
except (LookupError, RuntimeError):
# catch known library exceptions and raise Fwaas generic exception
LOG.exception(_LE("Failed to update firewall: %s"), firewall['id'])
raise fw_ext.FirewallInternalDriverError(driver=FWAAS_DRIVER_NAME)
def apply_default_policy(self, agent_mode, apply_list, firewall):
LOG.debug('Applying firewall %(fw_id)s for tenant %(tid)s',
{'fw_id': firewall['id'], 'tid': firewall['tenant_id']})
fwid = firewall['id']
try:
for ri, router_fw_ports in apply_list:
ipt_if_prefix_list = self._get_ipt_mgrs_with_if_prefix(
agent_mode, ri)
for ipt_if_prefix in ipt_if_prefix_list:
# the following only updates local memory; no hole in FW
ipt_mgr = ipt_if_prefix['ipt']
self._remove_chains(fwid, ipt_mgr)
self._remove_default_chains(ipt_mgr)
# create default 'DROP ALL' policy chain
self._add_default_policy_chain_v4v6(ipt_mgr)
self._enable_policy_chain(fwid, ipt_if_prefix,
router_fw_ports)
# apply the changes immediately (no defer in firewall path)
ipt_mgr.defer_apply_off()
except (LookupError, RuntimeError):
# catch known library exceptions and raise Fwaas generic exception
LOG.exception(
_LE("Failed to apply default policy on firewall: %s"), fwid)
raise fw_ext.FirewallInternalDriverError(driver=FWAAS_DRIVER_NAME)
def _setup_firewall(self, agent_mode, apply_list, firewall):
fwid = firewall['id']
for ri, router_fw_ports in apply_list:
ipt_if_prefix_list = self._get_ipt_mgrs_with_if_prefix(
agent_mode, ri)
for ipt_if_prefix in ipt_if_prefix_list:
ipt_mgr = ipt_if_prefix['ipt']
# the following only updates local memory; no hole in FW
self._remove_chains(fwid, ipt_mgr)
self._remove_default_chains(ipt_mgr)
# create default 'DROP ALL' policy chain
self._add_default_policy_chain_v4v6(ipt_mgr)
# create chain based on configured policy
self._setup_chains(firewall, ipt_if_prefix, router_fw_ports)
# apply the changes immediately (no defer in firewall path)
ipt_mgr.defer_apply_off()
def _get_chain_name(self, fwid, ver, direction):
return '%s%s%s' % (CHAIN_NAME_PREFIX[direction],
IP_VER_TAG[ver],
fwid)
def _setup_chains(self, firewall, ipt_if_prefix, router_fw_ports):
"""Create Fwaas chain using the rules in the policy
"""
egress_rule_list = firewall['egress_rule_list']
ingress_rule_list = firewall['ingress_rule_list']
fwid = firewall['id']
ipt_mgr = ipt_if_prefix['ipt']
# default rules for invalid packets and established sessions
invalid_rule = self._drop_invalid_packets_rule()
est_rule = self._allow_established_rule()
for ver in [IPV4, IPV6]:
if ver == IPV4:
table = ipt_mgr.ipv4['filter']
else:
table = ipt_mgr.ipv6['filter']
ichain_name = self._get_chain_name(fwid, ver, INGRESS_DIRECTION)
ochain_name = self._get_chain_name(fwid, ver, EGRESS_DIRECTION)
for name in [ichain_name, ochain_name]:
table.add_chain(name)
table.add_rule(name, invalid_rule)
table.add_rule(name, est_rule)
for rule in ingress_rule_list:
if not rule['enabled']:
continue
iptbl_rule = self._convert_fwaas_to_iptables_rule(rule)
if rule['ip_version'] == 4:
ver = IPV4
table = ipt_mgr.ipv4['filter']
else:
ver = IPV6
table = ipt_mgr.ipv6['filter']
ichain_name = self._get_chain_name(fwid, ver, INGRESS_DIRECTION)
table.add_rule(ichain_name, iptbl_rule)
for rule in egress_rule_list:
if not rule['enabled']:
continue
iptbl_rule = self._convert_fwaas_to_iptables_rule(rule)
if rule['ip_version'] == 4:
ver = IPV4
table = ipt_mgr.ipv4['filter']
else:
ver = IPV6
table = ipt_mgr.ipv6['filter']
ochain_name = self._get_chain_name(fwid, ver, EGRESS_DIRECTION)
table.add_rule(ochain_name, iptbl_rule)
self._enable_policy_chain(fwid, ipt_if_prefix, router_fw_ports)
def _find_changed_rules(self, pre_firewall, firewall):
"""Find the rules changed between the current firewall
and the updating rule
"""
changed_rules = []
for fw_rule_list in ['egress_rule_list', 'ingress_rule_list']:
pre_fw_rules = pre_firewall[fw_rule_list]
fw_rules = firewall[fw_rule_list]
for pre_fw_rule in pre_fw_rules:
for fw_rule in fw_rules:
if (pre_fw_rule.get('id') == fw_rule.get('id') and
pre_fw_rule != fw_rule):
changed_rules.append(pre_fw_rule)
changed_rules.append(fw_rule)
return changed_rules
def _find_removed_rules(self, pre_firewall, firewall):
removed_rules = []
for fw_rule_list in ['egress_rule_list', 'ingress_rule_list']:
pre_fw_rules = pre_firewall[fw_rule_list]
fw_rules = firewall[fw_rule_list]
fw_rule_ids = [fw_rule['id'] for fw_rule in fw_rules]
removed_rules.extend([pre_fw_rule for pre_fw_rule in pre_fw_rules
if pre_fw_rule['id'] not in fw_rule_ids])
return removed_rules
def _find_new_rules(self, pre_firewall, firewall):
return self._find_removed_rules(firewall, pre_firewall)
def _get_conntrack_cmd_from_rule(self, ipt_mgr, rule=None):
prefixcmd = ['ip', 'netns', 'exec'] + [ipt_mgr.namespace]
cmd = ['conntrack', '-D']
if rule:
conntrack_filter = self._get_conntrack_filter_from_rule(rule)
exec_cmd = prefixcmd + cmd + conntrack_filter
else:
exec_cmd = prefixcmd + cmd
return exec_cmd
def _remove_conntrack_by_cmd(self, cmd):
if cmd:
try:
linux_utils.execute(cmd, run_as_root=True,
check_exit_code=True,
extra_ok_codes=[1])
except RuntimeError:
LOG.exception(
_LE("Failed execute conntrack command %s"), str(cmd))
def _remove_conntrack_new_firewall(self, agent_mode, apply_list, firewall):
"""Remove conntrack when create new firewall"""
routers_list = list(set([apply_info[0] for apply_info in apply_list]))
for ri in routers_list:
ipt_if_prefix_list = self._get_ipt_mgrs_with_if_prefix(
agent_mode, ri)
for ipt_if_prefix in ipt_if_prefix_list:
ipt_mgr = ipt_if_prefix['ipt']
cmd = self._get_conntrack_cmd_from_rule(ipt_mgr)
self._remove_conntrack_by_cmd(cmd)
def _remove_conntrack_updated_firewall(self, agent_mode,
apply_list, pre_firewall, firewall):
"""Remove conntrack when updated firewall"""
routers_list = list(set([apply_info[0] for apply_info in apply_list]))
for ri in routers_list:
ipt_if_prefix_list = self._get_ipt_mgrs_with_if_prefix(
agent_mode, ri)
for ipt_if_prefix in ipt_if_prefix_list:
ipt_mgr = ipt_if_prefix['ipt']
ch_rules = self._find_changed_rules(pre_firewall,
firewall)
i_rules = self._find_new_rules(pre_firewall, firewall)
r_rules = self._find_removed_rules(pre_firewall, firewall)
removed_conntrack_rules_list = ch_rules + i_rules + r_rules
for rule in removed_conntrack_rules_list:
cmd = self._get_conntrack_cmd_from_rule(ipt_mgr, rule)
self._remove_conntrack_by_cmd(cmd)
def _get_conntrack_filter_from_rule(self, rule):
"""Get conntrack filter from rule.
The key for get conntrack filter is protocol, destination_port
and source_port. If we want to take more keys, add to the list.
"""
conntrack_filter = []
keys = [['-p', 'protocol'], ['-f', 'ip_version'],
['--dport', 'destination_port'], ['--sport', 'source_port']]
for key in keys:
if rule.get(key[1]):
if key[1] == 'ip_version':
conntrack_filter.append(key[0])
conntrack_filter.append('ipv' + str(rule.get(key[1])))
else:
conntrack_filter.append(key[0])
conntrack_filter.append(rule.get(key[1]))
return conntrack_filter
def _remove_default_chains(self, nsid):
"""Remove fwaas default policy chain."""
self._remove_chain_by_name(IPV4, FWAAS_DEFAULT_CHAIN, nsid)
self._remove_chain_by_name(IPV6, FWAAS_DEFAULT_CHAIN, nsid)
def _remove_chains(self, fwid, ipt_mgr):
"""Remove fwaas policy chain."""
for ver in [IPV4, IPV6]:
for direction in [INGRESS_DIRECTION, EGRESS_DIRECTION]:
chain_name = self._get_chain_name(fwid, ver, direction)
self._remove_chain_by_name(ver, chain_name, ipt_mgr)
def _add_default_policy_chain_v4v6(self, ipt_mgr):
ipt_mgr.ipv4['filter'].add_chain(FWAAS_DEFAULT_CHAIN)
ipt_mgr.ipv4['filter'].add_rule(FWAAS_DEFAULT_CHAIN, '-j DROP')
ipt_mgr.ipv6['filter'].add_chain(FWAAS_DEFAULT_CHAIN)
ipt_mgr.ipv6['filter'].add_rule(FWAAS_DEFAULT_CHAIN, '-j DROP')
def _remove_chain_by_name(self, ver, chain_name, ipt_mgr):
if ver == IPV4:
ipt_mgr.ipv4['filter'].remove_chain(chain_name)
else:
ipt_mgr.ipv6['filter'].remove_chain(chain_name)
def _add_rules_to_chain(self, ipt_mgr, ver, chain_name, rules):
if ver == IPV4:
table = ipt_mgr.ipv4['filter']
else:
table = ipt_mgr.ipv6['filter']
for rule in rules:
table.add_rule(chain_name, rule)
def _enable_policy_chain(self, fwid, ipt_if_prefix, router_fw_ports):
bname = iptables_manager.binary_name
ipt_mgr = ipt_if_prefix['ipt']
if_prefix = ipt_if_prefix['if_prefix']
for (ver, tbl) in [(IPV4, ipt_mgr.ipv4['filter']),
(IPV6, ipt_mgr.ipv6['filter'])]:
for direction in [INGRESS_DIRECTION, EGRESS_DIRECTION]:
chain_name = self._get_chain_name(fwid, ver, direction)
chain_name = iptables_manager.get_chain_name(chain_name)
if chain_name in tbl.chains:
for router_fw_port in router_fw_ports:
intf_name = self._get_intf_name(if_prefix,
router_fw_port)
jump_rule = ['%s %s -j %s-%s' % (
IPTABLES_DIR[direction], intf_name,
bname, chain_name)]
self._add_rules_to_chain(ipt_mgr, ver,
'FORWARD', jump_rule)
# jump to DROP_ALL policy
chain_name = iptables_manager.get_chain_name(FWAAS_DEFAULT_CHAIN)
for router_fw_port in router_fw_ports:
intf_name = self._get_intf_name(if_prefix,
router_fw_port)
jump_rule = ['-o %s -j %s-%s' % (intf_name, bname, chain_name)]
self._add_rules_to_chain(ipt_mgr, IPV4, 'FORWARD', jump_rule)
self._add_rules_to_chain(ipt_mgr, IPV6, 'FORWARD', jump_rule)
# jump to DROP_ALL policy
chain_name = iptables_manager.get_chain_name(FWAAS_DEFAULT_CHAIN)
for router_fw_port in router_fw_ports:
intf_name = self._get_intf_name(if_prefix,
router_fw_port)
jump_rule = ['-i %s -j %s-%s' % (intf_name, bname, chain_name)]
self._add_rules_to_chain(ipt_mgr, IPV4, 'FORWARD', jump_rule)
self._add_rules_to_chain(ipt_mgr, IPV6, 'FORWARD', jump_rule)
def _convert_fwaas_to_iptables_rule(self, rule):
action = FWAAS_TO_IPTABLE_ACTION_MAP[rule.get('action')]
# Output ordering is important here as it must exactly match what
# is returned by iptables-save. If not we risk unnecessarily removing
# and readding rules.
args = []
args += self._protocol_arg(rule.get('protocol'))
args += self._ip_prefix_arg('s', rule.get('source_ip_address'))
args += self._ip_prefix_arg('d', rule.get('destination_ip_address'))
# iptables adds '-m protocol' when any source
# or destination port number is specified
if not((rule.get('source_port') is None)
and (rule.get('destination_port') is None)):
args += self._match_arg(rule.get('protocol'))
args += self._port_arg('sport',
rule.get('protocol'),
rule.get('source_port'))
args += self._port_arg('dport',
rule.get('protocol'),
rule.get('destination_port'))
args += self._action_arg(action)
iptables_rule = ' '.join(args)
return iptables_rule
def _drop_invalid_packets_rule(self):
return '-m state --state INVALID -j DROP'
def _allow_established_rule(self):
return '-m state --state RELATED,ESTABLISHED -j ACCEPT'
def _action_arg(self, action):
if not action:
return []
args = ['-j', action]
return args
def _protocol_arg(self, protocol):
if not protocol:
return []
args = ['-p', protocol]
return args
def _match_arg(self, protocol):
if not protocol:
return []
protocol_modules = {'udp': 'udp', 'tcp': 'tcp',
'icmp': 'icmp', 'ipv6-icmp': 'icmp6'}
# iptables adds '-m protocol' when the port number is specified
args = ['-m', protocol_modules[protocol]]
return args
def _port_arg(self, direction, protocol, port):
if (protocol not in ['udp', 'tcp']
or port is None):
return []
args = ['--%s' % direction, '%s' % port]
return args
def _ip_prefix_arg(self, direction, ip_prefix):
if not(ip_prefix):
return []
args = ['-%s' % direction, '%s' % utils.ip_to_cidr(ip_prefix)]
return args

View File

@ -1,81 +0,0 @@
# Copyright (c) 2017 Fujitsu Limited
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_log import log as logging
from neutron.agent.linux import utils as linux_utils
from neutron_fwaas._i18n import _
from neutron_fwaas.services.firewall.drivers import conntrack_base
LOG = logging.getLogger(__name__)
class ConntrackLegacy(conntrack_base.ConntrackDriverBase):
def initialize(self, execute=None):
LOG.debug('Initialize Conntrack Legacy')
self.execute = execute or linux_utils.execute
def flush_entries(self, namespace):
prefixcmd = ['ip', 'netns', 'exec', namespace] if namespace else []
cmd = prefixcmd + ['conntrack', '-D']
self._execute_command(cmd)
def delete_entries(self, rules, namespace):
for rule in rules:
cmd = self._get_conntrack_cmd_from_rule(rule, namespace)
self._execute_command(cmd)
def _execute_command(self, cmd):
try:
self.execute(cmd,
run_as_root=True,
check_exit_code=True,
extra_ok_codes=[1])
except RuntimeError:
msg = _("Failed execute conntrack command %s") % cmd
raise RuntimeError(msg)
def _get_conntrack_cmd_from_rule(self, rule, namespace):
prefixcmd = (['ip', 'netns', 'exec', namespace]
if namespace else [])
cmd = ['conntrack', '-D']
if rule:
conntrack_filter = self._get_conntrack_filter_from_rule(rule)
exec_cmd = prefixcmd + cmd + conntrack_filter
else:
exec_cmd = prefixcmd + cmd
return exec_cmd
def _get_conntrack_filter_from_rule(self, rule):
"""Get conntrack filter from rule
The key for get conntrack filter is protocol, destination_port
and source_port. If we want to take more keys, add to the list.
"""
conntrack_filter = []
keys = [['-p', 'protocol'], ['-f', 'ip_version'],
['--dport', 'destination_port'], ['--sport', 'source_port']]
for arg_key, rule_key in keys:
val = rule.get(rule_key)
if val:
if rule_key == 'ip_version':
conntrack_filter.append(arg_key)
conntrack_filter.append('ipv' + str(val))
else:
conntrack_filter.append(arg_key)
conntrack_filter.append(val)
return conntrack_filter

View File

@ -1,137 +0,0 @@
# Copyright (c) 2017 Fujitsu Limited
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_log import log as logging
from neutron_fwaas.privileged import netlink_lib as nl_lib
from neutron_fwaas.services.firewall.drivers import conntrack_base
LOG = logging.getLogger(__name__)
class ConntrackNetlink(conntrack_base.ConntrackDriverBase):
def initialize(self, *args, **kwargs):
LOG.debug('Conntrack Netlink loaded')
def flush_entries(self, namespace):
"""Flush all conntrack entries within the namespace
:param namespace: namespace to flush
:return: None
"""
nl_lib.flush_entries(namespace)
def delete_entries(self, rules, namespace):
rule_filters = (self._get_filter_from_rule(r) for r in rules)
rule_filters = sorted(rule_filters)
entries = nl_lib.list_entries(namespace)
delete_entries = self._get_entries_to_delete(rule_filters, entries)
if delete_entries:
nl_lib.delete_entries(delete_entries, namespace)
def _get_entries_to_delete(self, rule_filters, entries):
"""Specify conntrack entries to delete
:param rule_filters: List of filters parsed from firewall rules
:param entries: all entries within namespace
:return: conntrack entries to delete
"""
# List all entries from namespace, they are already parsed
# to a list of tuples:
# [(4, 'icmp', 8, 0, '1.1.1.1', '2.2.2.2', 1234),
# (4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2')]
delete_entries = []
entry_index = 0
entry_number = len(entries)
for rule_filter in rule_filters:
while entry_index < entry_number:
# Compare entry with rule
comp = self._compare_entry_and_rule(rule_filter,
entries[entry_index])
# Increase entry_index when entry is under rule
if comp < 0:
entry_index += 1
# Append entry to delete_entry if it matches with rule
elif comp == 0:
delete_entries.append(entries[entry_index])
entry_index += 1
# Switch to new higher rule
else:
break
return delete_entries
@staticmethod
def _get_filter_from_rule(rule):
"""Parse the firewall rule to a tuple
:param rule: firewall rule
:return: a tuple of parsed information
"""
rule_filter = []
keys = ['ip_version', 'protocol',
'source_port', 'destination_port',
'source_ip_address', 'destination_ip_address']
for key in keys:
if key in ['source_port', 'destination_port']:
port_range = rule.get(key, [])
if port_range:
port_lower, sep, port_upper = port_range.partition(':')
port_upper = port_upper if sep else port_lower
port_range = [port_lower, port_upper]
rule_filter.append(port_range or [])
else:
rule_filter.append(rule.get(key, []))
return tuple(rule_filter)
@staticmethod
def _compare_entry_and_rule(rule_filter, entry):
"""Define that the entry should be deleted or not
:param rule_filter: filter that is parsed from a firewall rule
ex: (4, 'tcp', 1, 2)
:param entry: parsed conntrack entry,
ex: (4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2')
:return: -1 if entry is lower than rule, 0 if entry matches rule,
1 if entry is higher than rule
"""
ENTRY_IS_LOWER = -1
ENTRY_MATCHES = 0
ENTRY_IS_HIGHER = 1
rule_ipversion = rule_filter[0]
if entry[0] < rule_ipversion:
return ENTRY_IS_LOWER
elif entry[0] > rule_ipversion:
return ENTRY_IS_HIGHER
rule_protocol = rule_filter[1]
if rule_protocol:
if entry[1] < rule_protocol:
return ENTRY_IS_LOWER
elif entry[1] > rule_protocol:
return ENTRY_IS_HIGHER
sport_range = rule_filter[2]
if sport_range:
sport_range = [int(port) for port in sport_range]
if entry[2] < min(sport_range[0], sport_range[-1]):
return ENTRY_IS_LOWER
elif entry[2] > max(sport_range[0], sport_range[-1]):
return ENTRY_IS_HIGHER
dport_range = rule_filter[3]
if dport_range:
dport_range = [int(port) for port in dport_range]
if entry[3] < min(dport_range[0], dport_range[-1]):
return ENTRY_IS_LOWER
elif entry[3] > max(dport_range[0], dport_range[-1]):
return ENTRY_IS_HIGHER
return ENTRY_MATCHES

View File

@ -1,443 +0,0 @@
# 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.
from neutron_lib import constants as nl_constants
from neutron_lib import context as neutron_context
from neutron_lib.exceptions import firewall_v1 as f_exc
from neutron_lib.plugins import constants as plugin_constants
from neutron_lib.plugins import directory
from neutron.common import rpc as n_rpc
from neutron.common import utils as n_utils
from oslo_config import cfg
from oslo_log import log as logging
import oslo_messaging
from neutron_fwaas._i18n import _LI, _LW
from neutron_fwaas.common import fwaas_constants as f_const
from neutron_fwaas.db.firewall import firewall_db
from neutron_fwaas.db.firewall import firewall_router_insertion_db
from neutron_fwaas.extensions import firewall as fw_ext
LOG = logging.getLogger(__name__)
class FirewallCallbacks(object):
target = oslo_messaging.Target(version='1.0')
def __init__(self, plugin):
super(FirewallCallbacks, self).__init__()
self.plugin = plugin
def set_firewall_status(self, context, firewall_id, status, **kwargs):
"""Agent uses this to set a firewall's status."""
LOG.debug("Setting firewall %s to status: %s", firewall_id, status)
# Sanitize status first
if status in (nl_constants.ACTIVE, nl_constants.DOWN,
nl_constants.INACTIVE):
to_update = status
else:
to_update = nl_constants.ERROR
# ignore changing status if firewall expects to be deleted
# That case means that while some pending operation has been
# performed on the backend, neutron server received delete request
# and changed firewall status to PENDING_DELETE
updated = self.plugin.update_firewall_status(
context, firewall_id, to_update,
not_in=(nl_constants.PENDING_DELETE,))
if updated:
LOG.debug("firewall %s status set: %s", firewall_id, to_update)
return updated and to_update != nl_constants.ERROR
def firewall_deleted(self, context, firewall_id, **kwargs):
"""Agent uses this to indicate firewall is deleted."""
LOG.debug("firewall_deleted() called")
try:
with context.session.begin(subtransactions=True):
fw_db = self.plugin._get_firewall(context, firewall_id)
# allow to delete firewalls in ERROR state
if fw_db.status in (nl_constants.PENDING_DELETE,
nl_constants.ERROR):
self.plugin.delete_db_firewall_object(context, firewall_id)
return True
else:
LOG.warning(_LW('Firewall %(fw)s unexpectedly deleted by '
'agent, status was %(status)s'),
{'fw': firewall_id, 'status': fw_db.status})
fw_db.update({"status": nl_constants.ERROR})
return False
except f_exc.FirewallNotFound:
LOG.info(_LI('Firewall %s already deleted'), firewall_id)
return True
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 = []
for fw in self.plugin.get_firewalls(context):
fw_with_rules = self.plugin._make_firewall_dict_with_rules(
context, fw['id'])
if fw['status'] == nl_constants.PENDING_DELETE:
fw_with_rules['add-router-ids'] = []
fw_with_rules['del-router-ids'] = fw['router_ids']
else:
fw_with_rules['add-router-ids'] = fw['router_ids']
fw_with_rules['del-router-ids'] = []
fw_list.append(fw_with_rules)
return fw_list
def get_tenants_with_firewalls(self, context, **kwargs):
"""Agent uses this to get all tenants that have firewalls."""
LOG.debug("get_tenants_with_firewalls() called")
host = kwargs['host']
ctx = neutron_context.get_admin_context()
tenant_ids = self.plugin.get_firewall_tenant_ids_on_host(ctx, host)
return tenant_ids
class FirewallAgentApi(object):
"""Plugin side of plugin to agent RPC API."""
def __init__(self, topic, host):
self.host = host
target = oslo_messaging.Target(topic=topic, version='1.0')
self.client = n_rpc.get_client(target)
def _prepare_rpc_client(self, host=None):
if host:
return self.client.prepare(server=host)
else:
# historical behaviour (RPC broadcast)
return self.client.prepare(fanout=True)
def create_firewall(self, context, firewall, host=None):
cctxt = self._prepare_rpc_client(host)
# TODO(blallau) host param is not used on agent side (to be removed)
cctxt.cast(context, 'create_firewall', firewall=firewall,
host=self.host)
def update_firewall(self, context, firewall, host=None):
cctxt = self._prepare_rpc_client(host)
# TODO(blallau) host param is not used on agent side (to be removed)
cctxt.cast(context, 'update_firewall', firewall=firewall,
host=self.host)
def delete_firewall(self, context, firewall, host=None):
cctxt = self._prepare_rpc_client(host)
# TODO(blallau) host param is not used on agent side (to be removed)
cctxt.cast(context, 'delete_firewall', firewall=firewall,
host=self.host)
class FirewallPlugin(
firewall_db.Firewall_db_mixin,
firewall_router_insertion_db.FirewallRouterInsertionDbMixin):
"""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", "fwaasrouterinsertion"]
path_prefix = fw_ext.FIREWALL_PREFIX
def __init__(self):
"""Do the initialization for the firewall service plugin here."""
self.start_rpc_listeners()
self.agent_rpc = FirewallAgentApi(
f_const.FW_AGENT,
cfg.CONF.host
)
firewall_db.subscribe()
def start_rpc_listeners(self):
self.endpoints = [FirewallCallbacks(self)]
self.conn = n_rpc.create_connection()
self.conn.create_consumer(
f_const.FIREWALL_PLUGIN, self.endpoints, fanout=False)
return self.conn.consume_in_threads()
def _get_hosts_to_notify(self, context, router_ids):
"""Returns all hosts to send notification about firewall update"""
l3_plugin = directory.get_plugin(plugin_constants.L3)
no_broadcast = (
n_utils.is_extension_supported(
l3_plugin, nl_constants.L3_AGENT_SCHEDULER_EXT_ALIAS) and
getattr(l3_plugin, 'get_l3_agents_hosting_routers', False))
if no_broadcast:
agents = l3_plugin.get_l3_agents_hosting_routers(
context, router_ids, admin_state_up=True, active=True)
return [a.host for a in agents]
# NOTE(blallau): default: FirewallAgentAPI performs RPC broadcast
return [None]
def _rpc_update_firewall(self, context, firewall_id):
status_update = {"firewall": {"status": nl_constants.PENDING_UPDATE}}
super(FirewallPlugin, self).update_firewall(context, firewall_id,
status_update)
fw_with_rules = self._make_firewall_dict_with_rules(context,
firewall_id)
# this is triggered on an update to fw rule or policy, no
# change in associated routers.
fw_update_rtrs = self.get_firewall_routers(context, firewall_id)
fw_with_rules['add-router-ids'] = fw_update_rtrs
fw_with_rules['del-router-ids'] = []
hosts = self._get_hosts_to_notify(context, fw_update_rtrs)
for host in hosts:
self.agent_rpc.update_firewall(context, fw_with_rules,
host=host)
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 [nl_constants.PENDING_CREATE,
nl_constants.PENDING_UPDATE,
nl_constants.PENDING_DELETE]:
raise f_exc.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_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 _get_routers_for_create_firewall(self, tenant_id, context, firewall):
# pop router_id as this goes in the router association db
# and not firewall db
router_ids = firewall['firewall'].pop('router_ids', None)
if router_ids == nl_constants.ATTR_NOT_SPECIFIED:
# old semantics router-ids keyword not specified pick up
# all routers on tenant.
l3_plugin = directory.get_plugin(plugin_constants.L3)
ctx = neutron_context.get_admin_context()
routers = l3_plugin.get_routers(ctx)
router_ids = [
router['id']
for router in routers
if router['tenant_id'] == tenant_id]
# validation can still fail this if there is another fw
# which is associated with one of these routers.
self.validate_firewall_routers_not_in_use(context, router_ids)
return router_ids
else:
if not router_ids:
# This indicates that user specifies no routers.
return []
else:
# some router(s) provided.
self.validate_firewall_routers_not_in_use(context, router_ids)
return router_ids
def create_firewall(self, context, firewall):
LOG.debug("create_firewall() called")
fw_new_rtrs = self._get_routers_for_create_firewall(
firewall['firewall']['tenant_id'], context, firewall)
if not fw_new_rtrs:
# no messaging to agent needed, and fw needs to go
# to INACTIVE(no associated rtrs) state.
status = nl_constants.INACTIVE
fw = super(FirewallPlugin, self).create_firewall(
context, firewall, status)
fw['router_ids'] = []
return fw
else:
fw = super(FirewallPlugin, self).create_firewall(
context, firewall)
fw['router_ids'] = fw_new_rtrs
fw_with_rules = (
self._make_firewall_dict_with_rules(context, fw['id']))
fw_with_rtrs = {'fw_id': fw['id'],
'router_ids': fw_new_rtrs}
self.set_routers_for_firewall(context, fw_with_rtrs)
fw_with_rules['add-router-ids'] = fw_new_rtrs
fw_with_rules['del-router-ids'] = []
hosts = self._get_hosts_to_notify(context, fw_new_rtrs)
for host in hosts:
self.agent_rpc.create_firewall(context, fw_with_rules,
host=host)
return fw
def update_firewall(self, context, id, firewall):
LOG.debug("update_firewall() called on firewall %s", id)
self._ensure_update_firewall(context, id)
# pop router_id as this goes in the router association db
# and not firewall db
router_ids = firewall['firewall'].pop('router_ids', None)
fw_current_rtrs = fw_new_rtrs = self.get_firewall_routers(
context, id)
if router_ids is not None:
if router_ids == []:
# This indicates that user is indicating no routers.
fw_new_rtrs = []
else:
self.validate_firewall_routers_not_in_use(
context, router_ids, id)
fw_new_rtrs = router_ids
self.update_firewall_routers(context, {'fw_id': id,
'router_ids': fw_new_rtrs})
if not fw_new_rtrs and not fw_current_rtrs:
# no messaging to agent needed, and we need to continue
# in INACTIVE state
firewall['firewall']['status'] = nl_constants.INACTIVE
fw = super(FirewallPlugin, self).update_firewall(
context, id, firewall)
fw['router_ids'] = []
return fw
else:
firewall['firewall']['status'] = nl_constants.PENDING_UPDATE
fw = super(FirewallPlugin, self).update_firewall(
context, id, firewall)
fw['router_ids'] = fw_new_rtrs
fw_with_rules = (
self._make_firewall_dict_with_rules(context, fw['id']))
# determine rtrs to add fw to and del from
fw_with_rules['add-router-ids'] = fw_new_rtrs
fw_with_rules['del-router-ids'] = list(
set(fw_current_rtrs).difference(set(fw_new_rtrs)))
# last-router drives agent to ack with status to set state to INACTIVE
fw_with_rules['last-router'] = not fw_new_rtrs
LOG.debug("update_firewall %s: Add Routers: %s, Del Routers: %s",
fw['id'],
fw_with_rules['add-router-ids'],
fw_with_rules['del-router-ids'])
hosts = self._get_hosts_to_notify(context, list(
set(fw_new_rtrs).union(set(fw_current_rtrs))))
for host in hosts:
self.agent_rpc.update_firewall(context, fw_with_rules,
host=host)
return fw
def delete_db_firewall_object(self, context, id):
super(FirewallPlugin, self).delete_firewall(context, id)
def delete_firewall(self, context, id):
LOG.debug("delete_firewall() called on firewall %s", id)
fw_with_rules = (
self._make_firewall_dict_with_rules(context, id))
fw_delete_rtrs = self.get_firewall_routers(context, id)
fw_with_rules['del-router-ids'] = fw_delete_rtrs
fw_with_rules['add-router-ids'] = []
if not fw_with_rules['del-router-ids']:
# no routers to delete on the agent side
self.delete_db_firewall_object(context, id)
else:
status = {"firewall": {"status": nl_constants.PENDING_DELETE}}
super(FirewallPlugin, self).update_firewall(context, id, status)
# Reflect state change in fw_with_rules
fw_with_rules['status'] = status['firewall']['status']
hosts = self._get_hosts_to_notify(context, fw_delete_rtrs)
if hosts:
for host in hosts:
self.agent_rpc.delete_firewall(context, fw_with_rules,
host=host)
else:
# NOTE(blallau): we directly delete the firewall
# if router is not associated to an agent
self.delete_db_firewall_object(context, id)
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_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 _notify_firewall_updates(self, context, resource, update_info):
notifier = n_rpc.get_notifier('network')
notifier.info(context, resource, update_info)
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)
resource = 'firewall_policy.update.insert_rule'
self._notify_firewall_updates(context, resource, rule_info)
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)
resource = 'firewall_policy.update.remove_rule'
self._notify_firewall_updates(context, resource, rule_info)
return fwp
def get_firewalls(self, context, filters=None, fields=None):
LOG.debug("fwaas get_firewalls() called")
has_id_field = not fields or 'id' in fields
if not has_id_field:
fields = fields + ['id']
fw_list = super(FirewallPlugin, self).get_firewalls(
context, filters, fields)
if not fields or 'router_ids' in fields:
for fw in fw_list:
fw['router_ids'] = self.get_firewall_routers(context, fw['id'])
if not has_id_field:
for fw in fw_list:
del fw['id']
return fw_list
def get_firewall(self, context, id, fields=None):
LOG.debug("fwaas get_firewall() called")
res = super(FirewallPlugin, self).get_firewall(
context, id, fields)
fw_current_rtrs = self.get_firewall_routers(context, id)
res['router_ids'] = fw_current_rtrs
return res

View File

@ -1,392 +0,0 @@
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from neutron_lib import context as neutron_context
from neutron_lib.exceptions import firewall_v2 as f_exc
from neutron_lib.plugins import directory
from neutron.common import rpc as n_rpc
from neutron_lib import constants as nl_constants
from neutron_lib.plugins import constants as plugin_const
from oslo_config import cfg
from oslo_log import log as logging
import oslo_messaging
from neutron.db import servicetype_db as st_db
from neutron.services import provider_configuration as provider_conf
from neutron_fwaas._i18n import _LI
from neutron_fwaas.common import fwaas_constants
from neutron_fwaas.db.firewall.v2 import firewall_db_v2
from neutron_fwaas.extensions import firewall_v2 as fw_ext
LOG = logging.getLogger(__name__)
def add_provider_configuration(type_manager, service_type):
type_manager.add_provider_configuration(
service_type,
provider_conf.ProviderConfiguration('neutron_fwaas'))
class FirewallAgentApi(object):
"""Plugin side of plugin to agent RPC API."""
def __init__(self, topic, host):
self.host = host
target = oslo_messaging.Target(topic=topic, version='1.0')
self.client = n_rpc.get_client(target)
def create_firewall_group(self, context, firewall_group):
cctxt = self.client.prepare(fanout=True)
cctxt.cast(context, 'create_firewall_group',
firewall_group=firewall_group,
host=self.host)
def update_firewall_group(self, context, firewall_group):
cctxt = self.client.prepare(fanout=True)
cctxt.cast(context, 'update_firewall_group',
firewall_group=firewall_group,
host=self.host)
def delete_firewall_group(self, context, firewall_group):
cctxt = self.client.prepare(fanout=True)
cctxt.cast(context, 'delete_firewall_group',
firewall_group=firewall_group,
host=self.host)
class FirewallCallbacks(object):
target = oslo_messaging.Target(version='1.0')
def __init__(self, plugin):
super(FirewallCallbacks, self).__init__()
self.plugin = plugin
def set_firewall_group_status(self, context, fwg_id, status, **kwargs):
"""Agent uses this to set a firewall_group's status."""
LOG.debug("Setting firewall_group %s to status: %s",
fwg_id, status)
# Sanitize status first
if status in (nl_constants.ACTIVE, nl_constants.DOWN,
nl_constants.INACTIVE):
to_update = status
else:
to_update = nl_constants.ERROR
# ignore changing status if firewall_group expects to be deleted
# That case means that while some pending operation has been
# performed on the backend, neutron server received delete request
# and changed firewall status to PENDING_DELETE
updated = self.plugin.update_firewall_group_status(
context, fwg_id, to_update, not_in=(nl_constants.PENDING_DELETE,))
if updated:
LOG.debug("firewall %s status set: %s", fwg_id, to_update)
return updated and to_update != nl_constants.ERROR
def firewall_group_deleted(self, context, fwg_id, **kwargs):
"""Agent uses this to indicate firewall is deleted."""
LOG.debug("firewall_group_deleted() called")
try:
with context.session.begin(subtransactions=True):
fwg_db = self.plugin._get_firewall_group(context, fwg_id)
# allow to delete firewalls in ERROR state
if fwg_db.status in (nl_constants.PENDING_DELETE,
nl_constants.ERROR):
self.plugin.delete_db_firewall_group_object(context,
fwg_id)
return True
else:
LOG.warning(('Firewall %(fwg)s unexpectedly deleted by '
'agent, status was %(status)s'),
{'fwg': fwg_id, 'status': fwg_db.status})
fwg_db.update({"status": nl_constants.ERROR})
return False
except f_exc.FirewallGroupNotFound:
LOG.info(_LI('Firewall group %s already deleted'), fwg_id)
return True
def get_firewall_groups_for_project(self, context, **kwargs):
"""Gets all firewall_groups and rules on a project."""
LOG.debug("get_firewall_groups_for_project() called")
fwg_list = []
for fwg in self.plugin.get_firewall_groups(context):
fwg_with_rules = self.plugin._make_firewall_group_dict_with_rules(
context, fwg['id'])
if fwg['status'] == nl_constants.PENDING_DELETE:
fwg_with_rules['add-port-ids'] = []
fwg_with_rules['del-port-ids'] = (
self.plugin._get_ports_in_firewall_group(context,
fwg['id']))
else:
fwg_with_rules['add-port-ids'] = (
self.plugin._get_ports_in_firewall_group(context,
fwg['id']))
fwg_with_rules['del-port-ids'] = []
fwg_list.append(fwg_with_rules)
return fwg_list
def get_projects_with_firewall_groups(self, context, **kwargs):
"""Get all projects that have firewall_groups."""
LOG.debug("get_projects_with_firewall_groups() called")
ctx = neutron_context.get_admin_context()
fwg_list = self.plugin.get_firewall_groups(ctx)
fwg_project_list = list(set(fwg['tenant_id'] for fwg in fwg_list))
return fwg_project_list
class FirewallPluginV2(
firewall_db_v2.Firewall_db_mixin_v2):
"""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_v2.Firewall_db_mixin_v2.
"""
supported_extension_aliases = ["fwaas_v2"]
path_prefix = fw_ext.FIREWALL_PREFIX
def __init__(self):
"""Do the initialization for the firewall service plugin here."""
self.service_type_manager = st_db.ServiceTypeManager.get_instance()
add_provider_configuration(
self.service_type_manager, plugin_const.FIREWALL)
self.start_rpc_listeners()
self.agent_rpc = FirewallAgentApi(
fwaas_constants.FW_AGENT,
cfg.CONF.host
)
@property
def _core_plugin(self):
return directory.get_plugin()
def start_rpc_listeners(self):
self.endpoints = [FirewallCallbacks(self)]
self.conn = n_rpc.create_connection()
self.conn.create_consumer(
fwaas_constants.FIREWALL_PLUGIN, self.endpoints, fanout=False)
return self.conn.consume_in_threads()
def _rpc_update_firewall_group(self, context, fwg_id):
status_update = {"firewall_group": {"status":
nl_constants.PENDING_UPDATE}}
super(FirewallPluginV2, self).update_firewall_group(
context, fwg_id, status_update)
fwg_with_rules = self._make_firewall_group_dict_with_rules(context,
fwg_id)
# this is triggered on an update to fwg rule or policy, no
# change in associated ports.
fwg_with_rules['add-port-ids'] = self._get_ports_in_firewall_group(
context, fwg_id)
fwg_with_rules['del-port-ids'] = []
self.agent_rpc.update_firewall_group(context, fwg_with_rules)
def _rpc_update_firewall_policy(self, context, firewall_policy_id):
firewall_policy = self.get_firewall_policy(context, firewall_policy_id)
if firewall_policy:
ing_fwg_ids, eg_fwg_ids = self._get_fwgs_with_policy(context,
firewall_policy_id)
for fwg_id in list(set(ing_fwg_ids + eg_fwg_ids)):
self._rpc_update_firewall_group(context, fwg_id)
def _ensure_update_firewall_group(self, context, fwg_id):
fwg = self.get_firewall_group(context, fwg_id)
if fwg['status'] in [nl_constants.PENDING_CREATE,
nl_constants.PENDING_UPDATE,
nl_constants.PENDING_DELETE]:
raise f_exc.FirewallGroupInPendingState(firewall_id=fwg_id,
pending_state=fwg['status'])
def _ensure_update_firewall_policy(self, context, firewall_policy_id):
firewall_policy = self.get_firewall_policy(context, firewall_policy_id)
if firewall_policy:
ing_fwg_ids, eg_fwg_ids = self._get_fwgs_with_policy(context,
firewall_policy_id)
for fwg_id in list(set(ing_fwg_ids + eg_fwg_ids)):
self._ensure_update_firewall_group(context, fwg_id)
def _ensure_update_firewall_rule(self, context, fwr_id):
fwp_ids = self._get_policies_with_rule(context, fwr_id)
for fwp_id in fwp_ids:
self._ensure_update_firewall_policy(context, fwp_id)
def _validate_ports_for_firewall_group(self, context, tenant_id,
fwg_ports):
# TODO(sridar): elevated context and do we want to use public ?
for port_id in fwg_ports:
port_db = self._core_plugin._get_port(context, port_id)
if port_db['device_owner'] != "network:router_interface":
raise f_exc.FirewallGroupPortInvalid(port_id=port_id)
if port_db['tenant_id'] != tenant_id:
raise f_exc.FirewallGroupPortInvalidProject(
port_id=port_id, tenant_id=port_db['tenant_id'])
return
def _check_no_need_pending(self, context, fwg_id, fwg_body):
fwg_db = self._get_firewall_group(context, fwg_id)
fwp_req_in = fwg_body.get('ingress_firewall_policy_id', None)
fwp_req_eg = fwg_body.get('egress_firewall_policy_id', None)
if ((not fwg_db.ingress_firewall_policy_id and
fwp_req_in is fwg_db.ingress_firewall_policy_id) and
(not fwg_db.egress_firewall_policy_id and
fwp_req_eg is fwg_db.ingress_firewall_policy_id)):
return True
return False
def create_firewall_group(self, context, firewall_group):
LOG.debug("create_firewall_group() called")
fwgrp = firewall_group['firewall_group']
fwg_ports = fwgrp['ports']
if not fwg_ports:
# no messaging to agent needed, and fw needs to go
# to INACTIVE(no associated ports) state.
status = nl_constants.INACTIVE
fwg = super(FirewallPluginV2, self).create_firewall_group(
context, firewall_group, status)
fwg['ports'] = []
return fwg
else:
# Validate ports
self._validate_ports_for_firewall_group(context,
firewall_group['firewall_group']['tenant_id'],
fwg_ports)
self._validate_if_firewall_group_on_ports(context, fwg_ports)
if (not fwgrp['ingress_firewall_policy_id'] and
not fwgrp['egress_firewall_policy_id']):
# No policy configured
status = nl_constants.INACTIVE
fwg = super(FirewallPluginV2, self).create_firewall_group(
context, firewall_group, status)
return fwg
fwg = super(FirewallPluginV2, self).create_firewall_group(
context, firewall_group)
fwg['ports'] = fwg_ports
fwg_with_rules = (
self._make_firewall_group_dict_with_rules(context, fwg['id']))
fwg_with_rules['add-port-ids'] = fwg_ports
fwg_with_rules['del-ports-ids'] = []
self.agent_rpc.create_firewall_group(context, fwg_with_rules)
return fwg
def update_firewall_group(self, context, id, firewall_group):
LOG.debug("update_firewall_group() called on firewall_group %s", id)
self._ensure_update_firewall_group(context, id)
# TODO(sridar): need closure on status when no policy associated.
fwg_current_ports = fwg_new_ports = self._get_ports_in_firewall_group(
context, id)
if 'ports' in firewall_group['firewall_group']:
fwg_ports = firewall_group['firewall_group']['ports']
if fwg_ports == []:
# This indicates that user is indicating no ports.
fwg_new_ports = []
else:
self._validate_ports_for_firewall_group(
context, context.tenant_id, fwg_ports)
self._validate_if_firewall_group_on_ports(
context, fwg_ports, id)
fwg_new_ports = fwg_ports
if ((not fwg_new_ports and not fwg_current_ports) or
self._check_no_need_pending(context,
id, firewall_group['firewall_group'])):
# no messaging to agent needed, and we need to continue
# in INACTIVE state
firewall_group['firewall_group']['status'] = nl_constants.INACTIVE
fwg = super(FirewallPluginV2, self).update_firewall_group(
context, id, firewall_group)
if fwg_new_ports:
fwg['ports'] = fwg_new_ports
elif not fwg_new_ports and fwg_current_ports:
fwg['ports'] = fwg_current_ports
else:
fwg['ports'] = []
return fwg
else:
firewall_group['firewall_group']['status'] = (nl_constants.
PENDING_UPDATE)
fwg = super(FirewallPluginV2, self).update_firewall_group(
context, id, firewall_group)
fwg['ports'] = fwg_new_ports
fwg_with_rules = (
self._make_firewall_group_dict_with_rules(context, fwg['id']))
# determine ports to add fw to and del from
fwg_with_rules['add-port-ids'] = fwg_new_ports
fwg_with_rules['del-port-ids'] = list(
set(fwg_current_ports).difference(set(fwg_new_ports)))
# last-port drives agent to ack with status to set state to INACTIVE
fwg_with_rules['last-port'] = not fwg_new_ports
LOG.debug("update_firewall_group %s: Add Ports: %s, Del Ports: %s",
fwg['id'],
fwg_with_rules['add-port-ids'],
fwg_with_rules['del-port-ids'])
self.agent_rpc.update_firewall_group(context, fwg_with_rules)
return fwg
def delete_db_firewall_group_object(self, context, id):
super(FirewallPluginV2, self).delete_firewall_group(context, id)
def delete_firewall_group(self, context, id):
LOG.debug("delete_firewall_group() called on firewall_group %s", id)
fw_with_rules = (
self._make_firewall_group_dict_with_rules(context, id))
fw_with_rules['del-port-ids'] = self._get_ports_in_firewall_group(
context, id)
fw_with_rules['add-port-ids'] = []
if not fw_with_rules['del-port-ids']:
# no ports, no need to talk to the agent
self.delete_db_firewall_group_object(context, id)
else:
status = {"firewall_group":
{"status": nl_constants.PENDING_DELETE}}
super(FirewallPluginV2, self).update_firewall_group(
context, id, status)
# Reflect state change in fw_with_rules
fw_with_rules['status'] = status['firewall_group']['status']
self.agent_rpc.delete_firewall_group(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(FirewallPluginV2,
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_firewall_rule(context, id)
fwr = super(FirewallPluginV2,
self).update_firewall_rule(context, id, firewall_rule)
fwp_ids = self._get_policies_with_rule(context, id)
for fwp_id in fwp_ids:
self._rpc_update_firewall_policy(context, fwp_id)
return fwr

View File

@ -1,44 +0,0 @@
# Copyright 2014 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.
#
import os
from neutron.common import test_lib
from neutron.tests import base as n_base
from neutron.tests.unit.db import test_db_base_plugin_v2 as test_db_plugin
class BaseTestCase(n_base.BaseTestCase):
pass
class NeutronDbPluginV2TestCase(test_db_plugin.NeutronDbPluginV2TestCase):
def setup_config(self):
## Copied from neutron's test_db_base_plugin_v2 because they
## don't allow to specify args
# Create the default configurations
args = ['--config-file', n_base.etcdir('neutron.conf')]
# If test_config specifies some config-file, use it, as well
for config_file in test_lib.test_config.get('config_files', []):
args.extend(['--config-file', config_file])
## our own stuff
dirpath = os.path.join(os.path.dirname(__file__),
'./../../etc/neutron/policy.d')
args.extend(['--config-dir', dirpath])
self.config_parse(args=args)

View File

@ -1,3 +0,0 @@
The files in this directory are intended for use by the
infra jobs that run the various functional test
suite in the gate for the neutron-fwaas repo.

View File

@ -1,20 +0,0 @@
# neutron-rootwrap command filters to support functional testing. It
# is NOT intended to be used outside of a test environment.
#
# This file should be owned by (and only-writeable by) the root user
[Filters]
# '$BASE_PATH' is intended to be replaced with the expected tox path
# (e.g. /opt/stack/new/neutron/.tox/dsvm-functional) by the neutron
# functional jenkins job. This ensures that tests can kill the
# processes that they launch with their containing tox environment's
# python.
kill_tox_python: KillFilter, root, $BASE_PATH/bin/python, -9
# enable ping from namespace
ping_filter: CommandFilter, ping, root
# enable curl from namespace
curl_filter: CommandFilter, curl, root
tee_filter: CommandFilter, tee, root
tee_kill: KillFilter, root, tee, -9

View File

@ -1,7 +0,0 @@
# neutron-rootwrap command filters to support functional testing. It
# is NOT intended to be used outside of a test environment.
#
# This file should be owned by (and only-writeable by) the root user
[Filters]
#none currently

Some files were not shown because too many files have changed in this diff Show More