revive neutron-fwaas project

This reverts commit caae7b6a6f.

Reason for revert:
Many users still need L3 firewalls and Inspur team wants to maintain
this project.
Neutron drivers team discussed the topic of the maintenance of
neutron-fwaas, and agreed to include neutron-fwaas again to Neutron
stadium[1].

Some updates have been made:
Remove use "autonested_transaction" method, see more [2]
Replace "neutron_lib.callbacks.registry.notify" with "registry.publish"
Replace rootwrap execution with privsep context execution.
Ensure db Models and migration scripts are sync, set table
firewall_group_port_associations_v2's two columns nullable=False

[1] https://meetings.opendev.org/meetings/neutron_drivers/2022/neutron_drivers.2022-01-28-14.00.log.html#l-14
[2] https://review.opendev.org/c/openstack/neutron-lib/+/761728

Change-Id: I14f551c199d9badcf25b9e65c954c012326d27cd
This commit is contained in:
ZhouHeng 2022-02-08 06:18:55 +00:00
parent 11f0534e8b
commit a9f26b81e2
287 changed files with 25781 additions and 8 deletions

7
.coveragerc Normal file
View File

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

37
.gitignore vendored Normal file
View File

@ -0,0 +1,37 @@
AUTHORS
build/*
build-stamp
ChangeLog
cover/
covhtml/
dist/
doc/build
doc/source/_static/config_samples/*.sample
doc/source/_static/*.policy.yaml.sample
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
!/.zuul.yaml
!/.stestr.conf
# Files created by releasenotes build
releasenotes/build

11
.mailmap Normal file
View File

@ -0,0 +1,11 @@
# 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 Normal file
View File

@ -0,0 +1,130 @@
# 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

3
.stestr.conf Normal file
View File

@ -0,0 +1,3 @@
[DEFAULT]
test_path=${OS_TEST_PATH:-./neutron_fwaas/tests/unit}
top_dir=./

77
.zuul.yaml Normal file
View File

@ -0,0 +1,77 @@
- project:
templates:
- check-requirements
- openstack-cover-jobs-neutron
- openstack-lower-constraints-jobs-neutron
- openstack-python3-ussuri-jobs-neutron
- periodic-stable-jobs-neutron
- publish-openstack-docs-pti
- release-notes-jobs-python3
check:
jobs:
- neutron-fwaas-functional
- neutron-fwaas-v2-dsvm-tempest-multinode:
voting: false
gate:
jobs:
- neutron-fwaas-functional
experimental:
jobs:
- neutron-fwaas-fullstack
- job:
name: neutron-fwaas-functional
parent: neutron-functional
timeout: 2400
pre-run: playbooks/configure_functional_job.yaml
vars:
project_name: neutron-fwaas
devstack_services:
INSTALL_OVN: false
- job:
name: neutron-fwaas-fullstack
parent: neutron-fullstack
vars:
project_name: neutron-fwaas
- job:
name: neutron-fwaas-v2-dsvm-tempest-multinode
parent: neutron-ovs-tempest-multinode-full
roles:
- zuul: openstack/devstack
required-projects:
- openstack/devstack-gate
- openstack/neutron
- openstack/neutron-fwaas
- openstack/neutron-tempest-plugin
- openstack/tempest
vars:
tox_envlist: all-plugin
tempest_test_regex: ^neutron_tempest_plugin\.fwaas
devstack_plugins:
neutron: https://opendev.org/openstack/neutron.git
neutron-fwaas: https://opendev.org/openstack/neutron-fwaas.git
neutron-tempest-plugin: https://opendev.org/openstack/neutron-tempest-plugin.git
devstack_services:
q-fwaas-v2: true
devstack_localrc:
NETWORK_API_EXTENSIONS: "agent,binding,dhcp_agent_scheduler,external-net,ext-gw-mode,extra_dhcp_opts,quotas,router,security-group,subnet_allocation,network-ip-availability,auto-allocated-topology,timestamp_core,tag,service-type,rbac-policies,standard-attr-description,pagination,sorting,project-id,fwaas_v2"
Q_AGENT: openvswitch
Q_ML2_TENANT_NETWORK_TYPE: vxlan
Q_ML2_PLUGIN_MECHANISM_DRIVERS: openvswitch
group-vars:
subnode:
devstack_services:
q-agt: true
devstack_localrc:
USE_PYTHON3: true
devstack_local_conf:
post-config:
# NOTE(slaweq): We can get rid of this hardcoded absolute path when
# devstack-tempest job will be switched to use lib/neutron instead of
# lib/neutron-legacy
"/$NEUTRON_CORE_PLUGIN_CONF":
ovs:
tunnel_bridge: br-tun
bridge_mappings: public:br-ex

4
CONTRIBUTING.rst Normal file
View File

@ -0,0 +1,4 @@
Please see the Neutron CONTRIBUTING.rst file for how to contribute to
neutron-fwaas:
`Neutron CONTRIBUTING.rst <https://opendev.org/openstack/neutron/src/branch/master/CONTRIBUTING.rst>`_

7
HACKING.rst Normal file
View File

@ -0,0 +1,7 @@
Neutron FWaaS Style Commandments
================================
Please see the Neutron HACKING.rst file for style commandments for
neutron-fwaas:
`Neutron HACKING.rst <https://opendev.org/openstack/neutron/src/branch/master/HACKING.rst>`_

176
LICENSE Normal file
View File

@ -0,0 +1,176 @@
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,10 +1,39 @@
This project is no longer maintained.
========================
Team and repository tags
========================
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".
.. image:: https://governance.openstack.org/tc/badges/neutron-fwaas.svg
:target: https://governance.openstack.org/tc/reference/tags/index.html
.. Change things from this point on
.. warning::
Due to lack of maintainers this project is now deprecated in the Neutron
stadium and will be removed from stadium in ``W`` cycle.
If You want to step in and be maintainer of this project to keep it in the
Neutron stadium, please contact the ``neutron team`` via
openstack-discuss@lists.openstack.org or IRC channel #openstack-neutron
@freenode.
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://opendev.org/openstack/neutron-fwaas>.
Please refer to Neutron documentation for more information:
`Neutron README.rst <https://opendev.org/openstack/neutron/src/branch/master/README.rst>`_
Get release notes:
`Neutron FWaaS Release Notes <https://docs.openstack.org/releasenotes/neutron-fwaas/>`_
For any further questions, please email
openstack-discuss@lists.openstack.org or join #openstack-dev on
OFTC.

12
TESTING.rst Normal file
View File

@ -0,0 +1,12 @@
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 <https://opendev.org/openstack/neutron/src/branch/master/TESTING.rst>`_
For instructions on how to use FWaaS with devstack, look at:
`Neutron-FWaaS DevStack <https://opendev.org/openstack/neutron-fwaas/src/branch/master/devstack/README.rst>`_

2
babel.cfg Normal file
View File

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

10
bindep.txt Normal file
View File

@ -0,0 +1,10 @@
# This file contains runtime (non-python) dependencies
# More info at: http://docs.openstack.org/infra/bindep/readme.html
# MySQL and PostgreSQL databases since some jobs are set up in
# OpenStack infra that need these like
libpq-dev [test]
# Packages required e.g. in functional tests
libnetfilter-log1 [platform:dpkg platform:suse]
libnetfilter-log [platform:rpm !platform:suse]

30
devstack/README.rst Normal file
View File

@ -0,0 +1,30 @@
=========================
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/devstack/latest/plugins.html>`_.
Please note that the old 'q-fwaas' keyword still exists, You can specify
enable_service q-fwaas or enable_service q-fwaas-v2 in local.conf
How to run FWaaS V2 in DevStack
===============================
Add the following to the localrc section of your local.conf to configure
FWaaS v2.
.. code-block:: ini
[[local|localrc]]
enable_plugin neutron-fwaas https://git.openstack.org/openstack/neutron-fwaas
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:: ini
[[local|localrc]]
enable_plugin neutron-fwaas https://review.openstack.org/p/openstack/neutron-fwaas refs/changes/50/214350/14

16
devstack/lib/l2_agent Normal file
View File

@ -0,0 +1,16 @@
# This file was shamelessly stolen from the neutron repository here:
# https://opendev.org/openstack/neutron/src/branch/master/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"
}

16
devstack/lib/l3_agent Normal file
View File

@ -0,0 +1,16 @@
# This file is completely based on one in the neutron repository here:
# https://opendev.org/openstack/neutron/src/branch/master/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"
}

145
devstack/plugin.sh Executable file
View File

@ -0,0 +1,145 @@
#!/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
if is_ubuntu; then
install_package libnetfilter-log1
else
# EPEL
install_package libnetfilter_log
fi
}
function configure_fwaas_v2() {
# Add conf file
cp $NEUTRON_FWAAS_DIR/etc/neutron_fwaas.conf.sample $NEUTRON_FWAAS_CONF
neutron_server_config_add $NEUTRON_FWAAS_CONF
inicomment $NEUTRON_FWAAS_CONF service_providers service_provider
iniadd $NEUTRON_FWAAS_CONF service_providers service_provider $NEUTRON_FWAAS_SERVICE_PROVIDERV2
neutron_fwaas_configure_driver fwaas_v2
if is_service_enabled q-l3; then
iniset_multiline $Q_L3_CONF_FILE fwaas agent_version v2
iniset_multiline $Q_L3_CONF_FILE fwaas driver $FWAAS_DRIVER_V2
fi
if is_service_enabled q-agt; then
# TODO(hoangcx) we can remove the slashes below once neutron-legacy has gone
iniset /$NEUTRON_CORE_PLUGIN_CONF fwaas firewall_l2_driver $FW_L2_DRIVER
iniset /$NEUTRON_CORE_PLUGIN_CONF agent extensions fwaas_v2
fi
}
function configure_l3_log_fwaas_v2(){
if is_service_enabled q-l3; then
iniadd $Q_L3_CONF_FILE agent extensions fwaas_v2_log
fi
}
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.
:
# 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.
:
if is_ubuntu; then
uninstall_package libnetfilter-log1
else
# EPEL
uninstall_package libnetfilter_log
fi
}
function neutron_fwaas_configure_common {
neutron_service_plugin_class_add $FWAAS_PLUGIN_V2
}
function neutron_fwaas_configure_driver {
if is_service_enabled q-l3; then
plugin_agent_add_l3_agent_extension $1
configure_l3_agent
iniset_multiline $Q_L3_CONF_FILE fwaas enabled True
fi
}
# check for service enabled
if is_service_enabled q-svc neutron-api && is_service_enabled q-fwaas q-fwaas-v2 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
echo_summary "Configuring neutron-fwaas for FWaaS v2"
configure_fwaas_v2
if is_service_enabled q-log neutron-log; then
echo_summary "Configuring FwaaS V2 packet log for l3 extension"
configure_l3_log_fwaas_v2
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

12
devstack/settings Normal file
View File

@ -0,0 +1,12 @@
FWAAS_DRIVER_V2=${FWAAS_DRIVER_V2:-iptables_v2}
FW_L2_DRIVER=${FW_L2_DRIVER:-noop}
FWAAS_PLUGIN_V2=${FWAAS_PLUGIN:-firewall_v2}
NEUTRON_FWAAS_DIR=$DEST/neutron-fwaas
NEUTRON_FWAAS_CONF_FILE=neutron_fwaas.conf
NEUTRON_FWAAS_CONF=$NEUTRON_CONF_DIR/$NEUTRON_FWAAS_CONF_FILE
NEUTRON_FWAAS_SERVICE_PROVIDERV2=${NEUTRON_FWAAS_SERVICE_PROVIDERV2:-FIREWALL_V2:fwaas_db:neutron_fwaas.services.firewall.service_drivers.agents.agents.FirewallAgentDriver:default}
enable_service q-fwaas-v2

7
doc/requirements.txt Normal file
View File

@ -0,0 +1,7 @@
# The order of packages is significant, because pip processes them in the order
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
sphinx!=1.6.6,!=1.6.7,>=1.6.2 # BSD
sphinxcontrib-apidoc>=0.2.0 # BSD
openstackdocstheme>=1.18.1 # Apache-2.0
reno>=2.5.0 # Apache-2.0

View File

311
doc/source/conf.py Normal file
View File

@ -0,0 +1,311 @@
# -*- 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 = ['sphinxcontrib.apidoc',
'sphinx.ext.coverage',
'sphinx.ext.ifconfig',
'sphinx.ext.graphviz',
'sphinx.ext.todo',
'oslo_config.sphinxext',
'oslo_config.sphinxconfiggen',
'oslo_policy.sphinxext',
'oslo_policy.sphinxpolicygen',
'openstackdocstheme',]
try:
import openstackdocstheme
extensions.append('openstackdocstheme')
except ImportError:
openstackdocstheme = None
todo_include_todos = True
# sphinxcontrib.apidoc options
apidoc_module_dir = '../../neutron_fwaas'
apidoc_output_dir = 'contributor/api'
# TODO(hoangcx): remove 'services/logapi/*' and
# 'services/firewall/fwaas_plugin_v2.py' after the next neutron release
# (current release is Rocky-3)
# NOTE(longkb): Due to libnetfilter_log library is not installed in sphinx-docs
# gate, so we would like to ignore 'privileged/netfilter_log/*'.
apidoc_excluded_paths = [
'db/migration/alembic_migrations/*',
'privileged/netfilter_log/*',
'services/firewall/fwaas_plugin_v2.py',
'services/logapi/*',
'setup.py',
'tests/*',
'tests']
apidoc_separate_modules = True
# Add any paths that contain templates here, relative to this directory.
templates_path = []
# 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'
if openstackdocstheme is not None:
html_theme = 'openstackdocs'
else:
html_theme = 'default'
# 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', 'doc-neutron-fwaas.tex',
u'Neutron Firewall-as-s-Service 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
# Disable usage of xindy https://bugzilla.redhat.com/show_bug.cgi?id=1643664
latex_use_xindy = False
latex_domain_indices = False
latex_elements = {
'makeindex': '',
'printindex': '',
'preamble': r'\setcounter{tocdepth}{3}',
}
# -- Options for openstackdocstheme -------------------------------------------
repository_name = 'openstack/neutron-fwaas'
bug_project = 'neutron'
bug_tag = 'doc'
# -- Options for oslo_config.sphinxconfiggen ---------------------------------
_config_generator_config_files = [
'fwaas_driver.ini',
'neutron_fwaas.conf',
]
def _get_config_generator_config_definition(conf):
config_file_path = '../../etc/oslo-config-generator/%s' % conf
# oslo_config.sphinxconfiggen appends '.conf.sample' to the filename,
# strip file extentension (.conf or .ini).
output_file_path = '_static/config_samples/%s' % conf.rsplit('.', 1)[0]
return (config_file_path, output_file_path)
config_generator_config_file = [
_get_config_generator_config_definition(conf)
for conf in _config_generator_config_files
]
# -- Options for oslo_policy.sphinxpolicygen ---------------------------------
policy_generator_config_file = '../../etc/oslo-policy-generator/policy.conf'
sample_policy_basename = '_static/neutron-fwaas'

View File

@ -0,0 +1,6 @@
================
fwaas_driver.ini
================
.. show-options::
:config-file: etc/oslo-config-generator/fwaas_driver.ini

View File

@ -0,0 +1,41 @@
.. _configuring:
=================================
Neutron FWaaS Configuration Guide
=================================
This section provides a list of all possible options for each
configuration file.
Configuration
-------------
Neutron FWaaS uses the following configuration files for its various services.
.. toctree::
:maxdepth: 1
neutron_fwaas
fwaas_driver
The following are sample configuration files for Neutron FWaaS and utilities.
These are generated from code and reflect the current state of code
in the neutron-fwaas repository.
.. toctree::
:glob:
:maxdepth: 1
samples/*
Policy
------
Neutron FWaaS, like most OpenStack projects, uses a policy language to restrict
permissions on REST API actions.
.. toctree::
:maxdepth: 1
Policy Reference <policy>
Sample Policy File <policy-sample>

View File

@ -0,0 +1,6 @@
==================
neutron_fwaas.conf
==================
.. show-options::
:config-file: etc/oslo-config-generator/neutron_fwaas.conf

View File

@ -0,0 +1,16 @@
================================
Sample Neutron FWaaS Policy File
================================
The following is a sample neutron-fwaas policy file for adaptation and use.
The sample policy can also be viewed in :download:`file form
</_static/neutron-fwaas.policy.yaml.sample>`.
.. important::
The sample policy file is auto-generated from neutron-fwaas when this
documentation is built. You must ensure your version of neutron-fwaas
matches the version of this documentation.
.. literalinclude:: /_static/neutron-fwaas.policy.yaml.sample

View File

@ -0,0 +1,9 @@
======================
neutron-fwaas policies
======================
The following is an overview of all available policies in neutron-fwaas.
For a sample configuration file, refer to :doc:`/configuration/policy-sample`.
.. show-policy::
:config-file: etc/oslo-policy-generator/policy.conf

View File

@ -0,0 +1,8 @@
=======================
Sample fwaas_driver.ini
=======================
This sample configuration can also be viewed in `the raw format
<../../_static/config_samples/fwaas_driver.conf.sample>`_.
.. literalinclude:: ../../_static/config_samples/fwaas_driver.conf.sample

View File

@ -0,0 +1,8 @@
=========================
Sample neutron_fwaas.conf
=========================
This sample configuration can also be viewed in `the raw format
<../../_static/config_samples/neutron_fwaas.conf.sample>`_.
.. literalinclude:: ../../_static/config_samples/neutron_fwaas.conf.sample

View File

@ -0,0 +1,28 @@
=============================
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

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

View File

@ -0,0 +1,7 @@
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

@ -0,0 +1,17 @@
=================
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

@ -0,0 +1,19 @@
================
Module Reference
================
.. The module reference is rendered in HTML version much much better.
PDF version is not good for reading due to page width, lack of TOC
in subsections and so on, so we skip the module reference in PDF version.
.. only:: html
.. toctree::
:maxdepth: 1
:glob:
api/*
.. only:: latex
See the online version of this document for the module reference.

27
doc/source/index.rst Normal file
View File

@ -0,0 +1,27 @@
===========================
neutron-fwaas documentation
===========================
.. warning::
Due to lack of maintainers this project is now deprecated in the Neutron
stadium and will be removed from stadium in ``W`` cycle.
If You want to step in and be maintainer of this project to keep it in the
Neutron stadium, please contact the ``neutron team`` via
openstack-discuss@lists.openstack.org or IRC channel #openstack-neutron
@freenode.
.. toctree::
:glob:
:maxdepth: 2
install/index
configuration/index
contributor/index
.. only:: html
.. rubric:: Indices and tables
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

View File

@ -0,0 +1,39 @@
..
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/neutron/latest/admin/fwaas-v2-scenario.html>`_ or
the `Firewall-as-a-Service (FWaaS) v1 scenario <https://docs.openstack.org/neutron/latest/admin/fwaas-v1-scenario.html>`_.

9
etc/README.txt Normal file
View File

@ -0,0 +1,9 @@
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

@ -0,0 +1,7 @@
# 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

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

View File

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

View File

@ -0,0 +1,3 @@
[DEFAULT]
output_file = etc/policy.yaml.sample
namespace = neutron-fwaas

147
lower-constraints.txt Normal file
View File

@ -0,0 +1,147 @@
alabaster==0.7.10
alembic==1.6.5
amqp==2.1.1
appdirs==1.4.3
Babel==2.3.4
beautifulsoup4==4.6.0
cachetools==2.0.0
cffi==1.7.0
chardet==3.0.4
cliff==2.8.0
cmd2==0.8.0
contextlib2==0.4.0
coverage==4.0
debtcollector==1.2.0
decorator==4.1.0
deprecation==1.0
doc8==0.6.0
docutils==0.11
dogpile.cache==0.6.2
dulwich==0.15.0
eventlet==0.18.2
extras==1.0.0
fasteners==0.7.0
fixtures==3.0.0
flake8-import-order==0.12
flake8==3.6.0
future==0.16.0
futurist==1.2.0
greenlet==0.4.10
hacking==3.0.1
httplib2==0.9.1
imagesize==0.7.1
iso8601==0.1.11
Jinja2==2.10
jmespath==0.9.0
jsonpatch==1.16
jsonpointer==1.13
jsonschema==2.6.0
keystoneauth1==3.4.0
keystonemiddleware==4.17.0
kombu==4.0.0
linecache2==1.0.0
logutils==0.3.5
Mako==1.0.7
MarkupSafe==1.1.1
mccabe==0.6.0
mock==2.0.0
monotonic==0.6
mox3==0.20.0
msgpack-python==0.4.0
munch==2.1.0
netaddr==0.7.18
netifaces==0.10.4
neutron-lib==1.26.0
neutron==14.0.0.0b3
openstackdocstheme==1.18.1
openstacksdk==0.11.2
os-client-config==1.28.0
os-ken==0.3.0
os-service-types==1.2.0
os-xenapi==0.3.1
osc-lib==1.8.0
oslo.cache==1.26.0
oslo.concurrency==3.26.0
oslo.config==5.2.0
oslo.context==2.19.2
oslo.db==4.37.0
oslo.i18n==3.15.3
oslo.log==3.36.0
oslo.messaging==5.29.0
oslo.middleware==3.31.0
oslo.policy==1.30.0
oslo.privsep==1.32.0
oslo.reports==1.18.0
oslo.rootwrap==5.8.0
oslo.serialization==2.18.0
oslo.service==1.24.0
oslo.utils==3.33.0
oslo.versionedobjects==1.31.2
oslotest==3.2.0
osprofiler==1.4.0
ovs==2.8.0
ovsdbapp==0.9.1
Paste==2.0.2
PasteDeploy==1.5.0
pbr==4.0.0
pecan==1.3.2
pep8==1.5.7
pika-pool==0.1.3
pika==0.10.0
positional==1.2.1
prettytable==0.7.2
psutil==3.2.2
psycopg2==2.7.3
pycadf==1.1.0
pycodestyle==2.4.0
pycparser==2.18
pyflakes==2.0.0
Pygments==2.2.0
pyinotify==0.9.6
PyMySQL==0.7.6
pyparsing==2.1.0
pyperclip==1.5.27
pyroute2==0.5.3
python-dateutil==2.5.3
python-designateclient==2.7.0
python-editor==1.0.3
python-keystoneclient==3.8.0
python-mimeparse==1.6.0
python-neutronclient==6.7.0
python-novaclient==9.1.0
python-subunit==1.0.0
pytz==2013.6
PyYAML==3.12
pyzmq==14.3.1
reno==2.5.0
repoze.lru==0.7
requests-mock==1.2.0
requests==2.14.2
requestsexceptions==1.2.0
restructuredtext-lint==1.1.1
rfc3986==0.3.1
Routes==2.3.1
simplejson==3.5.1
six==1.10.0
snowballstemmer==1.2.1
sphinx==1.6.5
sqlalchemy-migrate==0.11.0
SQLAlchemy==1.4.23
sqlparse==0.2.2
statsd==3.2.1
stestr==1.0.0
stevedore==1.20.0
Tempita==0.5.2
tenacity==3.2.1
testrepository==0.0.18
testresources==2.0.0
testscenarios==0.4
testtools==2.2.0
tinyrpc==0.6
traceback2==1.4.0
unittest2==1.1.0
vine==1.1.4
waitress==1.1.0
WebOb==1.8.2
WebTest==2.0.27
wrapt==1.7.0

24
neutron_fwaas/__init__.py Normal file
View File

@ -0,0 +1,24 @@
# 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')

32
neutron_fwaas/_i18n.py Normal file
View File

@ -0,0 +1,32 @@
# 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
def get_available_languages():
return oslo_i18n.get_available_languages(DOMAIN)

View File

View File

@ -0,0 +1,40 @@
# Copyright 2019 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.
from neutron_lib.utils import upgrade_checks as base_checks
from oslo_config import cfg
from oslo_upgradecheck import upgradecheck
from neutron_fwaas._i18n import _
class Checks(base_checks.BaseChecks):
def get_checks(self):
return [
(_("Check FWaaS v1"), self.fwaas_v1_check)
]
@staticmethod
def fwaas_v1_check(checker):
fwaas_v1_names = [
'firewall',
'neutron_fwaas.services.firewall.fwaas_plugin:FirewallPlugin']
for name in fwaas_v1_names:
if name in cfg.CONF.service_plugins:
return upgradecheck.Result(
upgradecheck.Code.FAILURE,
_("FWaaS v1 is removed. "
"FWaaS v2 should be used instead."))
return upgradecheck.Result(upgradecheck.Code.SUCCESS)

View File

@ -0,0 +1,138 @@
# 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 config
from neutron.db import models_v2
from oslo_config import cfg
from oslo_db.sqlalchemy import enginefacade
from oslo_log import log as logging
from neutron_fwaas._i18n import _
from neutron_fwaas.db.firewall import firewall_db as firewall_db_v1
from neutron_fwaas.db.firewall.v2 import firewall_db_v2
LOG = logging.getLogger(__name__)
def setup_conf():
cli_opts = [
cfg.StrOpt('neutron-db-connection',
required=True,
help=_('neutron database connection string')),
]
conf = cfg.CONF
conf.register_cli_opts(cli_opts)
conf()
def migrate_fwaas_v1_to_v2(db_session):
# the entire migration process will be done under the same transaction to
# allow full rollback in case of error
with db_session.begin(subtransactions=True):
# Read all V1 policies
v1_policies = db_session.query(firewall_db_v1.FirewallPolicy)
for v1_pol in v1_policies:
LOG.info("Migrating FWaaS V1 policy %s", v1_pol.id)
# read the rules of this policy
v1_rules = db_session.query(firewall_db_v1.FirewallRule).filter_by(
firewall_policy_id=v1_pol.id).all()
# Create the V2 policy
v2_pol = firewall_db_v2.FirewallPolicy(
id=v1_pol.id,
tenant_id=v1_pol.tenant_id,
name=v1_pol.name,
description=v1_pol.description,
shared=v1_pol.shared,
audited=v1_pol.audited,
rule_count=len(v1_rules))
db_session.add(v2_pol)
# Add the rules and associate them with the policy
for v1_rule in v1_rules:
LOG.info("Migrating FWaaS V1 rule %s", v1_rule.id)
v2_rule = firewall_db_v2.FirewallRuleV2(
id=v1_rule.id,
name=v1_rule.name,
description=v1_rule.description,
tenant_id=v1_rule.tenant_id,
shared=v1_rule.shared,
protocol=v1_rule.protocol,
ip_version=v1_rule.ip_version,
source_ip_address=v1_rule.source_ip_address,
destination_ip_address=v1_rule.destination_ip_address,
source_port_range_min=v1_rule.source_port_range_min,
source_port_range_max=v1_rule.source_port_range_max,
destination_port_range_min=(
v1_rule.destination_port_range_min),
destination_port_range_max=(
v1_rule.destination_port_range_max),
action=v1_rule.action,
enabled=v1_rule.enabled)
db_session.add(v2_rule)
v2_link = firewall_db_v2.FirewallPolicyRuleAssociation(
firewall_policy_id=v1_pol.id,
firewall_rule_id=v1_rule.id,
position=v1_rule.position)
db_session.add(v2_link)
# Read all V1 firewalls
v1_fws = db_session.query(firewall_db_v1.Firewall)
for v1_fw in v1_fws:
LOG.info("Migrating FWaaS V1 firewall %s", v1_fw.id)
# create the V2 firewall group
v2_fw_group = firewall_db_v2.FirewallGroup(
id=v1_fw.id,
name=v1_fw.name,
description=v1_fw.description,
tenant_id=v1_fw.tenant_id,
shared=v1_fw.shared,
admin_state_up=v1_fw.admin_state_up,
status=v1_fw.status,
ingress_firewall_policy_id=v1_fw.firewall_policy_id,
egress_firewall_policy_id=v1_fw.firewall_policy_id)
db_session.add(v2_fw_group)
# for every router in the V1 Firewall router association, add all
# its interface ports to the V2 FirewallGroupPortAssociation
v1_routers = db_session.query(
firewall_db_v1.FirewallRouterAssociation).filter_by(
fw_id=v1_fw.id)
for v1_router in v1_routers:
rtr_id = v1_router.router_id
LOG.info("Migrating FWaaS V1 %s router %s", v1_fw.id, rtr_id)
if_ports = db_session.query(models_v2.Port).filter_by(
device_id=rtr_id,
device_owner="network:router_interface")
for port in if_ports:
fw_port = firewall_db_v2.FirewallGroupPortAssociation(
firewall_group_id=v2_fw_group.id,
port_id=port.id)
db_session.add(fw_port)
def main():
# Initialize the cli options
setup_conf()
config.setup_logging()
# Get the neutron DB session
neutron_context_manager = enginefacade.transaction_context()
neutron_context_manager.configure(
connection=cfg.CONF.neutron_db_connection)
n_session_maker = neutron_context_manager.writer.get_sessionmaker()
n_session = n_session_maker(autocommit=True)
# Run DB migration
migrate_fwaas_v1_to_v2(n_session)
LOG.info("DB migration done.")

View File

View File

@ -0,0 +1,24 @@
# Copyright 2018 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 neutron_lib import exceptions as n_exc
from neutron_fwaas._i18n import _
# TODO(annp): migrate to neutron-lib after Queen release
class FirewallGroupPortNotSupported(n_exc.Conflict):
message = _("Port %(port_id)s is not supported by firewall driver "
"'%(driver_name)s'.")

View File

@ -0,0 +1,42 @@
# 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'
FIREWALL_V2 = 'FIREWALL_V2'
# Constants for "topics"
FIREWALL_PLUGIN = 'q-firewall-plugin'
FW_AGENT = 'firewall_agent'
FIREWALL_RULE_LIST = 'firewall_rule_list'
# V2 Constants
DEFAULT_FWG = 'default'
DEFAULT_FWP_INGRESS = 'default ingress'
DEFAULT_FWP_EGRESS = 'default egress'
# Firewall group events for agent-side
DELETE_FWG = 'delete_firewall_group'
UPDATE_FWG = 'update_firewall_group'
CREATE_FWG = 'create_firewall_group'
# Port events for L2 agent extension
HANDLE_PORT = 'handle_port'
DELETE_PORT = 'delete_port'
# Resource name
FIREWALL_GROUP = 'firewall_group'
FIREWALL_RULE = 'firewall_rule'
FIREWALL_POLICY = 'firewall_policy'

View File

@ -0,0 +1,17 @@
# 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

View File

View File

@ -0,0 +1,89 @@
# 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.db import model_base
import sqlalchemy as sa
from sqlalchemy.ext.orderinglist import ordering_list
from sqlalchemy import orm
# Note(annp): Keep firewall db v1 structure for migration
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 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)

View File

File diff suppressed because it is too large Load Diff

View File

View File

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

View File

@ -0,0 +1,86 @@
# 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

@ -0,0 +1,36 @@
# 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

@ -0,0 +1,35 @@
# 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
"""
from alembic import op
# revision identifiers, used by Alembic.
revision = '4202e3047e47'
down_revision = 'start_neutron_fwaas'
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

@ -0,0 +1,62 @@
# 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
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.engine import reflection
# revision identifiers, used by Alembic.
revision = '540142f314f4'
down_revision = '4202e3047e47'
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

@ -0,0 +1,45 @@
# 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
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '796c68dffbb'
down_revision = '540142f314f4'
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

@ -0,0 +1 @@
fd38cd995cc0

View File

@ -0,0 +1 @@
f24e0d5e5bff

View File

@ -0,0 +1,29 @@
# 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

@ -0,0 +1,38 @@
# 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

@ -0,0 +1,47 @@
# 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

@ -0,0 +1,34 @@
# 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

@ -0,0 +1,49 @@
#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

@ -0,0 +1,143 @@
# 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

@ -0,0 +1,115 @@
# 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'),
nullable=False),
sa.Column('port_id', sa.String(length=36),
sa.ForeignKey('ports.id', ondelete='CASCADE'),
nullable=False)
)
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

@ -0,0 +1,37 @@
# 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
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'fd38cd995cc0'
down_revision = 'f83a0b2964d0'
depends_on = ('d6a12e637e28',)
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

@ -0,0 +1,67 @@
# Copyright 2017 FUJITSU LIMITED
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
"""create_default_firewall_groups_table
Revision ID: 876782258a43
Revises: d6a12e637e28
Create Date: 2017-01-26 23:47:42.795504
"""
from alembic import op
from neutron_lib.db import constants as db_constants
from neutron_lib import exceptions
import sqlalchemy as sa
from neutron_fwaas._i18n import _
from neutron_fwaas.common import fwaas_constants as const
from neutron_fwaas.common import resources
# revision identifiers, used by Alembic.
revision = '876782258a43'
down_revision = 'd6a12e637e28'
class DuplicateDefaultFirewallGroup(exceptions.Conflict):
message = _("Duplicate Firewall group found named '%s'. "
"Database cannot be upgraded. Please, remove all duplicates "
"before upgrading the database.") % const.DEFAULT_FWG
def upgrade():
op.create_table(
'default_firewall_groups',
sa.Column('project_id',
sa.String(length=db_constants.PROJECT_ID_FIELD_SIZE),
nullable=False),
sa.Column('firewall_group_id',
sa.String(length=db_constants.UUID_FIELD_SIZE),
nullable=False),
sa.PrimaryKeyConstraint('project_id'),
sa.ForeignKeyConstraint(['firewall_group_id'],
['firewall_groups_v2.id'], ondelete="CASCADE"))
def check_sanity(connection):
# check for already existing firewall groups with name == DEFAULT_FWG
insp = sa.engine.reflection.Inspector.from_engine(connection)
if 'firewall_groups_v2' not in insp.get_table_names():
return []
session = sa.orm.Session(bind=connection.connect())
default_fwg = session.query(resources.FIREWALL_GROUP.name).filter(
resources.FIREWALL_GROUP.name == const.DEFAULT_FWG).first()
if default_fwg:
raise DuplicateDefaultFirewallGroup()

View File

@ -0,0 +1,71 @@
# Copyright 2017 Fujitsu Limited
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
"""uniq_firewallgroupportassociation0port
Revision ID: f24e0d5e5bff
Revises: 876782258a43
Create Date: 2017-11-08 15:55:40.990272
"""
from alembic import op
from neutron_lib import exceptions
import sqlalchemy as sa
from neutron._i18n import _
# revision identifiers, used by Alembic.
revision = 'f24e0d5e5bff'
down_revision = '876782258a43'
fwg_port_association = sa.Table(
'firewall_group_port_associations_v2', sa.MetaData(),
sa.Column('firewall_group_id', sa.String(36)),
sa.Column('port_id', sa.String(36)))
class DuplicatePortRecordinFirewallGroupPortAssociation(exceptions.Conflict):
message = _("Duplicate port(s) %(port_id)s records exist in"
"firewall_group_port_associations_v2 table. Database cannot"
"be upgraded. Please remove all duplicated records before"
"upgrading the database.")
def upgrade():
op.create_unique_constraint(
'uniq_firewallgroupportassociation0port_id',
'firewall_group_port_associations_v2',
['port_id'])
def check_sanity(connection):
duplicated_port_ids = (
get_duplicate_port_records_in_fwg_port_association(connection))
if duplicated_port_ids:
raise DuplicatePortRecordinFirewallGroupPortAssociation(
port_id=",".join(duplicated_port_ids))
def get_duplicate_port_records_in_fwg_port_association(connection):
insp = sa.engine.reflection.Inspector.from_engine(connection)
if 'firewall_group_port_associations_v2' not in insp.get_table_names():
return []
session = sa.orm.Session(bind=connection.connect())
query = (session.query(fwg_port_association.c.port_id)
.group_by(fwg_port_association.c.port_id)
.having(sa.func.count() > 1)).all()
return [q[0] for q in query]

View File

@ -0,0 +1,30 @@
# 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

View File

@ -0,0 +1,17 @@
# 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
def get_metadata():
return model_base.BASEV2.metadata

View File

View File

@ -0,0 +1,302 @@
# 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.definitions import constants as api_const
from neutron_lib.api.definitions import firewall_v2
from neutron_lib.api import extensions
from neutron_lib.exceptions import firewall_v2 as f_exc
from neutron_lib.services import base as service_base
from oslo_config import cfg
import six
from neutron_fwaas._i18n import _
from neutron_fwaas.common import fwaas_constants
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__)
default_fwg_rules_opts = [
cfg.StrOpt('ingress_action',
default=api_const.FWAAS_DENY,
help=_('Firewall group rule action allow or '
'deny or reject for ingress. '
'Default is deny.')),
cfg.StrOpt('ingress_source_ipv4_address',
default=None,
help=_('IPv4 source address for ingress '
'(address or address/netmask). '
'Default is None.')),
cfg.StrOpt('ingress_source_ipv6_address',
default=None,
help=_('IPv6 source address for ingress '
'(address or address/netmask). '
'Default is None.')),
cfg.StrOpt('ingress_source_port',
default=None,
help=_('Source port number or range '
'(min:max) for ingress. '
'Default is None.')),
cfg.StrOpt('ingress_destination_ipv4_address',
default=None,
help=_('IPv4 destination address for ingress '
'(address or address/netmask). '
'Default is None.')),
cfg.StrOpt('ingress_destination_ipv6_address',
default=None,
help=_('IPv6 destination address for ingress '
'(address or address/netmask). '
'Default is deny.')),
cfg.StrOpt('ingress_destination_port',
default=None,
help=_('Destination port number or range '
'(min:max) for ingress. '
'Default is None.')),
cfg.StrOpt('egress_action',
default=api_const.FWAAS_ALLOW,
help=_('Firewall group rule action allow or '
'deny or reject for egress. '
'Default is allow.')),
cfg.StrOpt('egress_source_ipv4_address',
default=None,
help=_('IPv4 source address for egress '
'(address or address/netmask). '
'Default is None.')),
cfg.StrOpt('egress_source_ipv6_address',
default=None,
help=_('IPv6 source address for egress '
'(address or address/netmask). '
'Default is deny.')),
cfg.StrOpt('egress_source_port',
default=None,
help=_('Source port number or range '
'(min:max) for egress. '
'Default is None.')),
cfg.StrOpt('egress_destination_ipv4_address',
default=None,
help=_('IPv4 destination address for egress '
'(address or address/netmask). '
'Default is deny.')),
cfg.StrOpt('egress_destination_ipv6_address',
default=None,
help=_('IPv6 destination address for egress '
'(address or address/netmask). '
'Default is deny.')),
cfg.StrOpt('egress_destination_port',
default=None,
help=_('Destination port number or range '
'(min:max) for egress. '
'Default is None.')),
cfg.BoolOpt('shared',
default=False,
help=_('Firewall group rule shared. '
'Default is False.')),
cfg.StrOpt('protocol',
default=None,
help=_('Network protocols (tcp, udp, ...). '
'Default is None.')),
cfg.BoolOpt('enabled',
default=True,
help=_('Firewall group rule enabled. '
'Default is True.')),
]
firewall_quota_opts = [
cfg.IntOpt('quota_firewall_group',
default=10,
help=_('Number of firewall groups 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(default_fwg_rules_opts, 'default_fwg_rules')
cfg.CONF.register_opts(firewall_quota_opts, 'QUOTAS')
# TODO(Reedip): Remove the convert_to functionality after bug1706061 is fixed.
def convert_to_string(value):
if value is not None:
return str(value)
return None
firewall_v2.RESOURCE_ATTRIBUTE_MAP[api_const.FIREWALL_RULES][
'source_port']['convert_to'] = convert_to_string
firewall_v2.RESOURCE_ATTRIBUTE_MAP[api_const.FIREWALL_RULES][
'destination_port']['convert_to'] = convert_to_string
class Firewall_v2(extensions.APIExtensionDescriptor):
api_definition = firewall_v2
@classmethod
def get_resources(cls):
special_mappings = {'firewall_policies': 'firewall_policy'}
plural_mappings = resource_helper.build_plural_mappings(
special_mappings, firewall_v2.RESOURCE_ATTRIBUTE_MAP)
return resource_helper.build_resource_info(
plural_mappings, firewall_v2.RESOURCE_ATTRIBUTE_MAP,
fwaas_constants.FIREWALL_V2, action_map=firewall_v2.ACTION_MAP,
register_quota=True)
@classmethod
def get_plugin_interface(cls):
return Firewallv2PluginBase
@six.add_metaclass(abc.ABCMeta)
class Firewallv2PluginBase(service_base.ServicePluginBase):
def get_plugin_type(self):
return fwaas_constants.FIREWALL_V2
def get_plugin_description(self):
return 'Firewall Service v2 Plugin'
# Firewall Group
@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, fields=None):
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
# Firewall Policy
@abc.abstractmethod
def create_firewall_policy(self, context, firewall_policy):
pass
@abc.abstractmethod
def delete_firewall_policy(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 update_firewall_policy(self, context, id, firewall_policy):
pass
# Firewall Rule
@abc.abstractmethod
def create_firewall_rule(self, context, firewall_rule):
pass
@abc.abstractmethod
def delete_firewall_rule(self, context, id):
pass
@abc.abstractmethod
def get_firewall_rule(self, context, id, fields=None):
pass
@abc.abstractmethod
def get_firewall_rules(self, context, filters=None, fields=None):
pass
@abc.abstractmethod
def update_firewall_rule(self, context, id, firewall_rule):
pass
@abc.abstractmethod
def insert_rule(self, context, id, rule_info):
pass
@abc.abstractmethod
def remove_rule(self, context, id, rule_info):
pass

36
neutron_fwaas/opts.py Normal file
View File

@ -0,0 +1,36 @@
# 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.conf.services.provider_configuration
import neutron_fwaas.services.firewall.service_drivers.agents.\
firewall_agent_api
import neutron_fwaas.extensions.firewall_v2
def list_agent_opts():
return [
('fwaas',
neutron_fwaas.services.firewall.service_drivers.agents.
firewall_agent_api.FWaaSOpts),
]
def list_opts():
return [
('quotas',
neutron_fwaas.extensions.firewall_v2.firewall_quota_opts),
('service_providers',
neutron.conf.services.provider_configuration.serviceprovider_opts),
('default_fwg_rules',
neutron_fwaas.extensions.firewall_v2.default_fwg_rules_opts),
]

View File

@ -0,0 +1,25 @@
# 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 itertools
from neutron_fwaas.policies import firewall_group
from neutron_fwaas.policies import firewall_policy
from neutron_fwaas.policies import firewall_rule
def list_rules():
return itertools.chain(
firewall_group.list_rules(),
firewall_policy.list_rules(),
firewall_rule.list_rules(),
)

View File

@ -0,0 +1,17 @@
# 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.
# TODO(amotoki): Define these in neutron or neutron-lib
RULE_ADMIN_OR_OWNER = 'rule:admin_or_owner'
RULE_ADMIN_ONLY = 'rule:admin_only'
RULE_ANY = 'rule:regular_user'

View File

@ -0,0 +1,113 @@
# 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_policy import policy
from neutron_fwaas.policies import base
rules = [
policy.RuleDefault(
'shared_firewall_groups',
'field:firewall_groups:shared=True',
'Definition of shared firewall groups'
),
policy.DocumentedRuleDefault(
'create_firewall_group',
base.RULE_ANY,
'Create a firewall group',
[
{
'method': 'POST',
'path': '/fwaas/firewall_groups',
},
]
),
policy.DocumentedRuleDefault(
'update_firewall_group',
base.RULE_ADMIN_OR_OWNER,
'Update a firewall group',
[
{
'method': 'PUT',
'path': '/fwaas/firewall_groups/{id}',
},
]
),
policy.DocumentedRuleDefault(
'delete_firewall_group',
base.RULE_ADMIN_OR_OWNER,
'Delete a firewall group',
[
{
'method': 'DELETE',
'path': '/fwaas/firewall_groups/{id}',
},
]
),
policy.DocumentedRuleDefault(
'create_firewall_group:shared',
base.RULE_ADMIN_ONLY,
'Create a shared firewall group',
[
{
'method': 'POST',
'path': '/fwaas/firewall_groups',
},
]
),
policy.DocumentedRuleDefault(
'update_firewall_group:shared',
base.RULE_ADMIN_ONLY,
'Update ``shared`` attribute of a firewall group',
[
{
'method': 'PUT',
'path': '/fwaas/firewall_groups/{id}',
},
]
),
# TODO(amotoki): Drop this rule as it has no effect.
policy.DocumentedRuleDefault(
'delete_firewall_group:shared',
base.RULE_ADMIN_ONLY,
'Delete a shared firewall group',
[
{
'method': 'DELETE',
'path': '/fwaas/firewall_groups/{id}',
},
]
),
policy.DocumentedRuleDefault(
'get_firewall_group',
'rule:admin_or_owner or rule:shared_firewall_groups',
'Get firewall groups',
[
{
'method': 'GET',
'path': '/fwaas/firewall_groups',
},
{
'method': 'GET',
'path': '/fwaas/firewall_groups/{id}',
},
]
),
]
def list_rules():
return rules

View File

@ -0,0 +1,113 @@
# 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_policy import policy
from neutron_fwaas.policies import base
rules = [
policy.RuleDefault(
'shared_firewall_policies',
'field:firewall_policies:shared=True',
'Definition of shared firewall policies'
),
policy.DocumentedRuleDefault(
'create_firewall_policy',
base.RULE_ANY,
'Create a firewall policy',
[
{
'method': 'POST',
'path': '/fwaas/firewall_policies',
},
]
),
policy.DocumentedRuleDefault(
'update_firewall_policy',
base.RULE_ADMIN_OR_OWNER,
'Update a firewall policy',
[
{
'method': 'PUT',
'path': '/fwaas/firewall_policies/{id}',
},
]
),
policy.DocumentedRuleDefault(
'delete_firewall_policy',
base.RULE_ADMIN_OR_OWNER,
'Delete a firewall policy',
[
{
'method': 'DELETE',
'path': '/fwaas/firewall_policies/{id}',
},
]
),
policy.DocumentedRuleDefault(
'create_firewall_policy:shared',
base.RULE_ADMIN_ONLY,
'Create a shared firewall policy',
[
{
'method': 'POST',
'path': '/fwaas/firewall_policies',
},
]
),
policy.DocumentedRuleDefault(
'update_firewall_policy:shared',
base.RULE_ADMIN_ONLY,
'Update ``shared`` attribute of a firewall policy',
[
{
'method': 'PUT',
'path': '/fwaas/firewall_policies/{id}',
},
]
),
# TODO(amotoki): Drop this rule as it has no effect.
policy.DocumentedRuleDefault(
'delete_firewall_policy:shared',
base.RULE_ADMIN_ONLY,
'Delete a shread firewall policy',
[
{
'method': 'DELETE',
'path': '/fwaas/firewall_policies/{id}',
},
]
),
policy.DocumentedRuleDefault(
'get_firewall_policy',
'rule:admin_or_owner or rule:shared_firewall_policies',
'Get firewall policies',
[
{
'method': 'GET',
'path': '/fwaas/firewall_policies',
},
{
'method': 'GET',
'path': '/fwaas/firewall_policies/{id}',
},
]
),
]
def list_rules():
return rules

View File

@ -0,0 +1,136 @@
# 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_policy import policy
from neutron_fwaas.policies import base
rules = [
policy.RuleDefault(
'shared_firewall_rules',
'field:firewall_rules:shared=True',
'Definition of shared firewall rules'
),
policy.DocumentedRuleDefault(
'create_firewall_rule',
base.RULE_ANY,
'Create a firewall rule',
[
{
'method': 'POST',
'path': '/fwaas/firewall_rules',
},
]
),
policy.DocumentedRuleDefault(
'update_firewall_rule',
base.RULE_ADMIN_OR_OWNER,
'Update a firewall rule',
[
{
'method': 'PUT',
'path': '/fwaas/firewall_rules/{id}',
},
]
),
policy.DocumentedRuleDefault(
'delete_firewall_rule',
base.RULE_ADMIN_OR_OWNER,
'Delete a firewall rule',
[
{
'method': 'DELETE',
'path': '/fwaas/firewall_rules/{id}',
},
]
),
policy.DocumentedRuleDefault(
'create_firewall_rule:shared',
base.RULE_ADMIN_ONLY,
'Create a shared firewall rule',
[
{
'method': 'POST',
'path': '/fwaas/firewall_rules',
},
]
),
policy.DocumentedRuleDefault(
'update_firewall_rule:shared',
base.RULE_ADMIN_ONLY,
'Update ``shared`` attribute of a firewall rule',
[
{
'method': 'PUT',
'path': '/fwaas/firewall_rules/{id}',
},
]
),
# TODO(amotoki): Drop this rule as it has no effect.
policy.DocumentedRuleDefault(
'delete_firewall_rule:shared',
base.RULE_ADMIN_ONLY,
'Delete a shread firewall rule',
[
{
'method': 'DELETE',
'path': '/fwaas/firewall_rules/{id}',
},
]
),
policy.DocumentedRuleDefault(
'get_firewall_rule',
'rule:admin_or_owner or rule:shared_firewall_rules',
'Get firewall rules',
[
{
'method': 'GET',
'path': '/fwaas/firewall_rules',
},
{
'method': 'GET',
'path': '/fwaas/firewall_rules/{id}',
},
]
),
policy.DocumentedRuleDefault(
'insert_rule',
base.RULE_ADMIN_OR_OWNER,
'Insert rule into a firewall policy',
[
{
'method': 'PUT',
'path': '/fwaas/firewall_policies/{id}/insert_rule',
},
]
),
policy.DocumentedRuleDefault(
'remove_rule',
base.RULE_ADMIN_OR_OWNER,
'Remove rule from a firewall policy',
[
{
'method': 'PUT',
'path': '/fwaas/firewall_policies/{id}/remove_rule',
},
]
),
]
def list_rules():
return rules

View File

@ -0,0 +1,29 @@
# 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

@ -0,0 +1,331 @@
# Copyright (c) 2018 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 multiprocessing
import socket
import struct
import time
import cffi
import eventlet
from eventlet.green import zmq
from neutron_lib.utils import runtime
from os_ken.lib import addrconv
from os_ken.lib.packet import arp
from os_ken.lib.packet import ether_types
from os_ken.lib.packet import ethernet
from os_ken.lib.packet import ipv4
from os_ken.lib.packet import ipv6
from oslo_log import log as logging
from oslo_serialization import jsonutils
from oslo_utils import encodeutils
from oslo_utils import excutils
from neutron_fwaas._i18n import _
from neutron_fwaas import privileged
from neutron_fwaas.privileged import utils as fwaas_utils
LOG = logging.getLogger(__name__)
# TODO(annp): consider to make a pub-sub pattern which allows other logging
# driver like snat log can consume libnetfilter_log
NETFILTER_LOG = 'netfilter_log'
ADDR_IPC = "ipc:///var/run/nflog"
CDEF = '''
typedef unsigned char u_int8_t;
typedef unsigned short int u_int16_t;
typedef unsigned int u_int32_t;
struct nfulnl_msg_packet_hdr {
u_int16_t hw_protocol; // hw protocol (network order)
u_int8_t hook; // netfilter hook
u_int8_t _pad;
};
int nflog_fd(struct nflog_handle *h);
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
struct nflog_handle *nflog_open(void);
int nflog_close(struct nflog_handle *h);
int nflog_bind_pf(struct nflog_handle *h, u_int16_t pf);
int nflog_unbind_pf(struct nflog_handle *h, u_int16_t pf);
struct nflog_g_handle *nflog_bind_group(struct nflog_handle *h, u_int16_t num);
int nflog_unbind_group(struct nflog_g_handle *gh);
static const u_int8_t NFULNL_COPY_PACKET;
int nflog_set_mode(struct nflog_g_handle *gh, u_int8_t mode, unsigned int len);
int nflog_set_timeout(struct nflog_g_handle *gh, u_int32_t timeout);
int nflog_set_flags(struct nflog_g_handle *gh, u_int16_t flags);
int nflog_set_qthresh(struct nflog_g_handle *gh, u_int32_t qthresh);
int nflog_set_nlbufsiz(struct nflog_g_handle *gh, u_int32_t nlbufsiz);
typedef int nflog_callback(struct nflog_g_handle *gh,
struct nfgenmsg *nfmsg, struct nflog_data *nfd, void *data);
int nflog_callback_register(
struct nflog_g_handle *gh, nflog_callback *cb, void *data);
int nflog_handle_packet(struct nflog_handle *h, char *buf, int len);
struct nfulnl_msg_packet_hdr *nflog_get_msg_packet_hdr(
struct nflog_data *nfad);
u_int16_t nflog_get_hwtype(struct nflog_data *nfad);
u_int16_t nflog_get_msg_packet_hwhdrlen(struct nflog_data *nfad);
char *nflog_get_msg_packet_hwhdr(struct nflog_data *nfad);
u_int32_t nflog_get_nfmark(struct nflog_data *nfad);
int nflog_get_timestamp(struct nflog_data *nfad, struct timeval *tv);
u_int32_t nflog_get_indev(struct nflog_data *nfad);
u_int32_t nflog_get_physindev(struct nflog_data *nfad);
u_int32_t nflog_get_outdev(struct nflog_data *nfad);
u_int32_t nflog_get_physoutdev(struct nflog_data *nfad);
struct nfulnl_msg_packet_hw *nflog_get_packet_hw(struct nflog_data *nfad);
int nflog_get_payload(struct nflog_data *nfad, char **data);
char *nflog_get_prefix(struct nflog_data *nfad);
'''
ffi = None
libnflog = None
def init_library():
"""Load libnetfilter_log library"""
global ffi
global libnflog
if not ffi:
ffi = cffi.FFI()
ffi.cdef(CDEF)
if not libnflog:
try:
libnflog = ffi.dlopen(NETFILTER_LOG)
except OSError:
msg = "Could not found libnetfilter-log"
raise Exception(msg)
return ffi, libnflog
ffi, libnflog = init_library()
def _payload(nfa):
buf = ffi.new('char **')
pkt_len = libnflog.nflog_get_payload(nfa, buf)
if pkt_len <= 0:
return None
return ffi.buffer(buf[0], pkt_len)[:]
def decode(nfa):
"""This function analyses nflog packet by using os-ken packet library."""
prefix = ffi.string(libnflog.nflog_get_prefix(nfa))
packet_hdr = libnflog.nflog_get_msg_packet_hdr(nfa)
hw_proto = socket.ntohs(packet_hdr.hw_protocol)
msg = ''
msg_packet_hwhdr = libnflog.nflog_get_msg_packet_hwhdr(nfa)
if msg_packet_hwhdr != ffi.NULL:
packet_hwhdr = ffi.string(msg_packet_hwhdr)
if len(packet_hwhdr) >= 12:
dst, src = struct.unpack_from('!6s6s', packet_hwhdr)
# Dump ethernet packet to get mac addresses
eth = ethernet.ethernet(addrconv.mac.bin_to_text(dst),
addrconv.mac.bin_to_text(src),
ethertype=hw_proto)
msg = str(eth)
# Dump IP packet
pkt = _payload(nfa)
if hw_proto == ether_types.ETH_TYPE_IP:
ip_pkt, proto, data = ipv4.ipv4().parser(pkt)
msg += str(ip_pkt)
proto_pkt, a, b = proto.parser(data)
msg += str(proto_pkt)
elif hw_proto == ether_types.ETH_TYPE_IPV6:
ip_pkt, proto, data = ipv6.ipv6().parser(pkt)
proto_pkt, a, b = proto.parser(data)
msg += str(proto_pkt)
elif hw_proto == ether_types.ETH_TYPE_ARP:
ip_pkt, proto, data = arp.arp().parser(pkt)
msg += str(ip_pkt)
else:
msg += "Does not support hw_proto: " + str(hw_proto)
return {'prefix': encodeutils.safe_decode(prefix),
'msg': encodeutils.safe_decode(msg)}
class NFLogWrapper(object):
"""A wrapper for libnetfilter_log api"""
_instance = None
def __init__(self):
self.nflog_g_hanldes = {}
@classmethod
@runtime.synchronized("nflog-wrapper")
def _create_instance(cls):
if not cls.has_instance():
cls._instance = cls()
@classmethod
def has_instance(cls):
return cls._instance is not None
@classmethod
def clear_instance(cls):
cls._instance = None
@classmethod
def get_instance(cls):
# double checked locking
if not cls.has_instance():
cls._create_instance()
return cls._instance
def open(self):
self.nflog_handle = libnflog.nflog_open()
if not self.nflog_handle:
msg = _("Could not open nflog handle")
raise Exception(msg)
self._bind_pf()
def close(self):
if self.nflog_handle:
libnflog.nflog_close(self.nflog_handle)
def bind_group(self, group):
g_handle = libnflog.nflog_bind_group(self.nflog_handle, group)
if g_handle:
self.nflog_g_hanldes[group] = g_handle
self._set_mode(g_handle, 0x2, 0xffff)
self._set_callback(g_handle, self.cb)
def _bind_pf(self):
for pf in (socket.AF_INET, socket.AF_INET6):
libnflog.nflog_unbind_pf(self.nflog_handle, pf)
libnflog.nflog_bind_pf(self.nflog_handle, pf)
def unbind_group(self, group):
try:
g_handle = self.nflog_g_hanldes[group]
if g_handle:
libnflog.nflog_unbind_group(g_handle)
except Exception:
pass
def _set_mode(self, g_handle, mode, len):
ret = libnflog.nflog_set_mode(g_handle, mode, len)
if ret != 0:
msg = _("Could not set mode for nflog")
raise Exception(msg)
@ffi.callback("nflog_callback")
def cb(gh, nfmsg, nfa, data):
ev = decode(nfa)
msg = jsonutils.dumps(ev) + '\n'
ctx = zmq.Context(1)
pub = ctx.socket(zmq.XREQ)
pub.bind(ADDR_IPC)
pub.send(msg.encode('utf-8'))
pub.close()
return 0
def _set_callback(self, g_handle, cb):
ret = libnflog.nflog_callback_register(g_handle, cb, ffi.NULL)
if ret != 0:
msg = _("Could not set callback for nflog")
raise Exception(msg)
def run_loop(self):
fd = libnflog.nflog_fd(self.nflog_handle)
buff = ffi.new('char[]', 4096)
while True:
try:
pkt_len = libnflog.recv(fd, buff, 4096, 0)
except OSError as err:
# No buffer space available
if err.errno == 11:
continue
msg = _("Unknown exception")
raise Exception(msg)
if pkt_len > 0:
libnflog.nflog_handle_packet(self.nflog_handle, buff, pkt_len)
time.sleep(1.0)
def start(self):
nflog_process = multiprocessing.Process(target=self.run_loop)
nflog_process.daemon = True
nflog_process.start()
return nflog_process.pid
@privileged.default.entrypoint
def run_nflog(namespace=None, group=0):
"""Run a nflog process under a namespace
This process will listen nflog packets, which are sent from kernel to
userspace. Then it decode these packets and send it to IPC address for log
application.
"""
with fwaas_utils.in_namespace(namespace):
try:
handle = NFLogWrapper.get_instance()
handle.open()
handle.bind_group(group)
pid = handle.start()
except Exception:
with excutils.save_and_reraise_exception():
LOG.exception("NFLOG thread died of an exception")
try:
handle.unbind_group(group)
handle.close()
except Exception:
pass
return pid
class NFLogApp(object):
"""Log application for handling nflog packets"""
callback = None
def register_packet_handler(self, caller):
self.callback = caller
def unregister_packet_handler(self):
self.callback = None
def start(self):
def loop():
while True:
if self.callback:
ctx = zmq.Context(1)
sub = ctx.socket(zmq.XREQ)
sub.connect(ADDR_IPC)
msg = sub.recv()
if len(msg):
self.callback(jsonutils.loads(msg))
sub.close()
time.sleep(1.0)
# Spawn loop
eventlet.spawn_n(loop)

View File

@ -0,0 +1,86 @@
# 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
# IPv6 address memory buffer
ADDR_BUFFER_6 = 16
ADDR_BUFFER_4 = 4
IPVERSION_SOCKET = {4: socket.AF_INET, 6: socket.AF_INET6}
IPVERSION_BUFFER = {4: ADDR_BUFFER_4, 6: ADDR_BUFFER_6}

View File

@ -0,0 +1,314 @@
# 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 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_lib = util.find_library('netfilter_conntrack')
nfct = ctypes.CDLL(nfct_lib)
libc = ctypes.CDLL(util.find_library('libc.so.6'))
# In unit tests the actual nfct library may not be installed, and since we
# don't make actual calls to it we don't want to add a hard dependency.
if nfct_lib:
# It's important that the types be defined properly on all of the functions
# we call from nfct, otherwise pointers can be truncated and cause
# segfaults.
nfct.nfct_set_attr.argtypes = [ctypes.c_void_p,
ctypes.c_int,
ctypes.c_void_p]
nfct.nfct_set_attr_u8.argtypes = [ctypes.c_void_p,
ctypes.c_int,
ctypes.c_uint8]
nfct.nfct_set_attr_u16.argtypes = [ctypes.c_void_p,
ctypes.c_int,
ctypes.c_uint16]
nfct.nfct_snprintf.argtypes = [ctypes.c_char_p,
ctypes.c_uint,
ctypes.c_void_p,
ctypes.c_uint,
ctypes.c_uint,
ctypes.c_uint]
nfct.nfct_new.restype = ctypes.c_void_p
nfct.nfct_destroy.argtypes = [ctypes.c_void_p]
nfct.nfct_query.argtypes = [ctypes.c_void_p,
ctypes.c_int,
ctypes.c_void_p]
nfct.nfct_callback_register.argtypes = [ctypes.c_void_p,
ctypes.c_int,
ctypes.c_void_p,
ctypes.c_void_p]
nfct.nfct_open.restype = ctypes.c_void_p
nfct.nfct_close.argtypes = [ctypes.c_void_p]
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)],
'icmpv6': [('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,
6: nfct.nfct_set_attr},
'dst': {4: nfct.nfct_set_attr,
6: nfct.nfct_set_attr},
'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': bytes,
'dst': bytes,
'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.decode('utf-8'))
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("Netlink query failed")
def _convert_text_to_binary(self, source, addr_family):
dest = ctypes.create_string_buffer(
nl_constants.IPVERSION_BUFFER[addr_family])
libc.inet_pton(nl_constants.IPVERSION_SOCKET[addr_family],
source.encode('utf-8'), dest)
return dest.raw
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]
if attr in ['src', 'dst']:
# convert src and dst of IPv4 and IPv6 into same format
value = self._convert_text_to_binary(value, ipversion)
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

@ -0,0 +1,29 @@
# 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

@ -0,0 +1,39 @@
# 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 pyroute2
from neutron_fwaas import privileged
from neutron_fwaas.privileged import utils
def _get_ifname(link):
attr_dict = dict(link['attrs'])
return attr_dict['IFLA_IFNAME']
def list_interface_names():
iproute = pyroute2.IPRoute()
result = iproute.get_links()
return [_get_ifname(link) for link in result]
@privileged.default.entrypoint
def get_in_namespace_interfaces(namespace):
before = list_interface_names()
with utils.in_namespace(namespace):
inside = list_interface_names()
after = list_interface_names()
return before, inside, after

View File

@ -0,0 +1,58 @@
# 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)
pynetns.setns(namespace)
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)

View File

View File

@ -0,0 +1,429 @@
# 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 servicetype_db as st_db
from neutron import service
from neutron.services import provider_configuration as provider_conf
from neutron.services import service_base
from neutron_lib.api.definitions import firewall_v2
from neutron_lib.api.definitions import portbindings as pb_def
from neutron_lib.api import validators
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.exceptions import firewall_v2 as f_exc
from neutron_lib.plugins import constants as plugin_const
from neutron_lib.plugins import directory
from oslo_log import helpers as log_helpers
from oslo_log import log as logging
from neutron_fwaas.common import exceptions
from neutron_fwaas.common import fwaas_constants
from neutron_fwaas.extensions.firewall_v2 import Firewallv2PluginBase
from neutron_fwaas.services.firewall.service_drivers import driver_api
from neutron_fwaas.services.logapi.agents.drivers.iptables \
import driver as logging_driver
LOG = logging.getLogger(__name__)
@registry.has_registry_receivers
class FirewallPluginV2(Firewallv2PluginBase):
"""Firewall v2 Neutron service plugin class"""
supported_extension_aliases = [firewall_v2.ALIAS]
path_prefix = firewall_v2.API_PREFIX
def __init__(self):
super(FirewallPluginV2, self).__init__()
"""Do the initialization for the firewall service plugin here."""
# Initialize the Firewall v2 service plugin
service_type_manager = st_db.ServiceTypeManager.get_instance()
service_type_manager.add_provider_configuration(
fwaas_constants.FIREWALL_V2,
provider_conf.ProviderConfiguration('neutron_fwaas'))
# Load the default driver
drivers, default_provider = service_base.load_drivers(
fwaas_constants.FIREWALL_V2, self)
LOG.info("Firewall v2 Service Plugin using Service Driver: %s",
default_provider)
if len(drivers) > 1:
LOG.warning("Multiple drivers configured for Firewall v2, "
"although running multiple drivers in parallel is "
"not yet supported")
self.driver_name = default_provider
self.driver = drivers[default_provider]
# start rpc listener if driver required
if isinstance(self.driver, driver_api.FirewallDriverRPCMixin):
rpc_worker = service.RpcWorker([self], worker_process_count=0)
self.add_worker(rpc_worker)
log_plugin = directory.get_plugin(plugin_const.LOG_API)
logging_driver.register()
# If log_plugin was loaded before firewall plugin
if log_plugin:
# Register logging driver with LoggingServiceDriverManager again
log_plugin.driver_manager.register_driver(logging_driver.DRIVER)
def start_rpc_listeners(self):
return self.driver.start_rpc_listener()
@property
def _core_plugin(self):
return directory.get_plugin()
def _ensure_update_firewall_group(self, context, fwg_id):
"""Checks if the firewall group can be updated
Raises FirewallGroupInPendingState if the firewall group is in pending
state.
:param context: neutron context
:param fwg_id: firewall group ID to check
:return: Firewall group dict
"""
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'])
return fwg
def _ensure_update_firewall_policy(self, context, fwp_id):
"""Checks if the firewall policy can be updated
Fetch firewall group associated to the policy and checks if they can be
updated.
:param context: neutron context
:param fwp_id: firewall policy ID to check
"""
fwp = self.get_firewall_policy(context, fwp_id)
ing_fwg_ids, eg_fwg_ids = self._get_fwgs_with_policy(context, fwp)
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):
"""Checks if the firewall rule can be updated
Fetch firewall policy associated to the rule and checks if they can be
updated.
:param context: neutron context
:param fwr_id: firewall policy ID to check
"""
fwr = self.get_firewall_rule(context, fwr_id)
fwp_ids = self._get_policies_with_rule(context, fwr)
for fwp_id in fwp_ids:
self._ensure_update_firewall_policy(context, fwp_id)
def _validate_firewall_policies_for_firewall_group(self, context, fwg):
"""Validate firewall group and policy owner
Check if the firewall policy is not shared, it have the same project
owner than the friewall group.
:param context: neutron context
:param fwg: firewall group to validate
"""
for policy_type in ['ingress_firewall_policy_id',
'egress_firewall_policy_id']:
if fwg.get(policy_type):
fwp = self.get_firewall_policy(context, fwg[policy_type])
if fwg['tenant_id'] != fwp['tenant_id'] and not fwp['shared']:
raise f_exc.FirewallPolicyConflict(
firewall_policy_id=fwg[policy_type])
def _validate_ports_for_firewall_group(self, context, tenant_id,
fwg_ports):
"""Validate firewall group associated ports
Check if the firewall group associated ports have the same project
owner and is router interface type or a compute layer 2 and supported
by the firewall driver
:param context: neutron context
:param tenant_id: firewall group project ID
:param fwg_ports: firewall group associated ports
"""
# TODO(sridar): elevated context and do we want to use public ?
for port_id in fwg_ports:
port = self._core_plugin.get_port(context, port_id)
if port['tenant_id'] != tenant_id:
raise f_exc.FirewallGroupPortInvalidProject(
port_id=port_id, project_id=port['tenant_id'])
device_owner = port.get('device_owner', '')
if device_owner in nl_constants.ROUTER_INTERFACE_OWNERS:
if not self.driver.is_supported_l3_port(port):
raise exceptions.FirewallGroupPortNotSupported(
driver_name=self.driver_name, port_id=port_id)
elif device_owner.startswith(
nl_constants.DEVICE_OWNER_COMPUTE_PREFIX):
if not self._is_supported_l2_port(context, port_id):
raise exceptions.FirewallGroupPortNotSupported(
driver_name=self.driver_name, port_id=port_id)
else:
raise f_exc.FirewallGroupPortInvalid(port_id=port_id)
def _is_supported_l2_port(self, context, port_id):
"""Whether this l2 port is supported"""
# Re-fetch to get up-to-date data from db
port = self._core_plugin.get_port(context, id=port_id)
# Skip port binding is unbound or failed
if port[pb_def.VIF_TYPE] in [pb_def.VIF_TYPE_UNBOUND,
pb_def.VIF_TYPE_BINDING_FAILED]:
return False
return self.driver.is_supported_l2_port(port)
def _validate_if_firewall_group_on_ports(self, context, firewall_group,
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.
:param context: neutron context
:param fwg: firewall group to validate
"""
if 'ports' not in firewall_group or not firewall_group['ports']:
return
filters = {
'tenant_id': [firewall_group['tenant_id']],
'ports': firewall_group['ports'],
}
ports_in_use = set()
for fwg in self.get_firewall_groups(context, filters=filters):
if id is not None and fwg['id'] == id:
continue
ports_in_use |= set(fwg.get('ports', [])) & \
set(firewall_group['ports'])
if ports_in_use:
raise f_exc.FirewallGroupPortInUse(port_ids=list(ports_in_use))
def _get_fwgs_with_policy(self, context, firewall_policy):
"""List firewall group IDs which use a firewall policy
List all firewall group IDs which have the given firewall policy as
ingress or egress.
:param context: neutron context
:param firewall_policy: firewall policy to filter
"""
filters = {
'tenant_id': [firewall_policy['tenant_id']],
'ingress_firewall_policy_id': [firewall_policy['id']],
}
ingress_fwp_ids = [fwg['id']
for fwg in self.get_firewall_groups(
context, filters=filters)]
filters = {
'tenant_id': [firewall_policy['tenant_id']],
'egress_firewall_policy_id': [firewall_policy['id']],
}
egress_fwp_ids = [fwg['id']
for fwg in self.get_firewall_groups(
context, filters=filters)]
return ingress_fwp_ids, egress_fwp_ids
def _get_policies_with_rule(self, context, firewall_rule):
filters = {
'tenant_id': [firewall_rule['tenant_id']],
'firewall_rules': [firewall_rule['id']],
}
return [fwp['id'] for fwp in self.get_firewall_policies(
context, filters=filters)]
def _validate_insert_remove_rule_request(self, rule_info):
"""Validate rule_info dict
Check that all mandatory fields are present, otherwise raise
proper exception.
"""
if not rule_info or 'firewall_rule_id' not in rule_info:
raise f_exc.FirewallRuleInfoMissing()
# Validator doesn't return anything if the check passes
if validators.validate_uuid(rule_info['firewall_rule_id']):
raise f_exc.FirewallRuleNotFound(
firewall_rule_id=rule_info['firewall_rule_id'])
@registry.receives(resources.PORT, [events.AFTER_UPDATE])
def handle_update_port(self, resource, event, trigger, payload):
context = payload.context
original_port = payload.states[0]
updated_port = payload.states[1]
if not updated_port['device_owner'].startswith(
nl_constants.DEVICE_OWNER_COMPUTE_PREFIX):
return
if (original_port[pb_def.VIF_TYPE] != pb_def.VIF_TYPE_UNBOUND):
# Checking newly vm port binding allows us to avoid call to DB
# when a port update_event like restart, setting name, etc...
# Moreover, that will help us in case of tenant admin wants to
# only attach security group to vm port.
return
port_id = updated_port['id']
# Check port is supported by firewall driver
if not self._is_supported_l2_port(context, port_id):
return
project_id = updated_port['project_id']
fwgs = self.get_firewall_groups(
context,
filters={
'tenant_id': [project_id],
'name': [fwaas_constants.DEFAULT_FWG],
},
fields=['id', 'ports'],
)
if len(fwgs) != 1:
# Cannot found default Firewall Group, abandon
LOG.warning("Cannot found default firewall group of project %s",
project_id)
return
default_fwg = fwgs[0]
# Add default firewall group to the port
port_ids = default_fwg.get('ports', []) + [port_id]
try:
self.update_firewall_group(context, default_fwg['id'],
{'firewall_group': {'ports': port_ids}})
except f_exc.FirewallGroupPortInUse:
LOG.warning("Port %s has been already associated with default "
"firewall group %s and skip association", port_id,
default_fwg['id'])
# Firewall Group
@log_helpers.log_method_call
def create_firewall_group(self, context, firewall_group):
firewall_group = firewall_group['firewall_group']
ports = firewall_group.get('ports', [])
self._validate_firewall_policies_for_firewall_group(context,
firewall_group)
# Validate ports owner type and project
self._validate_ports_for_firewall_group(context,
firewall_group['tenant_id'],
ports)
self._validate_if_firewall_group_on_ports(context, firewall_group)
return self.driver.create_firewall_group(context, firewall_group)
@log_helpers.log_method_call
def delete_firewall_group(self, context, id):
# if no such group exists -> don't raise an exception according to
# 80fe2ba1, return None
try:
fwg = self.get_firewall_group(context, id)
except f_exc.FirewallGroupNotFound:
return
if fwg['status'] == nl_constants.ACTIVE:
raise f_exc.FirewallGroupInUse(firewall_id=id)
self.driver.delete_firewall_group(context, id)
@log_helpers.log_method_call
def get_firewall_group(self, context, id, fields=None):
return self.driver.get_firewall_group(context, id, fields=fields)
@log_helpers.log_method_call
def get_firewall_groups(self, context, filters=None, fields=None):
return self.driver.get_firewall_groups(context, filters, fields)
@log_helpers.log_method_call
def update_firewall_group(self, context, id, firewall_group):
firewall_group = firewall_group['firewall_group']
ports = firewall_group.get('ports', [])
old_firewall_group = self._ensure_update_firewall_group(context, id)
firewall_group['tenant_id'] = old_firewall_group['tenant_id']
self._validate_firewall_policies_for_firewall_group(context,
firewall_group)
# Validate ports owner type and project
self._validate_ports_for_firewall_group(context,
firewall_group['tenant_id'],
ports)
self._validate_if_firewall_group_on_ports(context, firewall_group,
id=id)
return self.driver.update_firewall_group(context, id, firewall_group)
# Firewall Policy
@log_helpers.log_method_call
def create_firewall_policy(self, context, firewall_policy):
firewall_policy = firewall_policy['firewall_policy']
return self.driver.create_firewall_policy(context, firewall_policy)
@log_helpers.log_method_call
def delete_firewall_policy(self, context, id):
self.driver.delete_firewall_policy(context, id)
@log_helpers.log_method_call
def get_firewall_policy(self, context, id, fields=None):
return self.driver.get_firewall_policy(context, id, fields)
@log_helpers.log_method_call
def get_firewall_policies(self, context, filters=None, fields=None):
return self.driver.get_firewall_policies(context, filters, fields)
@log_helpers.log_method_call
def update_firewall_policy(self, context, id, firewall_policy):
firewall_policy = firewall_policy['firewall_policy']
self._ensure_update_firewall_policy(context, id)
return self.driver.update_firewall_policy(context, id, firewall_policy)
# Firewall Rule
@log_helpers.log_method_call
def create_firewall_rule(self, context, firewall_rule):
firewall_rule = firewall_rule['firewall_rule']
return self.driver.create_firewall_rule(context, firewall_rule)
@log_helpers.log_method_call
def delete_firewall_rule(self, context, id):
self.driver.delete_firewall_rule(context, id)
@log_helpers.log_method_call
def get_firewall_rule(self, context, id, fields=None):
return self.driver.get_firewall_rule(context, id, fields)
@log_helpers.log_method_call
def get_firewall_rules(self, context, filters=None, fields=None):
return self.driver.get_firewall_rules(context, filters, fields)
@log_helpers.log_method_call
def update_firewall_rule(self, context, id, firewall_rule):
firewall_rule = firewall_rule['firewall_rule']
self._ensure_update_firewall_rule(context, id)
return self.driver.update_firewall_rule(context, id, firewall_rule)
@log_helpers.log_method_call
def insert_rule(self, context, policy_id, rule_info):
self._ensure_update_firewall_policy(context, policy_id)
self._validate_insert_remove_rule_request(rule_info)
return self.driver.insert_rule(context, policy_id, rule_info)
@log_helpers.log_method_call
def remove_rule(self, context, policy_id, rule_info):
self._ensure_update_firewall_policy(context, policy_id)
self._validate_insert_remove_rule_request(rule_info)
return self.driver.remove_rule(context, policy_id, rule_info)

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