Retire Packaging Deb project repos
This commit is part of a series to retire the Packaging Deb project. Step 2 is to remove all content from the project repos, replacing it with a README notification where to find ongoing work, and how to recover the repo if needed at some future point (as in https://docs.openstack.org/infra/manual/drivers.html#retiring-a-project). Change-Id: Ia93782af86d6fdf7efa5e32b516788925733fc1e
This commit is contained in:
parent
46972c1654
commit
34c32dca3f
|
@ -1,7 +0,0 @@
|
|||
[run]
|
||||
branch = True
|
||||
source = neutron_fwaas
|
||||
# omit = neutron_fwaas/tests/*
|
||||
|
||||
[report]
|
||||
ignore_errors = True
|
|
@ -1,34 +0,0 @@
|
|||
AUTHORS
|
||||
build/*
|
||||
build-stamp
|
||||
ChangeLog
|
||||
cover/
|
||||
covhtml/
|
||||
dist/
|
||||
doc/build
|
||||
doc/source/contributor/api/
|
||||
etc/*.sample
|
||||
*.DS_Store
|
||||
*.pyc
|
||||
neutron.egg-info/
|
||||
neutron_fwaas.egg-info/
|
||||
neutron/vcsversion.py
|
||||
neutron/versioninfo
|
||||
pbr*.egg/
|
||||
run_tests.err.log
|
||||
run_tests.log
|
||||
setuptools*.egg/
|
||||
subunit.log
|
||||
*.mo
|
||||
*.sw?
|
||||
*~
|
||||
/.*
|
||||
!/.coveragerc
|
||||
!/.gitignore
|
||||
!/.gitreview
|
||||
!/.mailmap
|
||||
!/.pylintrc
|
||||
!/.testr.conf
|
||||
|
||||
# Files created by releasenotes build
|
||||
releasenotes/build
|
|
@ -1,4 +0,0 @@
|
|||
[gerrit]
|
||||
host=review.openstack.org
|
||||
port=29418
|
||||
project=openstack/neutron-fwaas.git
|
11
.mailmap
11
.mailmap
|
@ -1,11 +0,0 @@
|
|||
# Format is:
|
||||
# <preferred e-mail> <other e-mail 1>
|
||||
# <preferred e-mail> <other e-mail 2>
|
||||
lawrancejing <lawrancejing@gmail.com> <liuqing@windawn.com>
|
||||
Jiajun Liu <jiajun@unitedstack.com> <iamljj@gmail.com>
|
||||
Zhongyue Luo <zhongyue.nah@intel.com> <lzyeval@gmail.com>
|
||||
Kun Huang <gareth@unitedstack.com> <academicgareth@gmail.com>
|
||||
Zhenguo Niu <zhenguo@unitedstack.com> <Niu.ZGlinux@gmail.com>
|
||||
Isaku Yamahata <isaku.yamahata@intel.com> <isaku.yamahata@gmail.com>
|
||||
Isaku Yamahata <isaku.yamahata@intel.com> <yamahata@private.email.ne.jp>
|
||||
Morgan Fainberg <morgan.fainberg@gmail.com> <m@metacloud.com>
|
130
.pylintrc
130
.pylintrc
|
@ -1,130 +0,0 @@
|
|||
# The format of this file isn't really documented; just use --generate-rcfile
|
||||
[MASTER]
|
||||
# Add <file or directory> to the black list. It should be a base name, not a
|
||||
# path. You may set this option multiple times.
|
||||
#
|
||||
ignore=.git,tests
|
||||
|
||||
[MESSAGES CONTROL]
|
||||
# NOTE(gus): This is a long list. A number of these are important and
|
||||
# should be re-enabled once the offending code is fixed (or marked
|
||||
# with a local disable)
|
||||
disable=
|
||||
# "F" Fatal errors that prevent further processing
|
||||
import-error,
|
||||
# "I" Informational noise
|
||||
locally-disabled,
|
||||
# "E" Error for important programming issues (likely bugs)
|
||||
access-member-before-definition,
|
||||
bad-super-call,
|
||||
maybe-no-member,
|
||||
no-member,
|
||||
no-method-argument,
|
||||
no-self-argument,
|
||||
not-callable,
|
||||
no-value-for-parameter,
|
||||
super-on-old-class,
|
||||
too-few-format-args,
|
||||
# "W" Warnings for stylistic problems or minor programming issues
|
||||
abstract-method,
|
||||
anomalous-backslash-in-string,
|
||||
anomalous-unicode-escape-in-string,
|
||||
arguments-differ,
|
||||
attribute-defined-outside-init,
|
||||
bad-builtin,
|
||||
bad-indentation,
|
||||
broad-except,
|
||||
dangerous-default-value,
|
||||
deprecated-lambda,
|
||||
duplicate-key,
|
||||
expression-not-assigned,
|
||||
fixme,
|
||||
global-statement,
|
||||
global-variable-not-assigned,
|
||||
logging-not-lazy,
|
||||
no-init,
|
||||
non-parent-init-called,
|
||||
pointless-string-statement,
|
||||
protected-access,
|
||||
redefined-builtin,
|
||||
redefined-outer-name,
|
||||
redefine-in-handler,
|
||||
signature-differs,
|
||||
star-args,
|
||||
super-init-not-called,
|
||||
unnecessary-lambda,
|
||||
unnecessary-pass,
|
||||
unpacking-non-sequence,
|
||||
unreachable,
|
||||
unused-argument,
|
||||
unused-import,
|
||||
unused-variable,
|
||||
# TODO(dougwig) - disable nonstandard-exception while we have neutron_lib shims
|
||||
nonstandard-exception,
|
||||
# "C" Coding convention violations
|
||||
bad-continuation,
|
||||
invalid-name,
|
||||
missing-docstring,
|
||||
old-style-class,
|
||||
superfluous-parens,
|
||||
# "R" Refactor recommendations
|
||||
abstract-class-little-used,
|
||||
abstract-class-not-used,
|
||||
duplicate-code,
|
||||
interface-not-implemented,
|
||||
no-self-use,
|
||||
too-few-public-methods,
|
||||
too-many-ancestors,
|
||||
too-many-arguments,
|
||||
too-many-branches,
|
||||
too-many-instance-attributes,
|
||||
too-many-lines,
|
||||
too-many-locals,
|
||||
too-many-public-methods,
|
||||
too-many-return-statements,
|
||||
too-many-statements
|
||||
|
||||
[BASIC]
|
||||
# Variable names can be 1 to 31 characters long, with lowercase and underscores
|
||||
variable-rgx=[a-z_][a-z0-9_]{0,30}$
|
||||
|
||||
# Argument names can be 2 to 31 characters long, with lowercase and underscores
|
||||
argument-rgx=[a-z_][a-z0-9_]{1,30}$
|
||||
|
||||
# Method names should be at least 3 characters long
|
||||
# and be lowercased with underscores
|
||||
method-rgx=([a-z_][a-z0-9_]{2,}|setUp|tearDown)$
|
||||
|
||||
# Module names matching neutron-* are ok (files in bin/)
|
||||
module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+)|(neutron-[a-z0-9_-]+))$
|
||||
|
||||
# Don't require docstrings on tests.
|
||||
no-docstring-rgx=((__.*__)|([tT]est.*)|setUp|tearDown)$
|
||||
|
||||
[FORMAT]
|
||||
# Maximum number of characters on a single line.
|
||||
max-line-length=79
|
||||
|
||||
[VARIABLES]
|
||||
# List of additional names supposed to be defined in builtins. Remember that
|
||||
# you should avoid to define new builtins when possible.
|
||||
# _ is used by our localization
|
||||
additional-builtins=_
|
||||
|
||||
[CLASSES]
|
||||
# List of interface methods to ignore, separated by a comma.
|
||||
ignore-iface-methods=
|
||||
|
||||
[IMPORTS]
|
||||
# Deprecated modules which should not be used, separated by a comma
|
||||
deprecated-modules=
|
||||
# should use oslo_serialization.jsonutils
|
||||
json
|
||||
|
||||
[TYPECHECK]
|
||||
# List of module names for which member attributes should not be checked
|
||||
ignored-modules=six.moves,_MovedItems
|
||||
|
||||
[REPORTS]
|
||||
# Tells whether to display a full report or only the messages
|
||||
reports=no
|
|
@ -1,4 +0,0 @@
|
|||
[DEFAULT]
|
||||
test_command=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 OS_LOG_CAPTURE=1 ${PYTHON:-python} -m subunit.run discover -t ./ ${OS_TEST_PATH:-./neutron_fwaas/tests/unit} $LISTOPT $IDOPTION
|
||||
test_id_option=--load-list $IDFILE
|
||||
test_list_option=--list
|
|
@ -1,4 +0,0 @@
|
|||
Please see the Neutron CONTRIBUTING.rst file for how to contribute to
|
||||
neutron-fwaas:
|
||||
|
||||
`Neutron CONTRIBUTING.rst <http://git.openstack.org/cgit/openstack/neutron/tree/CONTRIBUTING.rst>`_
|
|
@ -1,7 +0,0 @@
|
|||
Neutron FWaaS Style Commandments
|
||||
================================
|
||||
|
||||
Please see the Neutron HACKING.rst file for style commandments for
|
||||
neutron-fwaas:
|
||||
|
||||
`Neutron HACKING.rst <http://git.openstack.org/cgit/openstack/neutron/tree/HACKING.rst>`_
|
176
LICENSE
176
LICENSE
|
@ -1,176 +0,0 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
11
MANIFEST.in
11
MANIFEST.in
|
@ -1,11 +0,0 @@
|
|||
include AUTHORS
|
||||
include README.rst
|
||||
include ChangeLog
|
||||
include LICENSE
|
||||
include neutron_fwaas/db/migration/alembic_migrations/script.py.mako
|
||||
recursive-include neutron_fwaas/db/migration/alembic_migrations/versions *
|
||||
|
||||
exclude .gitignore
|
||||
exclude .gitreview
|
||||
|
||||
global-exclude *.pyc
|
|
@ -0,0 +1,14 @@
|
|||
This project is no longer maintained.
|
||||
|
||||
The contents of this repository are still available in the Git
|
||||
source code management system. To see the contents of this
|
||||
repository before it reached its end of life, please check out the
|
||||
previous commit with "git checkout HEAD^1".
|
||||
|
||||
For ongoing work on maintaining OpenStack packages in the Debian
|
||||
distribution, please see the Debian OpenStack packaging team at
|
||||
https://wiki.debian.org/OpenStack/.
|
||||
|
||||
For any further questions, please email
|
||||
openstack-dev@lists.openstack.org or join #openstack-dev on
|
||||
Freenode.
|
27
README.rst
27
README.rst
|
@ -1,27 +0,0 @@
|
|||
========================
|
||||
Team and repository tags
|
||||
========================
|
||||
|
||||
.. image:: https://governance.openstack.org/badges/neutron-fwaas.svg
|
||||
:target: https://governance.openstack.org/reference/tags/index.html
|
||||
|
||||
.. Change things from this point on
|
||||
|
||||
Welcome!
|
||||
========
|
||||
|
||||
This package contains the code for the Neutron Firewall as a Service
|
||||
(FWaaS) service. This package requires Neutron to run.
|
||||
|
||||
External Resources:
|
||||
===================
|
||||
|
||||
The homepage for Neutron is: https://launchpad.net/neutron. Use this
|
||||
site for asking for help, and filing bugs. We use a single Launchpad
|
||||
page for all Neutron projects.
|
||||
|
||||
Code is available on git.openstack.org at:
|
||||
<https://git.openstack.org/cgit/openstack/neutron-fwaas>.
|
||||
|
||||
Please refer to Neutron documentation for more information:
|
||||
`Neutron README.rst <https://git.openstack.org/cgit/openstack/neutron/tree/README.rst>`_
|
12
TESTING.rst
12
TESTING.rst
|
@ -1,12 +0,0 @@
|
|||
Testing Neutron FWaaS
|
||||
=====================
|
||||
|
||||
Please see the TESTING.rst file for the Neutron project itself. This will have
|
||||
the latest up to date instructions for how to test Neutron, and will
|
||||
be applicable to neutron-fwaas as well:
|
||||
|
||||
`Neutron TESTING.rst <http://git.openstack.org/cgit/openstack/neutron/tree/TESTING.rst>`_
|
||||
|
||||
For instructions on how to use FWaaS with devstack, look at:
|
||||
|
||||
`Neutron-FWaaS DevStack <http://git.openstack.org/cgit/openstack/neutron-fwaas/tree/devstack/README.rst>`_
|
|
@ -1,54 +0,0 @@
|
|||
=========================
|
||||
neutron-fwaas in DevStack
|
||||
=========================
|
||||
|
||||
This is setup as a DevStack plugin. For more information on DevStack plugins,
|
||||
see the `DevStack Plugins documentation
|
||||
<https://docs.openstack.org/developer/devstack/plugins.html>`_.
|
||||
|
||||
Please note that the old 'q-fwaas' keyword still exists, and will run FWaaS V1.
|
||||
This default will be changed during the Ocata cycle. The introduction of two
|
||||
new keywords, 'q-fwaas-v1' and 'q-fwaas-v2' allow you to explicitly select the
|
||||
version you with to run.
|
||||
|
||||
How to run FWaaS V2 in DevStack
|
||||
===============================
|
||||
|
||||
Add the following to the localrc section of your local.conf to configure FWaaS v2.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
[[local|localrc]]
|
||||
enable_plugin neutron-fwaas https://git.openstack.org/openstack/neutron-fwaas
|
||||
enable_service q-fwaas-v2
|
||||
|
||||
To check a specific patchset that is currently under development, use a form
|
||||
like the below example, which is checking out change 214350 patch set 14 for
|
||||
testing.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
[[local|localrc]]
|
||||
enable_plugin neutron-fwaas https://review.openstack.org/p/openstack/neutron-fwaas refs/changes/50/214350/14
|
||||
enable_service q-fwaas-v2
|
||||
|
||||
How to run FWaaS V1 in DevStack
|
||||
===============================
|
||||
|
||||
Add the following to the localrc section of your local.conf to configure FWaaS v1.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
[[local|localrc]]
|
||||
enable_plugin neutron-fwaas https://git.openstack.org/openstack/neutron-fwaas
|
||||
enable_service q-fwaas-v1
|
||||
|
||||
To check a specific patchset that is currently under development, use a form
|
||||
like the below example, which is checking out change 214350 patch set 14 for
|
||||
testing.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
[[local|localrc]]
|
||||
enable_plugin neutron-fwaas https://review.openstack.org/p/openstack/neutron-fwaas refs/changes/50/214350/14
|
||||
enable_service q-fwaas-v1
|
|
@ -1,16 +0,0 @@
|
|||
# This file was shamelessly stolen from the neutron repository here:
|
||||
# http://git.openstack.org/cgit/openstack/neutron/tree/devstack/lib/l2_agent
|
||||
|
||||
function plugin_agent_add_l2_agent_extension {
|
||||
local l2_agent_extension=$1
|
||||
if [[ -z "$L2_AGENT_EXTENSIONS" ]]; then
|
||||
L2_AGENT_EXTENSIONS=$l2_agent_extension
|
||||
elif [[ ! ,${L2_AGENT_EXTENSIONS}, =~ ,${l2_agent_extension}, ]]; then
|
||||
L2_AGENT_EXTENSIONS+=",$l2_agent_extension"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
function configure_l2_agent {
|
||||
iniset /$Q_PLUGIN_CONF_FILE agent extensions "$L2_AGENT_EXTENSIONS"
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
# This file is completely based on one in the neutron repository here:
|
||||
# http://git.openstack.org/cgit/openstack/neutron/tree/devstack/lib/l2_agent
|
||||
|
||||
function plugin_agent_add_l3_agent_extension {
|
||||
local l3_agent_extension=$1
|
||||
if [[ -z "$L3_AGENT_EXTENSIONS" ]]; then
|
||||
L3_AGENT_EXTENSIONS=$l3_agent_extension
|
||||
elif [[ ! ,${L3_AGENT_EXTENSIONS}, =~ ,${l3_agent_extension}, ]]; then
|
||||
L3_AGENT_EXTENSIONS+=",$l3_agent_extension"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
function configure_l3_agent {
|
||||
iniset $Q_L3_CONF_FILE AGENT extensions "$L3_AGENT_EXTENSIONS"
|
||||
}
|
|
@ -1,135 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
# Dependencies:
|
||||
#
|
||||
# ``functions`` file
|
||||
# ``DEST`` must be defined
|
||||
|
||||
# Save trace setting
|
||||
XTRACE=$(set +o | grep xtrace)
|
||||
set +o xtrace
|
||||
|
||||
# Source in L2 and L3 agent extension management
|
||||
LIBDIR=$DEST/neutron-fwaas/devstack/lib
|
||||
source $LIBDIR/l2_agent
|
||||
source $LIBDIR/l3_agent
|
||||
|
||||
function install_fwaas() {
|
||||
# Install the service.
|
||||
:
|
||||
setup_develop $DEST/neutron-fwaas
|
||||
}
|
||||
|
||||
function configure_fwaas_v1() {
|
||||
cp $NEUTRON_FWAAS_DIR/etc/neutron_fwaas.conf.sample $NEUTRON_FWAAS_CONF
|
||||
neutron_fwaas_configure_driver fwaas
|
||||
iniset_multiline $Q_L3_CONF_FILE fwaas agent_version v1
|
||||
iniset_multiline $Q_L3_CONF_FILE fwaas conntrack_driver conntrack
|
||||
}
|
||||
|
||||
function configure_fwaas_v2() {
|
||||
# Add conf file
|
||||
cp $NEUTRON_FWAAS_DIR/etc/neutron_fwaas.conf.sample $NEUTRON_FWAAS_CONF
|
||||
neutron_fwaas_configure_driver fwaas_v2
|
||||
iniset_multiline $Q_L3_CONF_FILE fwaas agent_version v2
|
||||
}
|
||||
|
||||
function neutron_fwaas_generate_config_files {
|
||||
(cd $NEUTRON_FWAAS_DIR && exec ./tools/generate_config_file_samples.sh)
|
||||
}
|
||||
|
||||
function init_fwaas() {
|
||||
# Initialize and start the service.
|
||||
:
|
||||
if [ ! -d /etc/neutron/policy.d ]; then
|
||||
mkdir /etc/neutron/policy.d
|
||||
fi
|
||||
cp $DEST/neutron-fwaas/etc/neutron/policy.d/neutron-fwaas.json /etc/neutron/policy.d/neutron-fwaas.json
|
||||
# Using sudo to gain the root privilege to be able to copy file to rootwrap.d
|
||||
sudo cp $DEST/neutron-fwaas/etc/neutron/rootwrap.d/fwaas-privsep.filters /etc/neutron/rootwrap.d/fwaas-privsep.filters
|
||||
}
|
||||
|
||||
function shutdown_fwaas() {
|
||||
# Shut the service down.
|
||||
:
|
||||
}
|
||||
|
||||
function cleanup_fwaas() {
|
||||
# Cleanup the service.
|
||||
:
|
||||
}
|
||||
|
||||
function neutron_fwaas_configure_common {
|
||||
if is_service_enabled q-fwaas-v1 neutron-fwaas-v1; then
|
||||
neutron_service_plugin_class_add $FWAAS_PLUGIN_V1
|
||||
elif is_service_enabled q-fwaas-v2 neutron-fwaas-v2; then
|
||||
neutron_service_plugin_class_add $FWAAS_PLUGIN_V2
|
||||
else
|
||||
neutron_service_plugin_class_add $FWAAS_PLUGIN_V1
|
||||
fi
|
||||
}
|
||||
|
||||
function neutron_fwaas_configure_driver {
|
||||
plugin_agent_add_l3_agent_extension $1
|
||||
configure_l3_agent
|
||||
iniset_multiline $Q_L3_CONF_FILE fwaas enabled True
|
||||
iniset_multiline $Q_L3_CONF_FILE fwaas driver $FWAAS_DRIVER
|
||||
}
|
||||
|
||||
# check for service enabled
|
||||
if is_service_enabled q-svc neutron-api && is_service_enabled q-fwaas q-fwaas-v1 q-fwaas-v2 neutron-fwaas-v1 neutron-fwaas-v2; then
|
||||
|
||||
if [[ "$1" == "stack" && "$2" == "install" ]]; then
|
||||
# Perform installation of service source
|
||||
echo_summary "Installing neutron-fwaas"
|
||||
install_fwaas
|
||||
|
||||
elif [[ "$1" == "stack" && "$2" == "post-config" ]]; then
|
||||
# Configure after the other layer 1 and 2 services have been configured
|
||||
neutron_fwaas_configure_common
|
||||
neutron_fwaas_generate_config_files
|
||||
if is_service_enabled q-fwaas-v1 neutron-fwaas-v1; then
|
||||
echo_summary "Configuring neutron-fwaas for FWaaS v1"
|
||||
configure_fwaas_v1
|
||||
elif is_service_enabled q-fwaas-v2 neutron-fwaas-v2; then
|
||||
echo_summary "Configuring neutron-fwaas for FWaaS v2"
|
||||
configure_fwaas_v2
|
||||
else
|
||||
echo_summary "Configuring neutron-fwaas for FWaaS v1"
|
||||
configure_fwaas_v1
|
||||
fi
|
||||
|
||||
elif [[ "$1" == "stack" && "$2" == "extra" ]]; then
|
||||
# Initialize and start the neutron-fwaas service
|
||||
echo_summary "Initializing neutron-fwaas"
|
||||
init_fwaas
|
||||
fi
|
||||
|
||||
if [[ "$1" == "unstack" ]]; then
|
||||
# Shut down neutron-fwaas services
|
||||
# no-op
|
||||
shutdown_fwaas
|
||||
fi
|
||||
|
||||
if [[ "$1" == "clean" ]]; then
|
||||
# Remove state and transient data
|
||||
# Remember clean.sh first calls unstack.sh
|
||||
# no-op
|
||||
cleanup_fwaas
|
||||
fi
|
||||
fi
|
||||
|
||||
# Restore xtrace
|
||||
$XTRACE
|
|
@ -1,9 +0,0 @@
|
|||
FWAAS_DRIVER=${FWAAS_DRIVER:-iptables}
|
||||
FWAAS_PLUGIN_V1=${FWAAS_PLUGIN:-neutron_fwaas.services.firewall.fwaas_plugin.FirewallPlugin}
|
||||
FWAAS_PLUGIN_V2=${FWAAS_PLUGIN:-neutron_fwaas.services.firewall.fwaas_plugin_v2.FirewallPluginV2}
|
||||
|
||||
NEUTRON_FWAAS_DIR=$DEST/neutron-fwaas
|
||||
NEUTRON_FWAAS_CONF_FILE=neutron_fwaas.conf
|
||||
|
||||
NEUTRON_FWAAS_CONF=$NEUTRON_CONF_DIR/$NEUTRON_FWAAS_CONF_FILE
|
||||
neutron_server_config_add $NEUTRON_FWAAS_CONF
|
|
@ -1,244 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2010 OpenStack Foundation.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
#
|
||||
# Keystone documentation build configuration file, created by
|
||||
# sphinx-quickstart on Tue May 18 13:50:15 2010.
|
||||
#
|
||||
# This file is execfile()'d with the current directory set to it's containing
|
||||
# dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
ROOT_DIR = os.path.abspath(os.path.join(BASE_DIR, "..", ".."))
|
||||
sys.path.insert(0, ROOT_DIR)
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = ['sphinx.ext.autodoc',
|
||||
'sphinx.ext.coverage',
|
||||
'sphinx.ext.ifconfig',
|
||||
'sphinx.ext.graphviz',
|
||||
'sphinx.ext.todo',
|
||||
'openstackdocstheme',]
|
||||
|
||||
todo_include_todos = True
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = []
|
||||
if os.getenv('HUDSON_PUBLISH_DOCS'):
|
||||
templates_path = ['_ga', '_templates']
|
||||
else:
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
#source_encoding = 'utf-8'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'Neutron FWaaS'
|
||||
copyright = u'2011-present, OpenStack Foundation.'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# Version info
|
||||
from neutron_fwaas.version import version_info as neutron_fwaas_version
|
||||
release = neutron_fwaas_version.release_string()
|
||||
# The short X.Y version.
|
||||
version = neutron_fwaas_version.version_string()
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
#today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
#today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of documents that shouldn't be included in the build.
|
||||
# unused_docs = []
|
||||
|
||||
# List of directories, relative to source directory, that shouldn't be searched
|
||||
# for source files.
|
||||
exclude_trees = []
|
||||
|
||||
# The reST default role (for this markup: `text`) to use for all documents.
|
||||
#default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
#add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
show_authors = True
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
modindex_common_prefix = ['neutron_fwaas.']
|
||||
|
||||
# -- Options for man page output --------------------------------------------
|
||||
|
||||
# Grouping the document tree for man pages.
|
||||
# List of tuples 'sourcefile', 'target', u'title', u'Authors name', 'manual'
|
||||
|
||||
#man_pages = [
|
||||
# ('man/neutron-server', 'neutron-server', u'Neutron Server',
|
||||
# [u'OpenStack'], 1)
|
||||
#]
|
||||
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. Major themes that come with
|
||||
# Sphinx are currently 'default' and 'sphinxdoc'.
|
||||
# html_theme_path = ["."]
|
||||
# html_theme = '_theme'
|
||||
html_theme = 'openstackdocs'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
#html_theme_path = ['_theme']
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
#html_title = None
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
#html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
#html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
#html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
# html_static_path = ['_static']
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
# html_last_updated_fmt = '%b %d, %Y'
|
||||
html_last_updated_fmt = '%Y-%m-%d %H:%M'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
#html_sidebars = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
#html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
#html_use_modindex = True
|
||||
|
||||
# If false, no index is generated.
|
||||
#html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
#html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
#html_show_sourcelink = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
#html_use_opensearch = ''
|
||||
|
||||
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
#html_file_suffix = ''
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
#htmlhelp_basename = 'neutrondoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output ------------------------------------------------
|
||||
|
||||
# The paper size ('letter' or 'a4').
|
||||
#latex_paper_size = 'letter'
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#latex_font_size = '10pt'
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# documentclass [howto/manual]).
|
||||
#latex_documents = [
|
||||
# ('index', 'Neutron.tex', u'Neutron Documentation',
|
||||
# u'Neutron development team', 'manual'),
|
||||
#]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
#latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
#latex_use_parts = False
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#latex_preamble = ''
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#latex_use_modindex = True
|
||||
|
||||
# -- Options for openstackdocstheme -------------------------------------------
|
||||
repository_name = 'openstack/neutron-fwaas'
|
||||
bug_project = 'neutron'
|
||||
bug_tag = 'doc'
|
|
@ -1,28 +0,0 @@
|
|||
=============================
|
||||
Contributing to neutron-fwaas
|
||||
=============================
|
||||
|
||||
If you would like to contribute to the development of OpenStack, you must
|
||||
follow the steps documented at:
|
||||
https://docs.openstack.org/infra/manual/developers.html
|
||||
|
||||
Once those steps have been completed, changes to OpenStack should be submitted
|
||||
for review via the Gerrit tool, following the workflow documented at:
|
||||
https://docs.openstack.org/infra/manual/developers.html#development-workflow
|
||||
|
||||
Pull requests submitted through GitHub will be ignored.
|
||||
|
||||
Bugs should be filed on Launchpad in the 'neutron' project:
|
||||
https://bugs.launchpad.net/neutron
|
||||
|
||||
To get in touch with the neutron-fwaas community,
|
||||
look at the following resources:
|
||||
|
||||
- Join the #openstack-fwaas IRC channel on Freenode. This is where the
|
||||
FireWall-as-a-Service team is available for discussion.
|
||||
- Join the `FireWall-as-a-Service weekly IRC meeting
|
||||
<http://eavesdrop.openstack.org/#Firewall_as_a_Service_(FWaaS)_Team_Meeting>`_
|
||||
where the status of new initiatives and bugs is discussed.
|
||||
|
||||
These are a great places to get recommendations on where to start contributing
|
||||
to neutron-fwaas.
|
|
@ -1 +0,0 @@
|
|||
.. include:: ../../../devstack/README.rst
|
|
@ -1,7 +0,0 @@
|
|||
FireWall as a Service V2
|
||||
========================
|
||||
|
||||
The `FireWall as a Service API V2
|
||||
<https://specs.openstack.org/openstack/neutron-specs/specs/newton/fwaas-api-2.0.html>`_
|
||||
specification lists the changes that together compose FWaaS V2. These changes
|
||||
are not fully implemented.
|
|
@ -1,17 +0,0 @@
|
|||
=================
|
||||
Contributor Guide
|
||||
=================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
contributing
|
||||
fwaas_v2
|
||||
devstack
|
||||
|
||||
.. API reference contains a lot of sections, toctree with maxdepth 1 is used.
|
||||
.. toctree::
|
||||
:glob:
|
||||
:maxdepth: 1
|
||||
|
||||
modules
|
|
@ -1,9 +0,0 @@
|
|||
================
|
||||
Module Reference
|
||||
================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:glob:
|
||||
|
||||
api/*
|
|
@ -1,18 +0,0 @@
|
|||
===========================
|
||||
neutron-fwaas documentation
|
||||
===========================
|
||||
|
||||
General Information and Other Project References:
|
||||
|
||||
.. toctree::
|
||||
:glob:
|
||||
:maxdepth: 2
|
||||
|
||||
install/index
|
||||
contributor/index
|
||||
|
||||
.. rubric:: Indices and tables
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
|
@ -1,39 +0,0 @@
|
|||
..
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
not use this file except in compliance with the License. You may obtain
|
||||
a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
|
||||
Convention for heading levels in Neutron devref:
|
||||
======= Heading 0 (reserved for the title in a document)
|
||||
------- Heading 1
|
||||
~~~~~~~ Heading 2
|
||||
+++++++ Heading 3
|
||||
''''''' Heading 4
|
||||
(Avoid deeper levels because they do not render well.)
|
||||
|
||||
|
||||
============
|
||||
Installation
|
||||
============
|
||||
|
||||
At the command line::
|
||||
|
||||
$ pip install neutron-fwaas
|
||||
|
||||
Or, if you have virtualenvwrapper installed::
|
||||
|
||||
$ mkvirtualenv neutron-fwaas
|
||||
$ pip install neutron-fwaas
|
||||
|
||||
For information on what to do with FWaaS once it is installed, please check the
|
||||
Networking Guide `Firewall-as-a-Service (FWaaS) v2 scenario <https://docs.openstack.org/newton/networking-guide/fwaas-v2-scenario.html>`_ or
|
||||
the `Firewall-as-a-Service (FWaaS) v1 scenario <https://docs.openstack.org/newton/networking-guide/fwaas-v1-scenario.html>`_.
|
|
@ -1,9 +0,0 @@
|
|||
To generate the sample neutron-fwaas configuration files, run the following
|
||||
command from the top level of the neutron-fwaas directory:
|
||||
|
||||
tox -e genconfig
|
||||
|
||||
If a 'tox' environment is unavailable, then you can run the following script
|
||||
instead to generate the configuration files:
|
||||
|
||||
./tools/generate_config_file_samples.sh
|
|
@ -1,52 +0,0 @@
|
|||
{
|
||||
"shared_firewalls": "field:firewalls:shared=True",
|
||||
"shared_firewall_policies": "field:firewall_policies:shared=True",
|
||||
"shared_firewall_rules": "field:firewall_rules:shared=True",
|
||||
|
||||
"create_firewall": "",
|
||||
"update_firewall": "rule:admin_or_owner",
|
||||
"delete_firewall": "rule:admin_or_owner",
|
||||
|
||||
"create_firewall:shared": "rule:admin_only",
|
||||
"update_firewall:shared": "rule:admin_only",
|
||||
"delete_firewall:shared": "rule:admin_only",
|
||||
|
||||
"get_firewall": "rule:admin_or_owner or rule:shared_firewalls",
|
||||
|
||||
"shared_firewall_groups": "field:firewall_groups:shared=True",
|
||||
"shared_firewall_policies": "field:firewall_policies:shared=True",
|
||||
"shared_firewall_rules": "field:firewall_rules:shared=True",
|
||||
|
||||
"create_firewall_group": "",
|
||||
"update_firewall_group": "rule:admin_or_owner",
|
||||
"delete_firewall_group": "rule:admin_or_owner",
|
||||
|
||||
"create_firewall_group:shared": "rule:admin_only",
|
||||
"update_firewall_group:shared": "rule:admin_only",
|
||||
"delete_firewall_group:shared": "rule:admin_only",
|
||||
|
||||
"get_firewall_group": "rule:admin_or_owner or rule:shared_firewall_groups",
|
||||
|
||||
"create_firewall_policy": "",
|
||||
"update_firewall_policy": "rule:admin_or_owner",
|
||||
"delete_firewall_policy": "rule:admin_or_owner",
|
||||
|
||||
"create_firewall_policy:shared": "rule:admin_only",
|
||||
"update_firewall_policy:shared": "rule:admin_only",
|
||||
"delete_firewall_policy:shared": "rule:admin_only",
|
||||
|
||||
"get_firewall_policy": "rule:admin_or_owner or rule:shared_firewall_policies",
|
||||
|
||||
"insert_rule": "rule:admin_or_owner",
|
||||
"remove_rule": "rule:admin_or_owner",
|
||||
|
||||
"create_firewall_rule": "",
|
||||
"update_firewall_rule": "rule:admin_or_owner",
|
||||
"delete_firewall_rule": "rule:admin_or_owner",
|
||||
|
||||
"create_firewall_rule:shared": "rule:admin_only",
|
||||
"update_firewall_rule:shared": "rule:admin_only",
|
||||
"delete_firewall_rule:shared": "rule:admin_only",
|
||||
|
||||
"get_firewall_rule": "rule:admin_or_owner or rule:shared_firewall_rules"
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
# neutron-fwaas privsep filters
|
||||
|
||||
# This file should be owned by (and only-writeable by) the root user
|
||||
|
||||
[Filters]
|
||||
|
||||
privsep-rootwrap: PathFilter, privsep-helper, root, privsep-helper, --config-file, /etc/(?!\.\.).*, --privsep_context, neutron_fwaas.privileged.default
|
|
@ -1,5 +0,0 @@
|
|||
[DEFAULT]
|
||||
output_file = etc/fwaas_driver.ini.sample
|
||||
wrap_width = 79
|
||||
|
||||
namespace = firewall.agent
|
|
@ -1,6 +0,0 @@
|
|||
[DEFAULT]
|
||||
output_file = etc/neutron_fwaas.conf.sample
|
||||
wrap_width = 79
|
||||
|
||||
namespace = neutron.fwaas
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
# Copyright 2011 OpenStack Foundation
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import gettext
|
||||
|
||||
import six
|
||||
|
||||
|
||||
if six.PY2:
|
||||
gettext.install('neutron', unicode=1)
|
||||
else:
|
||||
gettext.install('neutron')
|
|
@ -1,42 +0,0 @@
|
|||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import oslo_i18n
|
||||
|
||||
DOMAIN = "neutron_fwaas"
|
||||
|
||||
_translators = oslo_i18n.TranslatorFactory(domain=DOMAIN)
|
||||
|
||||
# The primary translation function using the well-known name "_"
|
||||
_ = _translators.primary
|
||||
|
||||
# The contextual translation function using the name "_C"
|
||||
_C = _translators.contextual_form
|
||||
|
||||
# The plural translation function using the name "_P"
|
||||
_P = _translators.plural_form
|
||||
|
||||
# Translators for log levels.
|
||||
#
|
||||
# The abbreviated names are meant to reflect the usual use of a short
|
||||
# name like '_'. The "L" is for "log" and the other letter comes from
|
||||
# the level.
|
||||
_LI = _translators.log_info
|
||||
_LW = _translators.log_warning
|
||||
_LE = _translators.log_error
|
||||
_LC = _translators.log_critical
|
||||
|
||||
|
||||
def get_available_languages():
|
||||
return oslo_i18n.get_available_languages(DOMAIN)
|
|
@ -1,22 +0,0 @@
|
|||
# Copyright 2015 Cisco Systems, Inc
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
FIREWALL = 'FIREWALL'
|
||||
|
||||
# Constants for "topics"
|
||||
FIREWALL_PLUGIN = 'q-firewall-plugin'
|
||||
L3_AGENT = 'l3_agent'
|
||||
FW_AGENT = 'firewall_agent'
|
||||
FIREWALL_RULE_LIST = 'firewall_rule_list'
|
|
@ -1,17 +0,0 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutron_fwaas.db.firewall.v2 import firewall_db_v2
|
||||
|
||||
FIREWALL_GROUP = firewall_db_v2.FirewallGroup
|
||||
FIREWALL_POLICY = firewall_db_v2.FirewallPolicy
|
||||
FIREWALL_RULE = firewall_db_v2.FirewallRuleV2
|
|
@ -1,657 +0,0 @@
|
|||
# Copyright 2013 Big Switch Networks, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import operator
|
||||
|
||||
from neutron.db import common_db_mixin as base_db
|
||||
from neutron.db.models import agent as agent_model
|
||||
from neutron.db.models import l3agent as l3agent_model
|
||||
from neutron_lib.callbacks import events
|
||||
from neutron_lib.callbacks import registry
|
||||
from neutron_lib.callbacks import resources
|
||||
from neutron_lib import constants as nl_constants
|
||||
from neutron_lib.db import model_base
|
||||
from neutron_lib.exceptions import firewall_v1 as f_exc
|
||||
from neutron_lib.exceptions import l3
|
||||
from neutron_lib.plugins import directory
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import uuidutils
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.ext.orderinglist import ordering_list
|
||||
from sqlalchemy import orm
|
||||
from sqlalchemy.orm import exc
|
||||
|
||||
import netaddr
|
||||
|
||||
from neutron_fwaas.common import fwaas_constants
|
||||
from neutron_fwaas.db.firewall import firewall_router_insertion_db \
|
||||
as fw_r_ins_db
|
||||
from neutron_fwaas.extensions import firewall as fw_ext
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FirewallRule(model_base.BASEV2, model_base.HasId, model_base.HasProject):
|
||||
"""Represents a Firewall rule."""
|
||||
__tablename__ = 'firewall_rules'
|
||||
__table_args__ = ({'mysql_collate': 'utf8_bin'})
|
||||
name = sa.Column(sa.String(255))
|
||||
description = sa.Column(sa.String(1024))
|
||||
firewall_policy_id = sa.Column(sa.String(36),
|
||||
sa.ForeignKey('firewall_policies.id'),
|
||||
nullable=True)
|
||||
shared = sa.Column(sa.Boolean)
|
||||
protocol = sa.Column(sa.String(40))
|
||||
ip_version = sa.Column(sa.Integer, nullable=False)
|
||||
source_ip_address = sa.Column(sa.String(46))
|
||||
destination_ip_address = sa.Column(sa.String(46))
|
||||
source_port_range_min = sa.Column(sa.Integer)
|
||||
source_port_range_max = sa.Column(sa.Integer)
|
||||
destination_port_range_min = sa.Column(sa.Integer)
|
||||
destination_port_range_max = sa.Column(sa.Integer)
|
||||
action = sa.Column(sa.Enum('allow', 'deny', 'reject',
|
||||
name='firewallrules_action'))
|
||||
enabled = sa.Column(sa.Boolean)
|
||||
position = sa.Column(sa.Integer)
|
||||
|
||||
|
||||
class Firewall(model_base.BASEV2, model_base.HasId, model_base.HasProject):
|
||||
"""Represents a Firewall resource."""
|
||||
__tablename__ = 'firewalls'
|
||||
__table_args__ = ({'mysql_collate': 'utf8_bin'})
|
||||
name = sa.Column(sa.String(255))
|
||||
description = sa.Column(sa.String(1024))
|
||||
shared = sa.Column(sa.Boolean)
|
||||
admin_state_up = sa.Column(sa.Boolean)
|
||||
status = sa.Column(sa.String(16))
|
||||
firewall_policy_id = sa.Column(sa.String(36),
|
||||
sa.ForeignKey('firewall_policies.id'),
|
||||
nullable=True)
|
||||
|
||||
|
||||
class FirewallPolicy(model_base.BASEV2, model_base.HasId,
|
||||
model_base.HasProject):
|
||||
"""Represents a Firewall Policy resource."""
|
||||
__tablename__ = 'firewall_policies'
|
||||
__table_args__ = ({'mysql_collate': 'utf8_bin'})
|
||||
name = sa.Column(sa.String(255))
|
||||
description = sa.Column(sa.String(1024))
|
||||
shared = sa.Column(sa.Boolean)
|
||||
firewall_rules = orm.relationship(
|
||||
FirewallRule,
|
||||
backref=orm.backref('firewall_policies', cascade='all, delete'),
|
||||
order_by='FirewallRule.position',
|
||||
collection_class=ordering_list('position', count_from=1))
|
||||
audited = sa.Column(sa.Boolean)
|
||||
firewalls = orm.relationship(Firewall, backref='firewall_policies')
|
||||
|
||||
|
||||
class Firewall_db_mixin(fw_ext.FirewallPluginBase, base_db.CommonDbMixin):
|
||||
"""Mixin class for Firewall DB implementation."""
|
||||
|
||||
@property
|
||||
def _core_plugin(self):
|
||||
return directory.get_plugin()
|
||||
|
||||
def _get_firewall(self, context, id):
|
||||
try:
|
||||
return self._get_by_id(context, Firewall, id)
|
||||
except exc.NoResultFound:
|
||||
raise f_exc.FirewallNotFound(firewall_id=id)
|
||||
|
||||
def _get_firewall_policy(self, context, id):
|
||||
try:
|
||||
return self._get_by_id(context, FirewallPolicy, id)
|
||||
except exc.NoResultFound:
|
||||
raise f_exc.FirewallPolicyNotFound(firewall_policy_id=id)
|
||||
|
||||
def _get_firewall_rule(self, context, id):
|
||||
try:
|
||||
return self._get_by_id(context, FirewallRule, id)
|
||||
except exc.NoResultFound:
|
||||
raise f_exc.FirewallRuleNotFound(firewall_rule_id=id)
|
||||
|
||||
def _make_firewall_dict(self, fw, fields=None):
|
||||
res = {'id': fw['id'],
|
||||
'tenant_id': fw['tenant_id'],
|
||||
'name': fw['name'],
|
||||
'description': fw['description'],
|
||||
'shared': fw['shared'],
|
||||
'admin_state_up': fw['admin_state_up'],
|
||||
'status': fw['status'],
|
||||
'firewall_policy_id': fw['firewall_policy_id']}
|
||||
return self._fields(res, fields)
|
||||
|
||||
def _make_firewall_policy_dict(self, firewall_policy, fields=None):
|
||||
fw_rules = [rule['id'] for rule in firewall_policy['firewall_rules']]
|
||||
firewalls = [fw['id'] for fw in firewall_policy['firewalls']]
|
||||
res = {'id': firewall_policy['id'],
|
||||
'tenant_id': firewall_policy['tenant_id'],
|
||||
'name': firewall_policy['name'],
|
||||
'description': firewall_policy['description'],
|
||||
'shared': firewall_policy['shared'],
|
||||
'audited': firewall_policy['audited'],
|
||||
'firewall_rules': fw_rules,
|
||||
'firewall_list': firewalls}
|
||||
return self._fields(res, fields)
|
||||
|
||||
def _make_firewall_rule_dict(self, firewall_rule, fields=None):
|
||||
position = None
|
||||
# We return the position only if the firewall_rule is bound to a
|
||||
# firewall_policy.
|
||||
if firewall_rule['firewall_policy_id']:
|
||||
position = firewall_rule['position']
|
||||
src_port_range = self._get_port_range_from_min_max_ports(
|
||||
firewall_rule['source_port_range_min'],
|
||||
firewall_rule['source_port_range_max'])
|
||||
dst_port_range = self._get_port_range_from_min_max_ports(
|
||||
firewall_rule['destination_port_range_min'],
|
||||
firewall_rule['destination_port_range_max'])
|
||||
res = {'id': firewall_rule['id'],
|
||||
'tenant_id': firewall_rule['tenant_id'],
|
||||
'name': firewall_rule['name'],
|
||||
'description': firewall_rule['description'],
|
||||
'firewall_policy_id': firewall_rule['firewall_policy_id'],
|
||||
'shared': firewall_rule['shared'],
|
||||
'protocol': firewall_rule['protocol'],
|
||||
'ip_version': firewall_rule['ip_version'],
|
||||
'source_ip_address': firewall_rule['source_ip_address'],
|
||||
'destination_ip_address':
|
||||
firewall_rule['destination_ip_address'],
|
||||
'source_port': src_port_range,
|
||||
'destination_port': dst_port_range,
|
||||
'action': firewall_rule['action'],
|
||||
'position': position,
|
||||
'enabled': firewall_rule['enabled']}
|
||||
return self._fields(res, fields)
|
||||
|
||||
def _make_firewall_dict_with_rules(self, context, firewall_id):
|
||||
firewall = self.get_firewall(context, firewall_id)
|
||||
fw_policy_id = firewall['firewall_policy_id']
|
||||
if fw_policy_id:
|
||||
fw_rules_list = self.get_firewall_rules(
|
||||
context, filters={'firewall_policy_id': [fw_policy_id]})
|
||||
fw_rules_list = sorted(
|
||||
fw_rules_list, key=operator.itemgetter('position'))
|
||||
firewall['firewall_rule_list'] = fw_rules_list
|
||||
else:
|
||||
firewall['firewall_rule_list'] = []
|
||||
# FIXME(Sumit): If the size of the firewall object we are creating
|
||||
# here exceeds the largest message size supported by rabbit/qpid
|
||||
# then we will have a problem.
|
||||
return firewall
|
||||
|
||||
def _check_firewall_rule_conflict(self, fwr_db, fwp_db):
|
||||
if not fwr_db['shared']:
|
||||
if fwr_db['tenant_id'] != fwp_db['tenant_id']:
|
||||
raise f_exc.FirewallRuleConflict(
|
||||
firewall_rule_id=fwr_db['id'],
|
||||
project_id=fwr_db['tenant_id'])
|
||||
|
||||
def _set_rules_for_policy(self, context, firewall_policy_db, fwp):
|
||||
rule_id_list = fwp['firewall_rules']
|
||||
fwp_db = firewall_policy_db
|
||||
with context.session.begin(subtransactions=True):
|
||||
if not rule_id_list:
|
||||
fwp_db.firewall_rules = []
|
||||
fwp_db.audited = False
|
||||
return
|
||||
# We will first check if the new list of rules is valid
|
||||
filters = {'id': [r_id for r_id in rule_id_list]}
|
||||
rules_in_db = self._get_collection_query(context, FirewallRule,
|
||||
filters=filters)
|
||||
rules_dict = dict((fwr_db['id'], fwr_db) for fwr_db in rules_in_db)
|
||||
for fwrule_id in rule_id_list:
|
||||
if fwrule_id not in rules_dict:
|
||||
# If we find an invalid rule in the list we
|
||||
# do not perform the update since this breaks
|
||||
# the integrity of this list.
|
||||
raise f_exc.FirewallRuleNotFound(
|
||||
firewall_rule_id=fwrule_id)
|
||||
elif rules_dict[fwrule_id]['firewall_policy_id']:
|
||||
if (rules_dict[fwrule_id]['firewall_policy_id'] !=
|
||||
fwp_db['id']):
|
||||
raise f_exc.FirewallRuleInUse(
|
||||
firewall_rule_id=fwrule_id)
|
||||
if 'shared' in fwp:
|
||||
if fwp['shared'] and not rules_dict[fwrule_id]['shared']:
|
||||
raise f_exc.FirewallRuleSharingConflict(
|
||||
firewall_rule_id=fwrule_id,
|
||||
firewall_policy_id=fwp_db['id'])
|
||||
elif fwp_db['shared'] and not rules_dict[fwrule_id]['shared']:
|
||||
raise f_exc.FirewallRuleSharingConflict(
|
||||
firewall_rule_id=fwrule_id,
|
||||
firewall_policy_id=fwp_db['id'])
|
||||
for fwr_db in rules_in_db:
|
||||
self._check_firewall_rule_conflict(fwr_db, fwp_db)
|
||||
# New list of rules is valid so we will first reset the existing
|
||||
# list and then add each rule in order.
|
||||
# Note that the list could be empty in which case we interpret
|
||||
# it as clearing existing rules.
|
||||
fwp_db.firewall_rules = []
|
||||
for fwrule_id in rule_id_list:
|
||||
fwp_db.firewall_rules.append(rules_dict[fwrule_id])
|
||||
fwp_db.firewall_rules.reorder()
|
||||
fwp_db.audited = False
|
||||
|
||||
def _check_unshared_rules_for_policy(self, fwp_db, fwp):
|
||||
if fwp['shared']:
|
||||
rules_in_db = fwp_db['firewall_rules']
|
||||
for fwr_db in rules_in_db:
|
||||
if not fwr_db['shared']:
|
||||
raise f_exc.FirewallPolicySharingConflict(
|
||||
firewall_rule_id=fwr_db['id'],
|
||||
firewall_policy_id=fwp_db['id'])
|
||||
|
||||
def _process_rule_for_policy(self, context, firewall_policy_id,
|
||||
firewall_rule_db, position):
|
||||
with context.session.begin(subtransactions=True):
|
||||
fwp_query = context.session.query(
|
||||
FirewallPolicy).with_lockmode('update')
|
||||
fwp_db = fwp_query.filter_by(id=firewall_policy_id).one()
|
||||
if position:
|
||||
# Note that although position numbering starts at 1,
|
||||
# internal ordering of the list starts at 0, so we compensate.
|
||||
fwp_db.firewall_rules.insert(position - 1, firewall_rule_db)
|
||||
else:
|
||||
fwp_db.firewall_rules.remove(firewall_rule_db)
|
||||
fwp_db.firewall_rules.reorder()
|
||||
fwp_db.audited = False
|
||||
return self._make_firewall_policy_dict(fwp_db)
|
||||
|
||||
def _get_min_max_ports_from_range(self, port_range):
|
||||
if not port_range:
|
||||
return [None, None]
|
||||
min_port, sep, max_port = port_range.partition(":")
|
||||
if not max_port:
|
||||
max_port = min_port
|
||||
self._validate_fwr_port_range(min_port, max_port)
|
||||
return [int(min_port), int(max_port)]
|
||||
|
||||
def _get_port_range_from_min_max_ports(self, min_port, max_port):
|
||||
if not min_port:
|
||||
return None
|
||||
if min_port == max_port:
|
||||
return str(min_port)
|
||||
self._validate_fwr_port_range(min_port, max_port)
|
||||
return '%s:%s' % (min_port, max_port)
|
||||
|
||||
def _validate_fw_parameters(self, context, fw, fw_tenant_id):
|
||||
if 'firewall_policy_id' not in fw:
|
||||
return
|
||||
fwp_id = fw['firewall_policy_id']
|
||||
fwp = self._get_firewall_policy(context, fwp_id)
|
||||
if fw_tenant_id != fwp['tenant_id'] and not fwp['shared']:
|
||||
raise f_exc.FirewallPolicyConflict(firewall_policy_id=fwp_id)
|
||||
|
||||
def _validate_fwr_src_dst_ip_version(self, fwr):
|
||||
src_version = dst_version = None
|
||||
if fwr.get('source_ip_address', None):
|
||||
src_version = netaddr.IPNetwork(fwr['source_ip_address']).version
|
||||
if fwr.get('destination_ip_address', None):
|
||||
dst_version = netaddr.IPNetwork(
|
||||
fwr['destination_ip_address']).version
|
||||
rule_ip_version = fwr.get('ip_version', None)
|
||||
if ((src_version and src_version != rule_ip_version) or
|
||||
(dst_version and dst_version != rule_ip_version)):
|
||||
raise f_exc.FirewallIpAddressConflict()
|
||||
|
||||
def _validate_fwr_port_range(self, min_port, max_port):
|
||||
if int(min_port) > int(max_port):
|
||||
port_range = '%s:%s' % (min_port, max_port)
|
||||
raise f_exc.FirewallRuleInvalidPortValue(port=port_range)
|
||||
|
||||
def _validate_fwr_protocol_parameters(self, fwr):
|
||||
protocol = fwr.get('protocol', None)
|
||||
if protocol not in (nl_constants.PROTO_NAME_TCP,
|
||||
nl_constants.PROTO_NAME_UDP):
|
||||
if (fwr.get('source_port', None) or
|
||||
fwr.get('destination_port', None)):
|
||||
raise f_exc.FirewallRuleInvalidICMPParameter(
|
||||
param="Source, destination port")
|
||||
|
||||
def create_firewall(self, context, firewall, status=None):
|
||||
LOG.debug("create_firewall() called")
|
||||
fw = firewall['firewall']
|
||||
tenant_id = fw['tenant_id']
|
||||
# distributed routers may required a more complex state machine;
|
||||
# the introduction of a new 'CREATED' state allows this, whilst
|
||||
# keeping a backward compatible behavior of the logical resource.
|
||||
if not status:
|
||||
status = (nl_constants.CREATED if cfg.CONF.router_distributed
|
||||
else nl_constants.PENDING_CREATE)
|
||||
with context.session.begin(subtransactions=True):
|
||||
self._validate_fw_parameters(context, fw, tenant_id)
|
||||
firewall_db = Firewall(
|
||||
id=uuidutils.generate_uuid(),
|
||||
tenant_id=tenant_id,
|
||||
name=fw['name'],
|
||||
description=fw['description'],
|
||||
firewall_policy_id=fw['firewall_policy_id'],
|
||||
admin_state_up=fw['admin_state_up'],
|
||||
status=status)
|
||||
context.session.add(firewall_db)
|
||||
return self._make_firewall_dict(firewall_db)
|
||||
|
||||
def update_firewall(self, context, id, firewall):
|
||||
LOG.debug("update_firewall() called")
|
||||
fw = firewall['firewall']
|
||||
with context.session.begin(subtransactions=True):
|
||||
fw_db = self.get_firewall(context, id)
|
||||
self._validate_fw_parameters(context, fw, fw_db['tenant_id'])
|
||||
count = context.session.query(Firewall).filter_by(id=id).update(fw)
|
||||
if not count:
|
||||
raise f_exc.FirewallNotFound(firewall_id=id)
|
||||
return self.get_firewall(context, id)
|
||||
|
||||
def update_firewall_status(self, context, id, status, not_in=None):
|
||||
"""Conditionally update firewall status.
|
||||
|
||||
Status transition is performed only if firewall is not in the specified
|
||||
states as defined by 'not_in' list.
|
||||
"""
|
||||
# filter in_ wants iterable objects, None isn't.
|
||||
not_in = not_in or []
|
||||
with context.session.begin(subtransactions=True):
|
||||
return (context.session.query(Firewall).
|
||||
filter(Firewall.id == id).
|
||||
filter(~Firewall.status.in_(not_in)).
|
||||
update({'status': status}, synchronize_session=False))
|
||||
|
||||
def delete_firewall(self, context, id):
|
||||
LOG.debug("delete_firewall() called")
|
||||
with context.session.begin(subtransactions=True):
|
||||
# Note: Plugin should ensure that it's okay to delete if the
|
||||
# firewall is active
|
||||
count = context.session.query(Firewall).filter_by(id=id).delete()
|
||||
if not count:
|
||||
raise f_exc.FirewallNotFound(firewall_id=id)
|
||||
|
||||
def get_firewall(self, context, id, fields=None):
|
||||
LOG.debug("get_firewall() called")
|
||||
fw = self._get_firewall(context, id)
|
||||
return self._make_firewall_dict(fw, fields)
|
||||
|
||||
def get_firewalls(self, context, filters=None, fields=None):
|
||||
LOG.debug("get_firewalls() called")
|
||||
return self._get_collection(context, Firewall,
|
||||
self._make_firewall_dict,
|
||||
filters=filters, fields=fields)
|
||||
|
||||
def get_firewalls_count(self, context, filters=None):
|
||||
LOG.debug("get_firewalls_count() called")
|
||||
return self._get_collection_count(context, Firewall,
|
||||
filters=filters)
|
||||
|
||||
def create_firewall_policy(self, context, firewall_policy):
|
||||
LOG.debug("create_firewall_policy() called")
|
||||
fwp = firewall_policy['firewall_policy']
|
||||
with context.session.begin(subtransactions=True):
|
||||
fwp_db = FirewallPolicy(id=uuidutils.generate_uuid(),
|
||||
tenant_id=fwp['tenant_id'],
|
||||
name=fwp['name'],
|
||||
description=fwp['description'],
|
||||
shared=fwp['shared'])
|
||||
context.session.add(fwp_db)
|
||||
self._set_rules_for_policy(context, fwp_db, fwp)
|
||||
fwp_db.audited = fwp['audited']
|
||||
return self._make_firewall_policy_dict(fwp_db)
|
||||
|
||||
def update_firewall_policy(self, context, id, firewall_policy):
|
||||
LOG.debug("update_firewall_policy() called")
|
||||
fwp = firewall_policy['firewall_policy']
|
||||
with context.session.begin(subtransactions=True):
|
||||
fwp_db = self._get_firewall_policy(context, id)
|
||||
# check tenant ids are same for fw and fwp or not
|
||||
if not fwp.get('shared', True) and fwp_db.firewalls:
|
||||
for fw in fwp_db['firewalls']:
|
||||
if fwp_db['tenant_id'] != fw['tenant_id']:
|
||||
raise f_exc.FirewallPolicyInUse(
|
||||
firewall_policy_id=id)
|
||||
# check any existing rules are not shared
|
||||
if 'shared' in fwp and 'firewall_rules' not in fwp:
|
||||
self._check_unshared_rules_for_policy(fwp_db, fwp)
|
||||
elif 'firewall_rules' in fwp:
|
||||
self._set_rules_for_policy(context, fwp_db, fwp)
|
||||
del fwp['firewall_rules']
|
||||
if 'audited' not in fwp:
|
||||
fwp['audited'] = False
|
||||
fwp_db.update(fwp)
|
||||
return self._make_firewall_policy_dict(fwp_db)
|
||||
|
||||
def delete_firewall_policy(self, context, id):
|
||||
LOG.debug("delete_firewall_policy() called")
|
||||
with context.session.begin(subtransactions=True):
|
||||
fwp = self._get_firewall_policy(context, id)
|
||||
# Ensure that the firewall_policy is not
|
||||
# being used
|
||||
qry = context.session.query(Firewall)
|
||||
if qry.filter_by(firewall_policy_id=id).first():
|
||||
raise f_exc.FirewallPolicyInUse(firewall_policy_id=id)
|
||||
else:
|
||||
context.session.delete(fwp)
|
||||
|
||||
def get_firewall_policy(self, context, id, fields=None):
|
||||
LOG.debug("get_firewall_policy() called")
|
||||
fwp = self._get_firewall_policy(context, id)
|
||||
return self._make_firewall_policy_dict(fwp, fields)
|
||||
|
||||
def get_firewall_policies(self, context, filters=None, fields=None):
|
||||
LOG.debug("get_firewall_policies() called")
|
||||
return self._get_collection(context, FirewallPolicy,
|
||||
self._make_firewall_policy_dict,
|
||||
filters=filters, fields=fields)
|
||||
|
||||
def get_firewalls_policies_count(self, context, filters=None):
|
||||
LOG.debug("get_firewall_policies_count() called")
|
||||
return self._get_collection_count(context, FirewallPolicy,
|
||||
filters=filters)
|
||||
|
||||
def create_firewall_rule(self, context, firewall_rule):
|
||||
LOG.debug("create_firewall_rule() called")
|
||||
fwr = firewall_rule['firewall_rule']
|
||||
self._validate_fwr_protocol_parameters(fwr)
|
||||
self._validate_fwr_src_dst_ip_version(fwr)
|
||||
if not fwr['protocol'] and (fwr['source_port'] or
|
||||
fwr['destination_port']):
|
||||
raise f_exc.FirewallRuleWithPortWithoutProtocolInvalid()
|
||||
src_port_min, src_port_max = self._get_min_max_ports_from_range(
|
||||
fwr['source_port'])
|
||||
dst_port_min, dst_port_max = self._get_min_max_ports_from_range(
|
||||
fwr['destination_port'])
|
||||
with context.session.begin(subtransactions=True):
|
||||
fwr_db = FirewallRule(
|
||||
id=uuidutils.generate_uuid(),
|
||||
tenant_id=fwr['tenant_id'],
|
||||
name=fwr['name'],
|
||||
description=fwr['description'],
|
||||
shared=fwr['shared'],
|
||||
protocol=fwr['protocol'],
|
||||
ip_version=fwr['ip_version'],
|
||||
source_ip_address=fwr['source_ip_address'],
|
||||
destination_ip_address=fwr['destination_ip_address'],
|
||||
source_port_range_min=src_port_min,
|
||||
source_port_range_max=src_port_max,
|
||||
destination_port_range_min=dst_port_min,
|
||||
destination_port_range_max=dst_port_max,
|
||||
action=fwr['action'],
|
||||
enabled=fwr['enabled'])
|
||||
context.session.add(fwr_db)
|
||||
return self._make_firewall_rule_dict(fwr_db)
|
||||
|
||||
def update_firewall_rule(self, context, id, firewall_rule):
|
||||
LOG.debug("update_firewall_rule() called")
|
||||
fwr = firewall_rule['firewall_rule']
|
||||
self._validate_fwr_protocol_parameters(fwr)
|
||||
self._validate_fwr_src_dst_ip_version(fwr)
|
||||
fwr_db = self._get_firewall_rule(context, id)
|
||||
if fwr_db.firewall_policy_id:
|
||||
fwp_db = self._get_firewall_policy(context,
|
||||
fwr_db.firewall_policy_id)
|
||||
if 'shared' in fwr and not fwr['shared']:
|
||||
if fwr_db['tenant_id'] != fwp_db['tenant_id']:
|
||||
raise f_exc.FirewallRuleInUse(firewall_rule_id=id)
|
||||
if 'source_port' in fwr:
|
||||
src_port_min, src_port_max = self._get_min_max_ports_from_range(
|
||||
fwr['source_port'])
|
||||
fwr['source_port_range_min'] = src_port_min
|
||||
fwr['source_port_range_max'] = src_port_max
|
||||
del fwr['source_port']
|
||||
if 'destination_port' in fwr:
|
||||
dst_port_min, dst_port_max = self._get_min_max_ports_from_range(
|
||||
fwr['destination_port'])
|
||||
fwr['destination_port_range_min'] = dst_port_min
|
||||
fwr['destination_port_range_max'] = dst_port_max
|
||||
del fwr['destination_port']
|
||||
with context.session.begin(subtransactions=True):
|
||||
protocol = fwr.get('protocol', fwr_db['protocol'])
|
||||
if not protocol:
|
||||
sport = fwr.get('source_port_range_min',
|
||||
fwr_db['source_port_range_min'])
|
||||
dport = fwr.get('destination_port_range_min',
|
||||
fwr_db['destination_port_range_min'])
|
||||
if sport or dport:
|
||||
raise f_exc.FirewallRuleWithPortWithoutProtocolInvalid()
|
||||
fwr_db.update(fwr)
|
||||
if fwr_db.firewall_policy_id:
|
||||
fwp_db.audited = False
|
||||
return self._make_firewall_rule_dict(fwr_db)
|
||||
|
||||
def delete_firewall_rule(self, context, id):
|
||||
LOG.debug("delete_firewall_rule() called")
|
||||
with context.session.begin(subtransactions=True):
|
||||
fwr = self._get_firewall_rule(context, id)
|
||||
if fwr.firewall_policy_id:
|
||||
raise f_exc.FirewallRuleInUse(firewall_rule_id=id)
|
||||
context.session.delete(fwr)
|
||||
|
||||
def get_firewall_rule(self, context, id, fields=None):
|
||||
LOG.debug("get_firewall_rule() called")
|
||||
fwr = self._get_firewall_rule(context, id)
|
||||
return self._make_firewall_rule_dict(fwr, fields)
|
||||
|
||||
def get_firewall_rules(self, context, filters=None, fields=None):
|
||||
LOG.debug("get_firewall_rules() called")
|
||||
return self._get_collection(context, FirewallRule,
|
||||
self._make_firewall_rule_dict,
|
||||
filters=filters, fields=fields)
|
||||
|
||||
def get_firewalls_rules_count(self, context, filters=None):
|
||||
LOG.debug("get_firewall_rules_count() called")
|
||||
return self._get_collection_count(context, FirewallRule,
|
||||
filters=filters)
|
||||
|
||||
def _validate_insert_remove_rule_request(self, id, rule_info):
|
||||
if not rule_info or 'firewall_rule_id' not in rule_info:
|
||||
raise f_exc.FirewallRuleInfoMissing()
|
||||
|
||||
def insert_rule(self, context, id, rule_info):
|
||||
LOG.debug("insert_rule() called")
|
||||
self._validate_insert_remove_rule_request(id, rule_info)
|
||||
firewall_rule_id = rule_info['firewall_rule_id']
|
||||
insert_before = True
|
||||
ref_firewall_rule_id = None
|
||||
if not firewall_rule_id:
|
||||
raise f_exc.FirewallRuleNotFound(firewall_rule_id=None)
|
||||
if 'insert_before' in rule_info:
|
||||
ref_firewall_rule_id = rule_info['insert_before']
|
||||
if not ref_firewall_rule_id and 'insert_after' in rule_info:
|
||||
# If insert_before is set, we will ignore insert_after.
|
||||
ref_firewall_rule_id = rule_info['insert_after']
|
||||
insert_before = False
|
||||
with context.session.begin(subtransactions=True):
|
||||
fwr_db = self._get_firewall_rule(context, firewall_rule_id)
|
||||
fwp_db = self._get_firewall_policy(context, id)
|
||||
if fwr_db.firewall_policy_id:
|
||||
raise f_exc.FirewallRuleInUse(firewall_rule_id=fwr_db['id'])
|
||||
self._check_firewall_rule_conflict(fwr_db, fwp_db)
|
||||
if ref_firewall_rule_id:
|
||||
# If reference_firewall_rule_id is set, the new rule
|
||||
# is inserted depending on the value of insert_before.
|
||||
# If insert_before is set, the new rule is inserted before
|
||||
# reference_firewall_rule_id, and if it is not set the new
|
||||
# rule is inserted after reference_firewall_rule_id.
|
||||
ref_fwr_db = self._get_firewall_rule(
|
||||
context, ref_firewall_rule_id)
|
||||
if ref_fwr_db.firewall_policy_id != id:
|
||||
raise f_exc.FirewallRuleNotAssociatedWithPolicy(
|
||||
firewall_rule_id=ref_fwr_db['id'],
|
||||
firewall_policy_id=id)
|
||||
if insert_before:
|
||||
position = ref_fwr_db.position
|
||||
else:
|
||||
position = ref_fwr_db.position + 1
|
||||
else:
|
||||
# If reference_firewall_rule_id is not set, it is assumed
|
||||
# that the new rule needs to be inserted at the top.
|
||||
# insert_before field is ignored.
|
||||
# So default insertion is always at the top.
|
||||
# Also note that position numbering starts at 1.
|
||||
position = 1
|
||||
return self._process_rule_for_policy(context, id, fwr_db,
|
||||
position)
|
||||
|
||||
def remove_rule(self, context, id, rule_info):
|
||||
LOG.debug("remove_rule() called")
|
||||
self._validate_insert_remove_rule_request(id, rule_info)
|
||||
firewall_rule_id = rule_info['firewall_rule_id']
|
||||
if not firewall_rule_id:
|
||||
raise f_exc.FirewallRuleNotFound(firewall_rule_id=None)
|
||||
with context.session.begin(subtransactions=True):
|
||||
fwr_db = self._get_firewall_rule(context, firewall_rule_id)
|
||||
if fwr_db.firewall_policy_id != id:
|
||||
raise f_exc.FirewallRuleNotAssociatedWithPolicy(
|
||||
firewall_rule_id=fwr_db['id'],
|
||||
firewall_policy_id=id)
|
||||
return self._process_rule_for_policy(context, id, fwr_db, None)
|
||||
|
||||
def get_firewall_tenant_ids_on_host(self, context, host):
|
||||
query = context.session.query(Firewall.tenant_id)
|
||||
query = query.join(fw_r_ins_db.FirewallRouterAssociation)
|
||||
query = query.join(l3agent_model.RouterL3AgentBinding,
|
||||
l3agent_model.RouterL3AgentBinding.router_id ==
|
||||
fw_r_ins_db.FirewallRouterAssociation.router_id)
|
||||
query = query.join(agent_model.Agent)
|
||||
query = query.filter(agent_model.Agent.host == host)
|
||||
query = query.distinct()
|
||||
return [item[0] for item in query]
|
||||
|
||||
|
||||
def migration_callback(resource, event, trigger, **kwargs):
|
||||
context = kwargs['context']
|
||||
router = kwargs['router']
|
||||
fw_plugin = directory.get_plugin(fwaas_constants.FIREWALL)
|
||||
if fw_plugin:
|
||||
tenant_firewalls = fw_plugin.get_firewalls(
|
||||
context, filters={'tenant_id': [router['tenant_id']]})
|
||||
if tenant_firewalls:
|
||||
raise l3.RouterInUse(router_id=router['id'])
|
||||
|
||||
|
||||
def subscribe():
|
||||
registry.subscribe(
|
||||
migration_callback, resources.ROUTER, events.BEFORE_UPDATE)
|
||||
|
||||
# NOTE(armax): multiple FW service plugins (potentially out of tree) may
|
||||
# inherit from firewall_db and may need the callbacks to be processed. Having
|
||||
# an implicit subscription (through the module import) preserves the existing
|
||||
# behavior, and at the same time it avoids fixing it manually in each and
|
||||
# every fw plugin out there. That said, The subscription is also made
|
||||
# explicitly in the reference fw plugin. The subscription operation is
|
||||
# idempotent so there is no harm in registering the same callback multiple
|
||||
# times.
|
||||
subscribe()
|
|
@ -1,98 +0,0 @@
|
|||
# Copyright 2015 Cisco Systems Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutron_lib.db import model_base
|
||||
from neutron_lib.exceptions import firewall_v1 as fwrtrins
|
||||
from oslo_log import helpers as log_helpers
|
||||
from oslo_log import log as logging
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FirewallRouterAssociation(model_base.BASEV2):
|
||||
|
||||
"""Tracks FW Router Association"""
|
||||
|
||||
__tablename__ = 'firewall_router_associations'
|
||||
|
||||
fw_id = sa.Column(sa.String(36),
|
||||
sa.ForeignKey('firewalls.id', ondelete="CASCADE"),
|
||||
primary_key=True)
|
||||
router_id = sa.Column(sa.String(36),
|
||||
sa.ForeignKey('routers.id', ondelete="CASCADE"),
|
||||
primary_key=True)
|
||||
|
||||
|
||||
class FirewallRouterInsertionDbMixin(object):
|
||||
|
||||
"""Access methods for the firewall_router_associations table."""
|
||||
|
||||
@log_helpers.log_method_call
|
||||
def set_routers_for_firewall(self, context, fw):
|
||||
"""Sets the routers associated with the fw."""
|
||||
with context.session.begin(subtransactions=True):
|
||||
for r_id in fw['router_ids']:
|
||||
fw_rtr_db = FirewallRouterAssociation(fw_id=fw['fw_id'],
|
||||
router_id=r_id)
|
||||
context.session.add(fw_rtr_db)
|
||||
|
||||
@log_helpers.log_method_call
|
||||
def get_firewall_routers(self, context, fwid):
|
||||
"""Gets all routers associated with a firewall."""
|
||||
with context.session.begin(subtransactions=True):
|
||||
fw_rtr_qry = context.session.query(
|
||||
FirewallRouterAssociation.router_id)
|
||||
fw_rtr_rows = fw_rtr_qry.filter_by(fw_id=fwid)
|
||||
fw_rtrs = [entry.router_id for entry in fw_rtr_rows]
|
||||
LOG.debug("get_firewall_routers(): fw_rtrs: %s", fw_rtrs)
|
||||
return fw_rtrs
|
||||
|
||||
@log_helpers.log_method_call
|
||||
def validate_firewall_routers_not_in_use(
|
||||
self, context, router_ids, fwid=None):
|
||||
"""Validate if router-ids not associated with any firewall.
|
||||
|
||||
If any of the router-ids in the list is already associated with
|
||||
a firewall, raise an exception else just return.
|
||||
"""
|
||||
fw_rtr_qry = context.session.query(FirewallRouterAssociation.router_id)
|
||||
fw_rtrs = fw_rtr_qry.filter(
|
||||
FirewallRouterAssociation.router_id.in_(router_ids),
|
||||
FirewallRouterAssociation.fw_id != fwid).all()
|
||||
if fw_rtrs:
|
||||
router_ids = [entry.router_id for entry in fw_rtrs]
|
||||
raise fwrtrins.FirewallRouterInUse(router_ids=router_ids)
|
||||
|
||||
@log_helpers.log_method_call
|
||||
def update_firewall_routers(self, context, fw):
|
||||
"""Update the firewall with new routers.
|
||||
|
||||
This involves removing existing router associations and replacing
|
||||
it with the new router associations provided in the update method.
|
||||
"""
|
||||
with context.session.begin(subtransactions=True):
|
||||
fw_rtr_qry = context.session.query(FirewallRouterAssociation)
|
||||
fw_rtr_qry.filter_by(fw_id=fw['fw_id']).delete()
|
||||
if fw['router_ids']:
|
||||
self.set_routers_for_firewall(context, fw)
|
||||
|
||||
# TODO(sridar): Investigate potential corner case if rpc failure
|
||||
# happens on PENDING_UPDATE and agent did not restart. Evaluate
|
||||
# complexity vs benefit of holding on to old entries until ack
|
||||
# from agent.
|
||||
|
||||
return fw
|
|
@ -1,816 +0,0 @@
|
|||
# Copyright (c) 2016
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutron.db import common_db_mixin as base_db
|
||||
from neutron_lib import constants as nl_constants
|
||||
from neutron_lib.db import model_base
|
||||
from neutron_lib.exceptions import firewall_v2 as f_exc
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import uuidutils
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.ext.orderinglist import ordering_list
|
||||
from sqlalchemy import orm
|
||||
from sqlalchemy.orm import exc
|
||||
|
||||
import netaddr
|
||||
|
||||
from neutron_fwaas.extensions import firewall_v2 as fw_ext
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HasName(object):
|
||||
name = sa.Column(sa.String(255))
|
||||
|
||||
|
||||
class HasDescription(object):
|
||||
description = sa.Column(sa.String(1024))
|
||||
|
||||
|
||||
class FirewallRuleV2(model_base.BASEV2, model_base.HasId, HasName,
|
||||
HasDescription, model_base.HasProject):
|
||||
__tablename__ = "firewall_rules_v2"
|
||||
shared = sa.Column(sa.Boolean)
|
||||
protocol = sa.Column(sa.String(40))
|
||||
ip_version = sa.Column(sa.Integer)
|
||||
source_ip_address = sa.Column(sa.String(46))
|
||||
destination_ip_address = sa.Column(sa.String(46))
|
||||
source_port_range_min = sa.Column(sa.Integer)
|
||||
source_port_range_max = sa.Column(sa.Integer)
|
||||
destination_port_range_min = sa.Column(sa.Integer)
|
||||
destination_port_range_max = sa.Column(sa.Integer)
|
||||
action = sa.Column(sa.Enum('allow', 'deny', 'reject',
|
||||
name='firewallrules_action'))
|
||||
enabled = sa.Column(sa.Boolean)
|
||||
|
||||
|
||||
class FirewallGroup(model_base.BASEV2, model_base.HasId, HasName,
|
||||
HasDescription, model_base.HasProject):
|
||||
__tablename__ = 'firewall_groups_v2'
|
||||
ports = orm.relationship(
|
||||
'FirewallGroupPortAssociation',
|
||||
backref=orm.backref('firewall_group_port_associations_v2',
|
||||
cascade='all, delete'))
|
||||
name = sa.Column(sa.String(255))
|
||||
description = sa.Column(sa.String(1024))
|
||||
ingress_firewall_policy_id = sa.Column(sa.String(36),
|
||||
sa.ForeignKey(
|
||||
'firewall_policies_v2.id'))
|
||||
egress_firewall_policy_id = sa.Column(sa.String(36),
|
||||
sa.ForeignKey(
|
||||
'firewall_policies_v2.id'))
|
||||
admin_state_up = sa.Column(sa.Boolean)
|
||||
status = sa.Column(sa.String(16))
|
||||
shared = sa.Column(sa.Boolean)
|
||||
|
||||
|
||||
class FirewallGroupPortAssociation(model_base.BASEV2):
|
||||
__tablename__ = 'firewall_group_port_associations_v2'
|
||||
firewall_group_id = sa.Column(sa.String(36),
|
||||
sa.ForeignKey('firewall_groups_v2.id',
|
||||
ondelete="CASCADE"),
|
||||
primary_key=True)
|
||||
port_id = sa.Column(sa.String(36),
|
||||
sa.ForeignKey('ports.id', ondelete="CASCADE"),
|
||||
primary_key=True)
|
||||
|
||||
|
||||
class FirewallPolicyRuleAssociation(model_base.BASEV2):
|
||||
|
||||
"""Tracks FW Policy and Rule(s) Association"""
|
||||
|
||||
__tablename__ = 'firewall_policy_rule_associations_v2'
|
||||
|
||||
firewall_policy_id = sa.Column(sa.String(36),
|
||||
sa.ForeignKey('firewall_policies_v2.id',
|
||||
ondelete="CASCADE"),
|
||||
primary_key=True)
|
||||
firewall_rule_id = sa.Column(sa.String(36),
|
||||
sa.ForeignKey('firewall_rules_v2.id',
|
||||
ondelete="CASCADE"),
|
||||
primary_key=True)
|
||||
position = sa.Column(sa.Integer)
|
||||
|
||||
|
||||
class FirewallPolicy(model_base.BASEV2, model_base.HasId, HasName,
|
||||
HasDescription, model_base.HasProject):
|
||||
__tablename__ = 'firewall_policies_v2'
|
||||
name = sa.Column(sa.String(255))
|
||||
description = sa.Column(sa.String(1024))
|
||||
rule_count = sa.Column(sa.Integer)
|
||||
audited = sa.Column(sa.Boolean)
|
||||
rule_associations = orm.relationship(
|
||||
FirewallPolicyRuleAssociation,
|
||||
backref=orm.backref('firewall_policies_v2', cascade='all, delete'),
|
||||
order_by='FirewallPolicyRuleAssociation.position',
|
||||
collection_class=ordering_list('position', count_from=1))
|
||||
shared = sa.Column(sa.Boolean)
|
||||
|
||||
|
||||
class Firewall_db_mixin_v2(fw_ext.Firewallv2PluginBase, base_db.CommonDbMixin):
|
||||
|
||||
def _get_firewall_group(self, context, id):
|
||||
try:
|
||||
return self._get_by_id(context, FirewallGroup, id)
|
||||
except exc.NoResultFound:
|
||||
raise f_exc.FirewallGroupNotFound(firewall_id=id)
|
||||
|
||||
def _get_firewall_policy(self, context, id):
|
||||
try:
|
||||
return self._get_by_id(context, FirewallPolicy, id)
|
||||
except exc.NoResultFound:
|
||||
raise f_exc.FirewallPolicyNotFound(firewall_policy_id=id)
|
||||
|
||||
def _get_firewall_rule(self, context, id):
|
||||
try:
|
||||
return self._get_by_id(context, FirewallRuleV2, id)
|
||||
except exc.NoResultFound:
|
||||
raise f_exc.FirewallRuleNotFound(firewall_rule_id=id)
|
||||
|
||||
def _validate_fwr_protocol_parameters(self, fwr, fwr_db=None):
|
||||
protocol = fwr.get('protocol', None)
|
||||
if fwr_db and not protocol:
|
||||
protocol = fwr_db.protocol
|
||||
if protocol not in (nl_constants.PROTO_NAME_TCP,
|
||||
nl_constants.PROTO_NAME_UDP):
|
||||
if (fwr.get('source_port', None) or
|
||||
fwr.get('destination_port', None)):
|
||||
raise f_exc.FirewallRuleInvalidICMPParameter(
|
||||
param="Source, destination port")
|
||||
|
||||
def _validate_fwr_src_dst_ip_version(self, fwr, fwr_db=None):
|
||||
src_version = dst_version = None
|
||||
if fwr.get('source_ip_address', None):
|
||||
src_version = netaddr.IPNetwork(fwr['source_ip_address']).version
|
||||
if fwr.get('destination_ip_address', None):
|
||||
dst_version = netaddr.IPNetwork(
|
||||
fwr['destination_ip_address']).version
|
||||
rule_ip_version = fwr.get('ip_version', None)
|
||||
if not rule_ip_version and fwr_db:
|
||||
rule_ip_version = fwr_db.ip_version
|
||||
if ((src_version and src_version != rule_ip_version) or
|
||||
(dst_version and dst_version != rule_ip_version)):
|
||||
raise f_exc.FirewallIpAddressConflict()
|
||||
|
||||
def _validate_fwr_port_range(self, min_port, max_port):
|
||||
if int(min_port) > int(max_port):
|
||||
port_range = '%s:%s' % (min_port, max_port)
|
||||
raise f_exc.FirewallRuleInvalidPortValue(port=port_range)
|
||||
|
||||
def _get_min_max_ports_from_range(self, port_range):
|
||||
if not port_range:
|
||||
return [None, None]
|
||||
min_port, sep, max_port = port_range.partition(":")
|
||||
if not max_port:
|
||||
max_port = min_port
|
||||
self._validate_fwr_port_range(min_port, max_port)
|
||||
return [int(min_port), int(max_port)]
|
||||
|
||||
def _get_port_range_from_min_max_ports(self, min_port, max_port):
|
||||
if not min_port:
|
||||
return None
|
||||
if min_port == max_port:
|
||||
return str(min_port)
|
||||
self._validate_fwr_port_range(min_port, max_port)
|
||||
return '%s:%s' % (min_port, max_port)
|
||||
|
||||
def _make_firewall_rule_dict(self, firewall_rule, fields=None):
|
||||
src_port_range = self._get_port_range_from_min_max_ports(
|
||||
firewall_rule['source_port_range_min'],
|
||||
firewall_rule['source_port_range_max'])
|
||||
dst_port_range = self._get_port_range_from_min_max_ports(
|
||||
firewall_rule['destination_port_range_min'],
|
||||
firewall_rule['destination_port_range_max'])
|
||||
res = {'id': firewall_rule['id'],
|
||||
'tenant_id': firewall_rule['tenant_id'],
|
||||
'name': firewall_rule['name'],
|
||||
'description': firewall_rule['description'],
|
||||
'protocol': firewall_rule['protocol'],
|
||||
'ip_version': firewall_rule['ip_version'],
|
||||
'source_ip_address': firewall_rule['source_ip_address'],
|
||||
'destination_ip_address':
|
||||
firewall_rule['destination_ip_address'],
|
||||
'source_port': src_port_range,
|
||||
'destination_port': dst_port_range,
|
||||
'action': firewall_rule['action'],
|
||||
'enabled': firewall_rule['enabled'],
|
||||
'shared': firewall_rule['shared']}
|
||||
return self._fields(res, fields)
|
||||
|
||||
def _make_firewall_policy_dict(self, firewall_policy, fields=None):
|
||||
fw_rules = [
|
||||
rule_association.firewall_rule_id
|
||||
for rule_association in firewall_policy['rule_associations']]
|
||||
res = {'id': firewall_policy['id'],
|
||||
'tenant_id': firewall_policy['tenant_id'],
|
||||
'name': firewall_policy['name'],
|
||||
'description': firewall_policy['description'],
|
||||
'audited': firewall_policy['audited'],
|
||||
'firewall_rules': fw_rules,
|
||||
'shared': firewall_policy['shared']}
|
||||
return self._fields(res, fields)
|
||||
|
||||
def _make_firewall_group_dict(self, firewall_group, fields=None):
|
||||
fwg_ports = [
|
||||
port_assoc.port_id for port_assoc in firewall_group['ports']
|
||||
]
|
||||
res = {'id': firewall_group['id'],
|
||||
'tenant_id': firewall_group['tenant_id'],
|
||||
'name': firewall_group['name'],
|
||||
'description': firewall_group['description'],
|
||||
'ingress_firewall_policy_id':
|
||||
firewall_group['ingress_firewall_policy_id'],
|
||||
'egress_firewall_policy_id':
|
||||
firewall_group['egress_firewall_policy_id'],
|
||||
'admin_state_up': firewall_group['admin_state_up'],
|
||||
'ports': fwg_ports,
|
||||
'status': firewall_group['status'],
|
||||
'shared': firewall_group['shared']}
|
||||
return self._fields(res, fields)
|
||||
|
||||
def _get_policy_ordered_rules(self, context, policy_id):
|
||||
query = (context.session.query(FirewallRuleV2)
|
||||
.join(FirewallPolicyRuleAssociation)
|
||||
.filter_by(firewall_policy_id=policy_id)
|
||||
.order_by(FirewallPolicyRuleAssociation.position))
|
||||
return [self._make_firewall_rule_dict(rule) for rule in query]
|
||||
|
||||
def _make_firewall_group_dict_with_rules(self, context, firewall_group_id):
|
||||
firewall_group = self.get_firewall_group(context, firewall_group_id)
|
||||
ingress_policy_id = firewall_group['ingress_firewall_policy_id']
|
||||
if ingress_policy_id:
|
||||
firewall_group['ingress_rule_list'] = (
|
||||
self._get_policy_ordered_rules(context, ingress_policy_id))
|
||||
else:
|
||||
firewall_group['ingress_rule_list'] = []
|
||||
|
||||
egress_policy_id = firewall_group['egress_firewall_policy_id']
|
||||
if egress_policy_id:
|
||||
firewall_group['egress_rule_list'] = (
|
||||
self._get_policy_ordered_rules(context, egress_policy_id))
|
||||
else:
|
||||
firewall_group['egress_rule_list'] = []
|
||||
return firewall_group
|
||||
|
||||
def _check_firewall_rule_conflict(self, fwr_db, fwp_db):
|
||||
if not fwr_db['shared']:
|
||||
if fwr_db['tenant_id'] != fwp_db['tenant_id']:
|
||||
raise f_exc.FirewallRuleConflict(
|
||||
firewall_rule_id=fwr_db['id'],
|
||||
project_id=fwr_db['tenant_id'])
|
||||
|
||||
def _process_rule_for_policy(self, context, firewall_policy_id,
|
||||
firewall_rule_id, position, association_db):
|
||||
with context.session.begin(subtransactions=True):
|
||||
fwp_query = context.session.query(
|
||||
FirewallPolicy).with_lockmode('update')
|
||||
fwp_db = fwp_query.filter_by(id=firewall_policy_id).one()
|
||||
if position:
|
||||
# Note that although position numbering starts at 1,
|
||||
# internal ordering of the list starts at 0, so we compensate.
|
||||
fwp_db.rule_associations.insert(
|
||||
position - 1,
|
||||
FirewallPolicyRuleAssociation(
|
||||
firewall_rule_id=firewall_rule_id))
|
||||
else:
|
||||
fwp_db.rule_associations.remove(association_db)
|
||||
context.session.delete(association_db)
|
||||
fwp_db.rule_associations.reorder()
|
||||
fwp_db.audited = False
|
||||
return self._make_firewall_policy_dict(fwp_db)
|
||||
|
||||
def _get_policy_rule_association_query(self, context, firewall_policy_id,
|
||||
firewall_rule_id):
|
||||
fwpra_query = context.session.query(FirewallPolicyRuleAssociation)
|
||||
return fwpra_query.filter_by(firewall_policy_id=firewall_policy_id,
|
||||
firewall_rule_id=firewall_rule_id)
|
||||
|
||||
def _ensure_rule_not_already_associated(self, context, firewall_policy_id,
|
||||
firewall_rule_id):
|
||||
"""Checks that a rule is not already associated with a particular
|
||||
policy. If it is the function will throw an exception.
|
||||
"""
|
||||
try:
|
||||
self._get_policy_rule_association_query(
|
||||
context, firewall_policy_id, firewall_rule_id).one()
|
||||
raise f_exc.FirewallRuleAlreadyAssociated(
|
||||
firewall_rule_id=firewall_rule_id,
|
||||
firewall_policy_id=firewall_policy_id)
|
||||
except exc.NoResultFound:
|
||||
return
|
||||
|
||||
def _get_policy_rule_association(self, context, firewall_policy_id,
|
||||
firewall_rule_id):
|
||||
"""Returns the association between a firewall rule and a firewall
|
||||
policy. Throws an exception if the assocaition does not exist.
|
||||
"""
|
||||
try:
|
||||
return self._get_policy_rule_association_query(
|
||||
context, firewall_policy_id, firewall_rule_id).one()
|
||||
except exc.NoResultFound:
|
||||
raise f_exc.FirewallRuleNotAssociatedWithPolicy(
|
||||
firewall_rule_id=firewall_rule_id,
|
||||
firewall_policy_id=firewall_policy_id)
|
||||
|
||||
def create_firewall_rule(self, context, firewall_rule):
|
||||
LOG.debug("create_firewall_rule() called")
|
||||
fwr = firewall_rule['firewall_rule']
|
||||
self._validate_fwr_protocol_parameters(fwr)
|
||||
self._validate_fwr_src_dst_ip_version(fwr)
|
||||
if not fwr['protocol'] and (fwr['source_port'] or
|
||||
fwr['destination_port']):
|
||||
raise f_exc.FirewallRuleWithPortWithoutProtocolInvalid()
|
||||
src_port_min, src_port_max = self._get_min_max_ports_from_range(
|
||||
fwr['source_port'])
|
||||
dst_port_min, dst_port_max = self._get_min_max_ports_from_range(
|
||||
fwr['destination_port'])
|
||||
with context.session.begin(subtransactions=True):
|
||||
fwr_db = FirewallRuleV2(
|
||||
id=uuidutils.generate_uuid(),
|
||||
tenant_id=fwr['tenant_id'],
|
||||
name=fwr['name'],
|
||||
description=fwr['description'],
|
||||
protocol=fwr['protocol'],
|
||||
ip_version=fwr['ip_version'],
|
||||
source_ip_address=fwr['source_ip_address'],
|
||||
destination_ip_address=fwr['destination_ip_address'],
|
||||
source_port_range_min=src_port_min,
|
||||
source_port_range_max=src_port_max,
|
||||
destination_port_range_min=dst_port_min,
|
||||
destination_port_range_max=dst_port_max,
|
||||
action=fwr['action'],
|
||||
enabled=fwr['enabled'],
|
||||
shared=fwr['shared'])
|
||||
context.session.add(fwr_db)
|
||||
return self._make_firewall_rule_dict(fwr_db)
|
||||
|
||||
def update_firewall_rule(self, context, id, firewall_rule):
|
||||
LOG.debug("update_firewall_rule() called")
|
||||
fwr = firewall_rule['firewall_rule']
|
||||
fwr_db = self._get_firewall_rule(context, id)
|
||||
self._validate_fwr_protocol_parameters(fwr, fwr_db=fwr_db)
|
||||
self._validate_fwr_src_dst_ip_version(fwr, fwr_db=fwr_db)
|
||||
if 'source_port' in fwr:
|
||||
src_port_min, src_port_max = self._get_min_max_ports_from_range(
|
||||
fwr['source_port'])
|
||||
fwr['source_port_range_min'] = src_port_min
|
||||
fwr['source_port_range_max'] = src_port_max
|
||||
del fwr['source_port']
|
||||
if 'destination_port' in fwr:
|
||||
dst_port_min, dst_port_max = self._get_min_max_ports_from_range(
|
||||
fwr['destination_port'])
|
||||
fwr['destination_port_range_min'] = dst_port_min
|
||||
fwr['destination_port_range_max'] = dst_port_max
|
||||
del fwr['destination_port']
|
||||
with context.session.begin(subtransactions=True):
|
||||
protocol = fwr.get('protocol', fwr_db['protocol'])
|
||||
if not protocol:
|
||||
sport = fwr.get('source_port_range_min',
|
||||
fwr_db['source_port_range_min'])
|
||||
dport = fwr.get('destination_port_range_min',
|
||||
fwr_db['destination_port_range_min'])
|
||||
if sport or dport:
|
||||
raise f_exc.FirewallRuleWithPortWithoutProtocolInvalid()
|
||||
fwr_db.update(fwr)
|
||||
# if the rule on a policy, fix audited flag
|
||||
fwp_ids = self._get_policies_with_rule(context, id)
|
||||
for fwp_id in fwp_ids:
|
||||
fwp_db = self._get_firewall_policy(context, fwp_id)
|
||||
fwp_db['audited'] = False
|
||||
return self._make_firewall_rule_dict(fwr_db)
|
||||
|
||||
def delete_firewall_rule(self, context, id):
|
||||
LOG.debug("delete_firewall_rule() called")
|
||||
with context.session.begin(subtransactions=True):
|
||||
fwr = self._get_firewall_rule(context, id)
|
||||
# make sure rule is not associated with any policy
|
||||
if self._get_policies_with_rule(context, id):
|
||||
raise f_exc.FirewallRuleInUse(firewall_rule_id=id)
|
||||
context.session.delete(fwr)
|
||||
|
||||
def insert_rule(self, context, id, rule_info):
|
||||
LOG.debug("insert_rule() called")
|
||||
self._validate_insert_remove_rule_request(id, rule_info)
|
||||
firewall_rule_id = rule_info['firewall_rule_id']
|
||||
# ensure rule is not already assigned to the policy
|
||||
self._ensure_rule_not_already_associated(context, id, firewall_rule_id)
|
||||
insert_before = True
|
||||
ref_firewall_rule_id = None
|
||||
if not firewall_rule_id:
|
||||
raise f_exc.FirewallRuleNotFound(firewall_rule_id=None)
|
||||
if 'insert_before' in rule_info:
|
||||
ref_firewall_rule_id = rule_info['insert_before']
|
||||
if not ref_firewall_rule_id and 'insert_after' in rule_info:
|
||||
# If insert_before is set, we will ignore insert_after.
|
||||
ref_firewall_rule_id = rule_info['insert_after']
|
||||
insert_before = False
|
||||
with context.session.begin(subtransactions=True):
|
||||
fwr_db = self._get_firewall_rule(context, firewall_rule_id)
|
||||
fwp_db = self._get_firewall_policy(context, id)
|
||||
self._check_firewall_rule_conflict(fwr_db, fwp_db)
|
||||
if ref_firewall_rule_id:
|
||||
# If reference_firewall_rule_id is set, the new rule
|
||||
# is inserted depending on the value of insert_before.
|
||||
# If insert_before is set, the new rule is inserted before
|
||||
# reference_firewall_rule_id, and if it is not set the new
|
||||
# rule is inserted after reference_firewall_rule_id.
|
||||
fwpra_db = self._get_policy_rule_association(
|
||||
context, id, ref_firewall_rule_id)
|
||||
if insert_before:
|
||||
position = fwpra_db.position
|
||||
else:
|
||||
position = fwpra_db.position + 1
|
||||
else:
|
||||
# If reference_firewall_rule_id is not set, it is assumed
|
||||
# that the new rule needs to be inserted at the top.
|
||||
# insert_before field is ignored.
|
||||
# So default insertion is always at the top.
|
||||
# Also note that position numbering starts at 1.
|
||||
position = 1
|
||||
return self._process_rule_for_policy(context, id, firewall_rule_id,
|
||||
position, None)
|
||||
|
||||
def remove_rule(self, context, id, rule_info):
|
||||
LOG.debug("remove_rule() called")
|
||||
self._validate_insert_remove_rule_request(id, rule_info)
|
||||
firewall_rule_id = rule_info['firewall_rule_id']
|
||||
if not firewall_rule_id:
|
||||
raise f_exc.FirewallRuleNotFound(firewall_rule_id=None)
|
||||
with context.session.begin(subtransactions=True):
|
||||
self._get_firewall_rule(context, firewall_rule_id)
|
||||
fwpra_db = self._get_policy_rule_association(context, id,
|
||||
firewall_rule_id)
|
||||
return self._process_rule_for_policy(context, id, firewall_rule_id,
|
||||
None, fwpra_db)
|
||||
|
||||
def get_firewall_rule(self, context, id, fields=None):
|
||||
LOG.debug("get_firewall_rule() called")
|
||||
fwr = self._get_firewall_rule(context, id)
|
||||
return self._make_firewall_rule_dict(fwr, fields)
|
||||
|
||||
def get_firewall_rules(self, context, filters=None, fields=None):
|
||||
LOG.debug("get_firewall_rules() called")
|
||||
return self._get_collection(context, FirewallRuleV2,
|
||||
self._make_firewall_rule_dict,
|
||||
filters=filters, fields=fields)
|
||||
|
||||
def _validate_insert_remove_rule_request(self, id, rule_info):
|
||||
if not rule_info or 'firewall_rule_id' not in rule_info:
|
||||
raise f_exc.FirewallRuleInfoMissing()
|
||||
|
||||
def _delete_rules_in_policy(self, context, firewall_policy_id):
|
||||
"""Delete the rules in the firewall policy."""
|
||||
with context.session.begin(subtransactions=True):
|
||||
fw_pol_rule_qry = context.session.query(
|
||||
FirewallPolicyRuleAssociation)
|
||||
fw_pol_rule_qry.filter_by(
|
||||
firewall_policy_id=firewall_policy_id).delete()
|
||||
return
|
||||
|
||||
def _get_rules_in_policy(self, context, fwpid):
|
||||
"""Gets rules in a firewall policy"""
|
||||
with context.session.begin(subtransactions=True):
|
||||
fw_pol_rule_qry = context.session.query(
|
||||
FirewallPolicyRuleAssociation).filter_by(
|
||||
firewall_policy_id=fwpid)
|
||||
fwp_rules = [entry.firewall_rule_id for entry in fw_pol_rule_qry]
|
||||
return fwp_rules
|
||||
|
||||
def _get_policies_with_rule(self, context, fwrid):
|
||||
"""Gets rules in a firewall policy"""
|
||||
with context.session.begin(subtransactions=True):
|
||||
fw_pol_rule_qry = context.session.query(
|
||||
FirewallPolicyRuleAssociation).filter_by(
|
||||
firewall_rule_id=fwrid)
|
||||
fwps = [entry.firewall_policy_id for entry in fw_pol_rule_qry]
|
||||
return fwps
|
||||
|
||||
def _set_rules_in_policy_rule_assoc(self, context, fwp_db, fwp):
|
||||
# Pull the rules and add it to policy - rule association table
|
||||
# Set the position (this can be used in the making the dict)
|
||||
# might be good to track the last position
|
||||
rule_id_list = fwp['firewall_rules']
|
||||
if not rule_id_list:
|
||||
return
|
||||
position = 0
|
||||
with context.session.begin(subtransactions=True):
|
||||
for rule_id in rule_id_list:
|
||||
fw_pol_rul_db = FirewallPolicyRuleAssociation(
|
||||
firewall_policy_id=fwp_db['id'],
|
||||
firewall_rule_id=rule_id,
|
||||
position=position)
|
||||
context.session.add(fw_pol_rul_db)
|
||||
position += 1
|
||||
|
||||
def _check_rules_for_policy_is_valid(self, context, fwp, fwp_db,
|
||||
rule_id_list, filters):
|
||||
rules_in_fwr_db = self._get_collection_query(context, FirewallRuleV2,
|
||||
filters=filters)
|
||||
rules_dict = dict((fwr_db['id'], fwr_db) for fwr_db in rules_in_fwr_db)
|
||||
for fwrule_id in rule_id_list:
|
||||
if fwrule_id not in rules_dict:
|
||||
# Bail as soon as we find an invalid rule.
|
||||
raise f_exc.FirewallRuleNotFound(
|
||||
firewall_rule_id=fwrule_id)
|
||||
if 'shared' in fwp:
|
||||
if fwp['shared'] and not rules_dict[fwrule_id]['shared']:
|
||||
raise f_exc.FirewallRuleSharingConflict(
|
||||
firewall_rule_id=fwrule_id,
|
||||
firewall_policy_id=fwp_db['id'])
|
||||
elif fwp_db['shared'] and not rules_dict[fwrule_id]['shared']:
|
||||
raise f_exc.FirewallRuleSharingConflict(
|
||||
firewall_rule_id=fwrule_id,
|
||||
firewall_policy_id=fwp_db['id'])
|
||||
else:
|
||||
# the policy is not shared, the rule and policy should be in
|
||||
# the same project if the rule is not shared.
|
||||
if not rules_dict[fwrule_id]['shared']:
|
||||
if (rules_dict[fwrule_id]['tenant_id'] != fwp_db[
|
||||
'tenant_id']):
|
||||
raise f_exc.FirewallRuleConflict(
|
||||
firewall_rule_id=fwrule_id,
|
||||
project_id=rules_dict[fwrule_id]['tenant_id'])
|
||||
|
||||
def _check_if_rules_shared_for_policy_shared(self, context, fwp_db, fwp):
|
||||
if fwp['shared']:
|
||||
rules_in_db = fwp_db.rule_associations
|
||||
for entry in rules_in_db:
|
||||
fwr_db = self._get_firewall_rule(context,
|
||||
entry.firewall_rule_id)
|
||||
if not fwp_db['shared']:
|
||||
raise f_exc.FirewallPolicySharingConflict(
|
||||
firewall_rule_id=fwr_db['id'],
|
||||
firewall_policy_id=fwp_db['id'])
|
||||
|
||||
def _get_fwgs_with_policy(self, context, fwp_id):
|
||||
with context.session.begin(subtransactions=True):
|
||||
fwg_ing_pol_qry = context.session.query(
|
||||
FirewallGroup).filter_by(
|
||||
ingress_firewall_policy_id=fwp_id)
|
||||
ing_fwg_ids = [entry.id for entry in fwg_ing_pol_qry]
|
||||
fwg_eg_pol_qry = context.session.query(
|
||||
FirewallGroup).filter_by(
|
||||
egress_firewall_policy_id=fwp_id)
|
||||
eg_fwg_ids = [entry.id for entry in fwg_eg_pol_qry]
|
||||
return ing_fwg_ids, eg_fwg_ids
|
||||
|
||||
def _check_fwgs_associated_with_policy_in_same_project(self, context,
|
||||
fwp_id,
|
||||
fwp_tenant_id):
|
||||
filters = {'ingress_firewall_rule_id': [fwp_id],
|
||||
'ingress_firewall_rule_id': [fwp_id]}
|
||||
with context.session.begin(subtransactions=True):
|
||||
fwg_with_fwp_id_db = self._get_collection_query(
|
||||
context,
|
||||
FirewallGroup,
|
||||
filters=filters)
|
||||
for entry in fwg_with_fwp_id_db:
|
||||
if entry.tenant_id != fwp_tenant_id:
|
||||
raise f_exc.FirewallPolicyInUse(
|
||||
firewall_policy_id=fwp_id)
|
||||
|
||||
def _set_rules_for_policy(self, context, firewall_policy_db, fwp):
|
||||
rule_id_list = fwp['firewall_rules']
|
||||
fwp_db = firewall_policy_db
|
||||
with context.session.begin(subtransactions=True):
|
||||
if not rule_id_list:
|
||||
for rule_id in [rule_assoc.firewall_rule_id
|
||||
for rule_assoc in fwp_db['rule_associations']]:
|
||||
fwpra_db = self._get_policy_rule_association(
|
||||
context, fwp_db['id'], rule_id)
|
||||
fwp_db.rule_associations.remove(fwpra_db)
|
||||
context.session.delete(fwpra_db)
|
||||
fwp_db.rule_associations = []
|
||||
return
|
||||
# We will first check if the new list of rules is valid
|
||||
filters = {'firewall_rule_id': [r_id for r_id in rule_id_list]}
|
||||
# Run a validation on the Firewall Rules table
|
||||
self._check_rules_for_policy_is_valid(context, fwp, fwp_db,
|
||||
rule_id_list, filters)
|
||||
# new rules are valid, lets delete the old association
|
||||
self._delete_rules_in_policy(context, fwp_db['id'])
|
||||
# and add in the new association
|
||||
self._set_rules_in_policy_rule_assoc(context, fwp_db, fwp)
|
||||
# we need care about the associations related with this policy
|
||||
# and its rules only.
|
||||
filters['firewall_policy_id'] = [fwp_db['id']]
|
||||
rules_in_fpol_rul_db = self._get_collection_query(
|
||||
context,
|
||||
FirewallPolicyRuleAssociation,
|
||||
filters=filters)
|
||||
rules_dict = dict((fpol_rul_db['firewall_rule_id'], fpol_rul_db)
|
||||
for fpol_rul_db in rules_in_fpol_rul_db)
|
||||
fwp_db.rule_associations = []
|
||||
for fwrule_id in rule_id_list:
|
||||
fwp_db.rule_associations.append(rules_dict[fwrule_id])
|
||||
fwp_db.rule_associations.reorder()
|
||||
|
||||
def create_firewall_policy(self, context, firewall_policy):
|
||||
LOG.debug("create_firewall_policy() called")
|
||||
fwp = firewall_policy['firewall_policy']
|
||||
with context.session.begin(subtransactions=True):
|
||||
fwp_db = FirewallPolicy(
|
||||
id=uuidutils.generate_uuid(),
|
||||
tenant_id=fwp['tenant_id'],
|
||||
name=fwp['name'],
|
||||
description=fwp['description'],
|
||||
audited=fwp['audited'],
|
||||
shared=fwp['shared'])
|
||||
context.session.add(fwp_db)
|
||||
self._set_rules_for_policy(context, fwp_db, fwp)
|
||||
return self._make_firewall_policy_dict(fwp_db)
|
||||
|
||||
def update_firewall_policy(self, context, id, firewall_policy):
|
||||
LOG.debug("update_firewall_policy() called")
|
||||
fwp = firewall_policy['firewall_policy']
|
||||
with context.session.begin(subtransactions=True):
|
||||
fwp_db = self._get_firewall_policy(context, id)
|
||||
if not fwp.get('shared', True):
|
||||
# an update is setting shared to False, make sure associated
|
||||
# firewall groups are in the same project.
|
||||
self._check_fwgs_associated_with_policy_in_same_project(
|
||||
context, id, fwp_db['tenant_id'])
|
||||
if 'shared' in fwp and 'firewall_rules' not in fwp:
|
||||
self._check_if_rules_shared_for_policy_shared(
|
||||
context, fwp_db, fwp)
|
||||
if 'firewall_rules' in fwp:
|
||||
self._set_rules_for_policy(context, fwp_db, fwp)
|
||||
del fwp['firewall_rules']
|
||||
if 'audited' not in fwp:
|
||||
fwp['audited'] = False
|
||||
fwp_db.update(fwp)
|
||||
return self._make_firewall_policy_dict(fwp_db)
|
||||
|
||||
def delete_firewall_policy(self, context, id):
|
||||
LOG.debug("delete_firewall_policy() called")
|
||||
with context.session.begin(subtransactions=True):
|
||||
fwp_db = self._get_firewall_policy(context, id)
|
||||
# check if policy in use
|
||||
qry = context.session.query(FirewallGroup)
|
||||
if qry.filter_by(ingress_firewall_policy_id=id).first():
|
||||
raise f_exc.FirewallPolicyInUse(firewall_policy_id=id)
|
||||
elif qry.filter_by(egress_firewall_policy_id=id).first():
|
||||
raise f_exc.FirewallPolicyInUse(firewall_policy_id=id)
|
||||
else:
|
||||
# Policy is not being used, delete.
|
||||
self._delete_rules_in_policy(context, id)
|
||||
context.session.delete(fwp_db)
|
||||
|
||||
def get_firewall_policy(self, context, id, fields=None):
|
||||
LOG.debug("get_firewall_policy() called")
|
||||
fwp = self._get_firewall_policy(context, id)
|
||||
return self._make_firewall_policy_dict(fwp, fields)
|
||||
|
||||
def get_firewall_policies(self, context, filters=None, fields=None):
|
||||
LOG.debug("get_firewall_policies() called")
|
||||
return self._get_collection(context, FirewallPolicy,
|
||||
self._make_firewall_policy_dict,
|
||||
filters=filters, fields=fields)
|
||||
|
||||
def _validate_fwg_parameters(self, context, fwg, fwg_tenant_id):
|
||||
# On updates, all keys will not be present so check and validate.
|
||||
if 'ingress_firewall_policy_id' in fwg:
|
||||
fwp_id = fwg['ingress_firewall_policy_id']
|
||||
if fwp_id is not None:
|
||||
fwp = self._get_firewall_policy(context, fwp_id)
|
||||
if fwg_tenant_id != fwp['tenant_id'] and not fwp['shared']:
|
||||
raise f_exc.FirewallPolicyConflict(
|
||||
firewall_policy_id=fwp_id)
|
||||
|
||||
if 'egress_firewall_policy_id' in fwg:
|
||||
fwp_id = fwg['egress_firewall_policy_id']
|
||||
if fwp_id is not None:
|
||||
fwp = self._get_firewall_policy(context, fwp_id)
|
||||
if fwg_tenant_id != fwp['tenant_id'] and not fwp['shared']:
|
||||
raise f_exc.FirewallPolicyConflict(
|
||||
firewall_policy_id=fwp_id)
|
||||
return
|
||||
|
||||
def _set_ports_for_firewall_group(self, context, fwg_db, fwg):
|
||||
port_id_list = fwg['ports']
|
||||
if not port_id_list:
|
||||
return
|
||||
with context.session.begin(subtransactions=True):
|
||||
for port_id in port_id_list:
|
||||
fwg_port_db = FirewallGroupPortAssociation(
|
||||
firewall_group_id=fwg_db['id'],
|
||||
port_id=port_id)
|
||||
context.session.add(fwg_port_db)
|
||||
|
||||
def _get_ports_in_firewall_group(self, context, firewall_group_id):
|
||||
"""Get the Ports associated with the firewall group."""
|
||||
with context.session.begin(subtransactions=True):
|
||||
fw_group_port_qry = context.session.query(
|
||||
FirewallGroupPortAssociation)
|
||||
fw_group_port_rows = fw_group_port_qry.filter_by(
|
||||
firewall_group_id=firewall_group_id)
|
||||
fw_ports = [entry.port_id for entry in fw_group_port_rows]
|
||||
return fw_ports
|
||||
|
||||
def _delete_ports_in_firewall_group(self, context, firewall_group_id):
|
||||
"""Delete the Ports associated with the firewall group."""
|
||||
with context.session.begin(subtransactions=True):
|
||||
fw_group_port_qry = context.session.query(
|
||||
FirewallGroupPortAssociation)
|
||||
fw_group_port_qry.filter_by(
|
||||
firewall_group_id=firewall_group_id).delete()
|
||||
return
|
||||
|
||||
def _validate_if_firewall_group_on_ports(
|
||||
self, context, port_ids, fwg_id=None):
|
||||
"""Validate if ports are not associated with any firewall_group.
|
||||
If any of the ports in the list is already associated with
|
||||
a firewall_group, raise an exception else just return.
|
||||
"""
|
||||
fwg_port_qry = context.session.query(
|
||||
FirewallGroupPortAssociation.port_id)
|
||||
fwg_ports = fwg_port_qry.filter(
|
||||
FirewallGroupPortAssociation.port_id.in_(port_ids),
|
||||
FirewallGroupPortAssociation.firewall_group_id != fwg_id).all()
|
||||
if fwg_ports:
|
||||
port_ids = [entry.port_id for entry in fwg_ports]
|
||||
raise f_exc.FirewallGroupPortInUse(port_ids=port_ids)
|
||||
|
||||
def create_firewall_group(self, context, firewall_group, status=None):
|
||||
fwg = firewall_group['firewall_group']
|
||||
if not status:
|
||||
status = (nl_constants.CREATED if cfg.CONF.router_distributed
|
||||
else nl_constants.PENDING_CREATE)
|
||||
with context.session.begin(subtransactions=True):
|
||||
self._validate_fwg_parameters(context, fwg, fwg['tenant_id'])
|
||||
fwg_db = FirewallGroup(id=uuidutils.generate_uuid(),
|
||||
tenant_id=fwg['tenant_id'],
|
||||
name=fwg['name'],
|
||||
description=fwg['description'],
|
||||
status=status,
|
||||
ingress_firewall_policy_id=fwg['ingress_firewall_policy_id'],
|
||||
egress_firewall_policy_id=fwg['egress_firewall_policy_id'],
|
||||
admin_state_up=fwg['admin_state_up'],
|
||||
shared=fwg['shared'])
|
||||
context.session.add(fwg_db)
|
||||
self._set_ports_for_firewall_group(context, fwg_db, fwg)
|
||||
return self._make_firewall_group_dict(fwg_db)
|
||||
|
||||
def update_firewall_group(self, context, id, firewall_group):
|
||||
LOG.debug("update_firewall() called")
|
||||
fwg = firewall_group['firewall_group']
|
||||
with context.session.begin(subtransactions=True):
|
||||
fwg_db = self.get_firewall_group(context, id)
|
||||
self._validate_fwg_parameters(context, fwg, fwg_db['tenant_id'])
|
||||
if 'ports' in fwg:
|
||||
LOG.debug("Ports are updated in Firewall Group")
|
||||
self._delete_ports_in_firewall_group(context, id)
|
||||
self._set_ports_for_firewall_group(context, fwg_db, fwg)
|
||||
del fwg['ports']
|
||||
count = context.session.query(
|
||||
FirewallGroup).filter_by(id=id).update(fwg)
|
||||
if not count:
|
||||
raise f_exc.FirewallGroupNotFound(firewall_id=id)
|
||||
return self.get_firewall_group(context, id)
|
||||
|
||||
def update_firewall_group_status(self, context, id, status, not_in=None):
|
||||
"""Conditionally update firewall_group status.
|
||||
Status transition is performed only if firewall is not in the specified
|
||||
states as defined by 'not_in' list.
|
||||
"""
|
||||
# filter in_ wants iterable objects, None isn't.
|
||||
not_in = not_in or []
|
||||
with context.session.begin(subtransactions=True):
|
||||
return (context.session.query(FirewallGroup).
|
||||
filter(FirewallGroup.id == id).
|
||||
filter(~FirewallGroup.status.in_(not_in)).
|
||||
update({'status': status}, synchronize_session=False))
|
||||
|
||||
def delete_firewall_group(self, context, id):
|
||||
LOG.debug("delete_firewall() called")
|
||||
with context.session.begin(subtransactions=True):
|
||||
# Note: Plugin should ensure that it's okay to delete if the
|
||||
# firewall is active
|
||||
count = context.session.query(
|
||||
FirewallGroup).filter_by(id=id).delete()
|
||||
if not count:
|
||||
raise f_exc.FirewallGroupNotFound(firewall_id=id)
|
||||
|
||||
def get_firewall_group(self, context, id, fields=None):
|
||||
LOG.debug("get_firewall_group() called")
|
||||
fw = self._get_firewall_group(context, id)
|
||||
return self._make_firewall_group_dict(fw, fields)
|
||||
|
||||
def get_firewall_groups(self, context, filters=None, fields=None):
|
||||
LOG.debug("get_firewall_groups() called")
|
||||
return self._get_collection(context, FirewallGroup,
|
||||
self._make_firewall_group_dict,
|
||||
filters=filters, fields=fields)
|
|
@ -1 +0,0 @@
|
|||
Generic single-database configuration.
|
|
@ -1,86 +0,0 @@
|
|||
# Copyright 2014 OpenStack Foundation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from logging import config as logging_config
|
||||
|
||||
from alembic import context
|
||||
from neutron_lib.db import model_base
|
||||
from oslo_config import cfg
|
||||
from oslo_db.sqlalchemy import session
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy import event
|
||||
|
||||
|
||||
MYSQL_ENGINE = None
|
||||
FWAAS_VERSION_TABLE = 'alembic_version_fwaas'
|
||||
config = context.config
|
||||
neutron_config = config.neutron_config
|
||||
logging_config.fileConfig(config.config_file_name)
|
||||
target_metadata = model_base.BASEV2.metadata
|
||||
|
||||
|
||||
def set_mysql_engine():
|
||||
try:
|
||||
mysql_engine = neutron_config.command.mysql_engine
|
||||
except cfg.NoSuchOptError:
|
||||
mysql_engine = None
|
||||
|
||||
global MYSQL_ENGINE
|
||||
MYSQL_ENGINE = (mysql_engine or
|
||||
model_base.BASEV2.__table_args__['mysql_engine'])
|
||||
|
||||
|
||||
def run_migrations_offline():
|
||||
set_mysql_engine()
|
||||
|
||||
kwargs = dict()
|
||||
if neutron_config.database.connection:
|
||||
kwargs['url'] = neutron_config.database.connection
|
||||
else:
|
||||
kwargs['dialect_name'] = neutron_config.database.engine
|
||||
kwargs['version_table'] = FWAAS_VERSION_TABLE
|
||||
context.configure(**kwargs)
|
||||
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
|
||||
|
||||
@event.listens_for(sa.Table, 'after_parent_attach')
|
||||
def set_storage_engine(target, parent):
|
||||
if MYSQL_ENGINE:
|
||||
target.kwargs['mysql_engine'] = MYSQL_ENGINE
|
||||
|
||||
|
||||
def run_migrations_online():
|
||||
set_mysql_engine()
|
||||
engine = session.create_engine(neutron_config.database.connection)
|
||||
|
||||
connection = engine.connect()
|
||||
context.configure(
|
||||
connection=connection,
|
||||
target_metadata=target_metadata,
|
||||
version_table=FWAAS_VERSION_TABLE
|
||||
)
|
||||
try:
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
finally:
|
||||
connection.close()
|
||||
engine.dispose()
|
||||
|
||||
|
||||
if context.is_offline_mode():
|
||||
run_migrations_offline()
|
||||
else:
|
||||
run_migrations_online()
|
|
@ -1,36 +0,0 @@
|
|||
# Copyright ${create_date.year} <PUT YOUR NAME/COMPANY HERE>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
"""${message}
|
||||
|
||||
Revision ID: ${up_revision}
|
||||
Revises: ${down_revision}
|
||||
Create Date: ${create_date}
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = ${repr(up_revision)}
|
||||
down_revision = ${repr(down_revision)}
|
||||
% if branch_labels:
|
||||
branch_labels = ${repr(branch_labels)}
|
||||
%endif
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
${imports if imports else ""}
|
||||
|
||||
def upgrade():
|
||||
${upgrades if upgrades else "pass"}
|
|
@ -1,36 +0,0 @@
|
|||
# Copyright 2015 OpenStack Foundation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
"""add_index_tenant_id
|
||||
|
||||
Revision ID: 4202e3047e47
|
||||
Revises: start_neutron_fwaas
|
||||
Create Date: 2015-02-10 17:17:47.846764
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '4202e3047e47'
|
||||
down_revision = 'start_neutron_fwaas'
|
||||
|
||||
from alembic import op
|
||||
|
||||
TABLES = ['firewall_rules', 'firewalls', 'firewall_policies']
|
||||
|
||||
|
||||
def upgrade():
|
||||
for table in TABLES:
|
||||
op.create_index(op.f('ix_%s_tenant_id' % table),
|
||||
table, ['tenant_id'], unique=False)
|
|
@ -1,62 +0,0 @@
|
|||
# Copyright 2014 OpenStack Foundation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
"""FWaaS router insertion
|
||||
|
||||
Revision ID: 540142f314f4
|
||||
Revises: 4202e3047e47
|
||||
Create Date: 2015-02-06 17:02:24.279337
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '540142f314f4'
|
||||
down_revision = '4202e3047e47'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.engine import reflection
|
||||
|
||||
SQL_STATEMENT = (
|
||||
"insert into firewall_router_associations "
|
||||
"select "
|
||||
"f.id as fw_id, r.id as router_id "
|
||||
"from firewalls f, routers r "
|
||||
"where "
|
||||
"f.tenant_id=r.%s"
|
||||
)
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.create_table('firewall_router_associations',
|
||||
sa.Column('fw_id', sa.String(length=36), nullable=False),
|
||||
sa.Column('router_id', sa.String(length=36), nullable=False),
|
||||
sa.ForeignKeyConstraint(['fw_id'], ['firewalls.id'],
|
||||
ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['router_id'], ['routers.id'],
|
||||
ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('fw_id', 'router_id'),
|
||||
)
|
||||
|
||||
# Depending on when neutron-fwaas is installed with neutron, this script
|
||||
# may be run before or after the neutron core tables have had their
|
||||
# tenant_id columns renamed to project_id. Account for both scenarios.
|
||||
bind = op.get_bind()
|
||||
insp = reflection.Inspector.from_engine(bind)
|
||||
columns = insp.get_columns('routers')
|
||||
if 'tenant_id' in [c['name'] for c in columns]:
|
||||
op.execute(SQL_STATEMENT % 'tenant_id')
|
||||
else:
|
||||
op.execute(SQL_STATEMENT % 'project_id')
|
|
@ -1,46 +0,0 @@
|
|||
# Copyright 2015 OpenStack Foundation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
"""cisco_csr_fwaas
|
||||
|
||||
Revision ID: 796c68dffbb
|
||||
Revises: 540142f314f4
|
||||
Create Date: 2015-02-02 13:11:55.184112
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '796c68dffbb'
|
||||
down_revision = '540142f314f4'
|
||||
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade(active_plugins=None, options=None):
|
||||
|
||||
op.create_table('cisco_firewall_associations',
|
||||
sa.Column('fw_id', sa.String(length=36), nullable=False),
|
||||
sa.Column('port_id', sa.String(length=36), nullable=True),
|
||||
sa.Column('direction', sa.String(length=16), nullable=True),
|
||||
sa.Column('acl_id', sa.String(length=36), nullable=True),
|
||||
sa.Column('router_id', sa.String(length=36), nullable=True),
|
||||
sa.ForeignKeyConstraint(['fw_id'], ['firewalls.id'],
|
||||
ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['port_id'], ['ports.id'],
|
||||
ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('fw_id')
|
||||
)
|
|
@ -1 +0,0 @@
|
|||
fd38cd995cc0
|
|
@ -1 +0,0 @@
|
|||
d6a12e637e28
|
|
@ -1,29 +0,0 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
"""kilo
|
||||
|
||||
Revision ID: kilo
|
||||
Revises: 796c68dffbb
|
||||
Create Date: 2015-04-16 00:00:00.000000
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'kilo'
|
||||
down_revision = '796c68dffbb'
|
||||
|
||||
|
||||
def upgrade():
|
||||
"""A no-op migration for marking the Kilo release."""
|
||||
pass
|
|
@ -1,38 +0,0 @@
|
|||
# Copyright 2015 Red Hat Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
"""Initial Liberty no-op script.
|
||||
|
||||
Revision ID: 67c8e8d61d5
|
||||
Revises: kilo
|
||||
Create Date: 2015-07-28 22:18:13.330846
|
||||
|
||||
"""
|
||||
|
||||
from neutron.db import migration
|
||||
from neutron_lib.db import constants
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '67c8e8d61d5'
|
||||
down_revision = 'kilo'
|
||||
branch_labels = (constants.CONTRACT_BRANCH,)
|
||||
|
||||
# milestone identifier, used by neutron-db-manage
|
||||
neutron_milestone = [migration.LIBERTY]
|
||||
|
||||
|
||||
def upgrade():
|
||||
pass
|
|
@ -1,47 +0,0 @@
|
|||
# Copyright 2015 NEC Corporation. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""add reject rule
|
||||
|
||||
Revision ID: 4b47ea298795
|
||||
Revises: c40fbb377ad
|
||||
Create Date: 2015-04-15 04:19:57.324584
|
||||
|
||||
"""
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from neutron.db import migration
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '4b47ea298795'
|
||||
down_revision = 'c40fbb377ad'
|
||||
|
||||
# milestone identifier, used by neutron-db-manage
|
||||
neutron_milestone = [migration.LIBERTY, migration.MITAKA]
|
||||
|
||||
|
||||
new_action = sa.Enum('allow', 'deny', 'reject', name='firewallrules_action')
|
||||
|
||||
|
||||
def upgrade():
|
||||
# NOTE: postgresql have a builtin ENUM type, so just altering the
|
||||
# column won't works
|
||||
# https://bitbucket.org/zzzeek/alembic/issues/270/altering-enum-type
|
||||
# alter_enum that was already invented for such case in neutron
|
||||
# https://github.com/openstack/neutron/blob/master/neutron/db/migration/__init__.py
|
||||
|
||||
migration.alter_enum(
|
||||
'firewall_rules', 'action', enum_type=new_action, nullable=True)
|
|
@ -1,34 +0,0 @@
|
|||
# Copyright 2015 Red Hat Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
"""Initial Liberty no-op script.
|
||||
|
||||
Revision ID: c40fbb377ad
|
||||
Revises: kilo
|
||||
Create Date: 2015-07-28 22:18:13.321233
|
||||
|
||||
"""
|
||||
|
||||
from neutron_lib.db import constants
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'c40fbb377ad'
|
||||
down_revision = 'kilo'
|
||||
branch_labels = (constants.EXPAND_BRANCH,)
|
||||
|
||||
|
||||
def upgrade():
|
||||
pass
|
|
@ -1,49 +0,0 @@
|
|||
#Copyright 2015 OpenStack Foundation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
"""fw_table_alter script to make <name> column case sensitive
|
||||
|
||||
Revision ID: 458aa42b14b
|
||||
Revises: 67c8e8d61d5
|
||||
Create Date: 2015-09-16 11:47:43.061649
|
||||
|
||||
"""
|
||||
|
||||
from alembic import op
|
||||
|
||||
from neutron.db import migration
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '458aa42b14b'
|
||||
down_revision = '67c8e8d61d5'
|
||||
|
||||
# milestone identifier, used by neutron-db-manage
|
||||
neutron_milestone = [migration.MITAKA]
|
||||
|
||||
|
||||
FW_TAB_NAME = ['firewall_rules', 'firewall_policies', 'firewalls']
|
||||
SQL_STATEMENT_UPDATE_CMD = (
|
||||
"alter table %s "
|
||||
"modify name varchar(255) "
|
||||
"CHARACTER SET utf8 COLLATE utf8_bin"
|
||||
)
|
||||
|
||||
|
||||
def upgrade():
|
||||
context = op.get_context()
|
||||
if context.bind.dialect.name == 'mysql':
|
||||
for table in FW_TAB_NAME:
|
||||
op.execute(SQL_STATEMENT_UPDATE_CMD % table)
|
|
@ -1,143 +0,0 @@
|
|||
# Copyright 2016 <PUT YOUR NAME/COMPANY HERE>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
"""rename tenant to project
|
||||
|
||||
Revision ID: f83a0b2964d0
|
||||
Revises: 458aa42b14b
|
||||
Create Date: 2016-07-14 13:11:53.112622
|
||||
|
||||
"""
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.engine import reflection
|
||||
|
||||
from neutron.db import migration
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'f83a0b2964d0'
|
||||
down_revision = '458aa42b14b'
|
||||
|
||||
# milestone identifier, used by neutron-db-manage
|
||||
neutron_milestone = [migration.NEWTON]
|
||||
|
||||
_INSPECTOR = None
|
||||
|
||||
|
||||
def get_inspector():
|
||||
"""Reuse inspector"""
|
||||
|
||||
global _INSPECTOR
|
||||
|
||||
if _INSPECTOR:
|
||||
return _INSPECTOR
|
||||
|
||||
else:
|
||||
bind = op.get_bind()
|
||||
_INSPECTOR = reflection.Inspector.from_engine(bind)
|
||||
|
||||
return _INSPECTOR
|
||||
|
||||
|
||||
def get_tables():
|
||||
"""
|
||||
Returns hardcoded list of tables which have ``tenant_id`` column.
|
||||
|
||||
The list is hardcoded to match the state of the schema when this
|
||||
upgrade script is run.
|
||||
"""
|
||||
|
||||
tables = [
|
||||
'firewalls',
|
||||
'firewall_policies',
|
||||
'firewall_rules',
|
||||
]
|
||||
|
||||
return tables
|
||||
|
||||
|
||||
def get_columns(table):
|
||||
"""Returns list of columns for given table."""
|
||||
inspector = get_inspector()
|
||||
return inspector.get_columns(table)
|
||||
|
||||
|
||||
def get_data():
|
||||
"""Returns combined list of tuples: [(table, column)].
|
||||
|
||||
The list is built from tables with a tenant_id column.
|
||||
"""
|
||||
|
||||
output = []
|
||||
tables = get_tables()
|
||||
for table in tables:
|
||||
columns = get_columns(table)
|
||||
|
||||
for column in columns:
|
||||
if column['name'] == 'tenant_id':
|
||||
output.append((table, column))
|
||||
|
||||
return output
|
||||
|
||||
|
||||
def alter_column(table, column):
|
||||
old_name = 'tenant_id'
|
||||
new_name = 'project_id'
|
||||
|
||||
op.alter_column(
|
||||
table_name=table,
|
||||
column_name=old_name,
|
||||
new_column_name=new_name,
|
||||
existing_type=column['type'],
|
||||
existing_nullable=column['nullable']
|
||||
)
|
||||
|
||||
|
||||
def recreate_index(index, table_name):
|
||||
old_name = index['name']
|
||||
new_name = old_name.replace('tenant', 'project')
|
||||
|
||||
op.drop_index(op.f(old_name), table_name)
|
||||
op.create_index(new_name, table_name, ['project_id'])
|
||||
|
||||
|
||||
def upgrade():
|
||||
"""Code reused from
|
||||
|
||||
Change-Id: I87a8ef342ccea004731ba0192b23a8e79bc382dc
|
||||
"""
|
||||
|
||||
inspector = get_inspector()
|
||||
|
||||
data = get_data()
|
||||
for table, column in data:
|
||||
alter_column(table, column)
|
||||
|
||||
indexes = inspector.get_indexes(table)
|
||||
for index in indexes:
|
||||
if 'tenant_id' in index['name']:
|
||||
recreate_index(index, table)
|
||||
|
||||
|
||||
def contract_creation_exceptions():
|
||||
"""Special migration for the blueprint to support Keystone V3.
|
||||
We drop all tenant_id columns and create project_id columns instead.
|
||||
"""
|
||||
return {
|
||||
sa.Column: ['.'.join([table, 'project_id']) for table in get_tables()],
|
||||
sa.Index: get_tables()
|
||||
}
|
|
@ -1,113 +0,0 @@
|
|||
# Copyright 2016 OpenStack Foundation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
"""neutron-fwaas v2.0
|
||||
|
||||
Revision ID: d6a12e637e28
|
||||
Revises: 4b47ea298795
|
||||
Create Date: 2016-06-08 19:57:13.848855
|
||||
|
||||
"""
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
from neutron.db import migration
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'd6a12e637e28'
|
||||
down_revision = '4b47ea298795'
|
||||
|
||||
# milestone identifier, used by neutron-db-manage
|
||||
neutron_milestone = [migration.NEWTON]
|
||||
|
||||
|
||||
def get_enum():
|
||||
engine = op.get_bind().engine
|
||||
# In PostgreSQL types created separately, so if type was already created in
|
||||
# 4b47ea298795_add_reject_rule it should be created one time.
|
||||
# Use parameter create_type=False for that.
|
||||
if engine.name == 'postgresql':
|
||||
return postgresql.ENUM('allow', 'deny', 'reject',
|
||||
name='firewallrules_action',
|
||||
create_type=False)
|
||||
else:
|
||||
return sa.Enum('allow', 'deny', 'reject',
|
||||
name='firewallrules_action')
|
||||
|
||||
|
||||
def upgrade():
|
||||
|
||||
op.create_table(
|
||||
'firewall_policies_v2',
|
||||
sa.Column('id', sa.String(length=36), primary_key=True),
|
||||
sa.Column('name', sa.String(length=255)),
|
||||
sa.Column('description', sa.String(length=1024)),
|
||||
sa.Column('project_id', sa.String(length=255), index=True),
|
||||
sa.Column('audited', sa.Boolean),
|
||||
sa.Column('public', sa.Boolean),
|
||||
sa.Column('rule_count', sa.Integer))
|
||||
|
||||
op.create_table(
|
||||
'firewall_rules_v2',
|
||||
sa.Column('id', sa.String(length=36), primary_key=True),
|
||||
sa.Column('name', sa.String(length=255)),
|
||||
sa.Column('description', sa.String(length=1024)),
|
||||
sa.Column('project_id', sa.String(length=255), index=True),
|
||||
sa.Column('protocol', sa.String(length=40)),
|
||||
sa.Column('ip_version', sa.Integer),
|
||||
sa.Column('source_ip_address', sa.String(length=46)),
|
||||
sa.Column('destination_ip_address', sa.String(length=46)),
|
||||
sa.Column('source_port_range_min', sa.Integer),
|
||||
sa.Column('source_port_range_max', sa.Integer),
|
||||
sa.Column('destination_port_range_min', sa.Integer),
|
||||
sa.Column('destination_port_range_max', sa.Integer),
|
||||
sa.Column('action', get_enum()),
|
||||
sa.Column('public', sa.Boolean),
|
||||
sa.Column('enabled', sa.Boolean))
|
||||
|
||||
op.create_table(
|
||||
'firewall_groups_v2',
|
||||
sa.Column('id', sa.String(length=36), primary_key=True),
|
||||
sa.Column('name', sa.String(length=255)),
|
||||
sa.Column('description', sa.String(length=1024)),
|
||||
sa.Column('project_id', sa.String(length=255), index=True),
|
||||
sa.Column('status', sa.String(length=16)),
|
||||
sa.Column('admin_state_up', sa.Boolean),
|
||||
sa.Column('public', sa.Boolean),
|
||||
sa.Column('egress_firewall_policy_id', sa.String(length=36),
|
||||
sa.ForeignKey('firewall_policies_v2.id')),
|
||||
sa.Column('ingress_firewall_policy_id', sa.String(length=36),
|
||||
sa.ForeignKey('firewall_policies_v2.id')))
|
||||
|
||||
op.create_table(
|
||||
'firewall_group_port_associations_v2',
|
||||
sa.Column('firewall_group_id', sa.String(length=36),
|
||||
sa.ForeignKey('firewall_groups_v2.id', ondelete='CASCADE')),
|
||||
sa.Column('port_id', sa.String(length=36),
|
||||
sa.ForeignKey('ports.id', ondelete='CASCADE'))
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
'firewall_policy_rule_associations_v2',
|
||||
sa.Column('firewall_policy_id', sa.String(length=36),
|
||||
sa.ForeignKey('firewall_policies_v2.id', ondelete='CASCADE'),
|
||||
nullable=False, primary_key=True),
|
||||
sa.Column('firewall_rule_id', sa.String(length=36),
|
||||
sa.ForeignKey('firewall_rules_v2.id', ondelete='CASCADE'),
|
||||
nullable=False, primary_key=True),
|
||||
sa.Column('position', sa.Integer))
|
|
@ -1,37 +0,0 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
"""change shared attribute for firewall resource
|
||||
|
||||
Revision ID: fd38cd995cc0
|
||||
Revises: f83a0b2964d0
|
||||
Create Date: 2017-03-31 14:22:21.063392
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'fd38cd995cc0'
|
||||
down_revision = 'f83a0b2964d0'
|
||||
depends_on = ('d6a12e637e28',)
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.alter_column('firewall_rules_v2', 'public', new_column_name='shared',
|
||||
existing_type=sa.Boolean)
|
||||
op.alter_column('firewall_groups_v2', 'public', new_column_name='shared',
|
||||
existing_type=sa.Boolean)
|
||||
op.alter_column('firewall_policies_v2', 'public', new_column_name='shared',
|
||||
existing_type=sa.Boolean)
|
|
@ -1,30 +0,0 @@
|
|||
# Copyright 2014 OpenStack Foundation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
"""start neutron-fwaas chain
|
||||
|
||||
Revision ID: start_neutron_fwaas
|
||||
Revises: None
|
||||
Create Date: 2014-12-09 18:42:08.262632
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'start_neutron_fwaas'
|
||||
down_revision = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
pass
|
|
@ -1,21 +0,0 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from neutron_lib.db import model_base
|
||||
|
||||
from neutron_fwaas.db.firewall import firewall_db # noqa
|
||||
from neutron_fwaas.db.firewall import firewall_router_insertion_db # noqa
|
||||
from neutron_fwaas.db.firewall.v2 import firewall_db_v2 # noqa
|
||||
|
||||
|
||||
def get_metadata():
|
||||
return model_base.BASEV2.metadata
|
|
@ -1,426 +0,0 @@
|
|||
# Copyright 2013 Big Switch Networks, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import abc
|
||||
|
||||
from debtcollector import moves
|
||||
|
||||
from neutron.api.v2 import resource_helper
|
||||
from neutron_lib.api import converters
|
||||
from neutron_lib.api import extensions
|
||||
from neutron_lib.api import validators
|
||||
from neutron_lib import constants
|
||||
from neutron_lib.db import constants as db_const
|
||||
from neutron_lib.exceptions import firewall_v1 as f_exc
|
||||
from neutron_lib.services import base as service_base
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
import six
|
||||
|
||||
from neutron_fwaas._i18n import _
|
||||
from neutron_fwaas.common import fwaas_constants
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
FirewallNotFound = moves.moved_class(
|
||||
f_exc.FirewallNotFound, 'FirewallNotFound', __name__)
|
||||
FirewallInUse = moves.moved_class(
|
||||
f_exc.FirewallInUse, 'FirewallInUse', __name__)
|
||||
FirewallPolicyNotFound = moves.moved_class(
|
||||
f_exc.FirewallPolicyNotFound, 'FirewallPolicyNotFound', __name__)
|
||||
FirewallPolicyInUse = moves.moved_class(
|
||||
f_exc.FirewallPolicyInUse, 'FirewallPolicyInUse', __name__)
|
||||
FirewallPolicyConflict = moves.moved_class(
|
||||
f_exc.FirewallPolicyConflict, 'FirewallPolicyConflict', __name__)
|
||||
FirewallRuleSharingConflict = moves.moved_class(
|
||||
f_exc.FirewallRuleSharingConflict, 'FirewallRuleSharingConflict', __name__)
|
||||
FirewallPolicySharingConflict = moves.moved_class(
|
||||
f_exc.FirewallPolicySharingConflict, 'FirewallPolicySharingConflict',
|
||||
__name__)
|
||||
FirewallRuleNotFound = moves.moved_class(
|
||||
f_exc.FirewallRuleNotFound, 'FirewallRuleNotFound', __name__)
|
||||
FirewallRuleInUse = moves.moved_class(
|
||||
f_exc.FirewallRuleInUse, 'FirewallRuleInUse', __name__)
|
||||
FirewallRuleNotAssociatedWithPolicy = moves.moved_class(
|
||||
f_exc.FirewallRuleNotAssociatedWithPolicy,
|
||||
'FirewallRuleNotAssociatedWithPolicy',
|
||||
__name__)
|
||||
FirewallRuleInvalidProtocol = moves.moved_class(
|
||||
f_exc.FirewallRuleInvalidProtocol, 'FirewallRuleInvalidProtocol',
|
||||
__name__)
|
||||
FirewallRuleInvalidAction = moves.moved_class(
|
||||
f_exc.FirewallRuleInvalidAction, 'FirewallRuleInvalidAction', __name__)
|
||||
FirewallRuleInvalidICMPParameter = moves.moved_class(
|
||||
f_exc.FirewallRuleInvalidICMPParameter,
|
||||
'FirewallRuleInvalidICMPParameter', __name__)
|
||||
FirewallRuleWithPortWithoutProtocolInvalid = moves.moved_class(
|
||||
f_exc.FirewallRuleWithPortWithoutProtocolInvalid,
|
||||
'FirewallRuleWithPortWithoutProtocolInvalid', __name__)
|
||||
FirewallRuleInvalidPortValue = moves.moved_class(
|
||||
f_exc.FirewallRuleInvalidPortValue, 'FirewallRuleInvalidPortValue',
|
||||
__name__)
|
||||
FirewallRuleInfoMissing = moves.moved_class(
|
||||
f_exc.FirewallRuleInfoMissing, 'FirewallRuleInfoMissing', __name__)
|
||||
FirewallIpAddressConflict = moves.moved_class(
|
||||
f_exc.FirewallIpAddressConflict, 'FirewallIpAddressConflict', __name__)
|
||||
FirewallInternalDriverError = moves.moved_class(
|
||||
f_exc.FirewallInternalDriverError, 'FirewallInternalDriverError', __name__)
|
||||
FirewallRuleConflict = moves.moved_class(
|
||||
f_exc.FirewallRuleConflict, 'FirewallRuleConflict', __name__)
|
||||
|
||||
# Firewall rule action
|
||||
FWAAS_ALLOW = "allow"
|
||||
FWAAS_DENY = "deny"
|
||||
FWAAS_REJECT = "reject"
|
||||
|
||||
# Firewall resource path prefix
|
||||
FIREWALL_PREFIX = "/fw"
|
||||
|
||||
|
||||
fw_valid_protocol_values = [None, constants.PROTO_NAME_TCP,
|
||||
constants.PROTO_NAME_UDP,
|
||||
constants.PROTO_NAME_ICMP]
|
||||
fw_valid_action_values = [FWAAS_ALLOW, FWAAS_DENY, FWAAS_REJECT]
|
||||
|
||||
|
||||
def convert_protocol(value):
|
||||
if value is None:
|
||||
return
|
||||
if (isinstance(value, six.integer_types) or
|
||||
(isinstance(value, six.string_types) and value.isdigit())):
|
||||
val = int(value)
|
||||
if 0 <= val <= 255:
|
||||
return val
|
||||
else:
|
||||
raise f_exc.FirewallRuleInvalidProtocol(
|
||||
protocol=value, values=fw_valid_protocol_values)
|
||||
elif isinstance(value, six.string_types):
|
||||
if value.lower() in fw_valid_protocol_values:
|
||||
return value.lower()
|
||||
raise f_exc.FirewallRuleInvalidProtocol(
|
||||
protocol=value, values=fw_valid_protocol_values)
|
||||
|
||||
|
||||
def convert_action_to_case_insensitive(value):
|
||||
if value is None:
|
||||
return
|
||||
else:
|
||||
return value.lower()
|
||||
|
||||
|
||||
def convert_port_to_string(value):
|
||||
if value is None:
|
||||
return
|
||||
else:
|
||||
return str(value)
|
||||
|
||||
|
||||
def _validate_port_range(data, key_specs=None):
|
||||
if data is None:
|
||||
return
|
||||
data = str(data)
|
||||
ports = data.split(':')
|
||||
for p in ports:
|
||||
try:
|
||||
val = int(p)
|
||||
except (ValueError, TypeError):
|
||||
msg = _("Port '%s' is not a valid number") % p
|
||||
LOG.debug(msg)
|
||||
return msg
|
||||
if val <= 0 or val > 65535:
|
||||
msg = _("Invalid port '%s'") % p
|
||||
LOG.debug(msg)
|
||||
return msg
|
||||
|
||||
|
||||
def _validate_ip_or_subnet_or_none(data, valid_values=None):
|
||||
if data is None:
|
||||
return None
|
||||
msg_ip = validators.validate_ip_address(data, valid_values)
|
||||
if not msg_ip:
|
||||
return
|
||||
msg_subnet = validators.validate_subnet(data, valid_values)
|
||||
if not msg_subnet:
|
||||
return
|
||||
return _("%(msg_ip)s and %(msg_subnet)s") % {'msg_ip': msg_ip,
|
||||
'msg_subnet': msg_subnet}
|
||||
|
||||
|
||||
validators.validators['type:port_range'] = _validate_port_range
|
||||
validators.validators['type:ip_or_subnet_or_none'] = \
|
||||
_validate_ip_or_subnet_or_none
|
||||
|
||||
|
||||
RESOURCE_ATTRIBUTE_MAP = {
|
||||
'firewall_rules': {
|
||||
'id': {'allow_post': False, 'allow_put': False,
|
||||
'validate': {'type:uuid': None},
|
||||
'is_visible': True, 'primary_key': True},
|
||||
'tenant_id': {'allow_post': True, 'allow_put': False,
|
||||
'required_by_policy': True,
|
||||
'is_visible': True},
|
||||
'name': {'allow_post': True, 'allow_put': True,
|
||||
'validate': {'type:string': db_const.NAME_FIELD_SIZE},
|
||||
'is_visible': True, 'default': ''},
|
||||
'description': {'allow_post': True, 'allow_put': True,
|
||||
'validate': {'type:string':
|
||||
db_const.DESCRIPTION_FIELD_SIZE},
|
||||
'is_visible': True, 'default': ''},
|
||||
'firewall_policy_id': {'allow_post': False, 'allow_put': False,
|
||||
'validate': {'type:uuid_or_none': None},
|
||||
'is_visible': True},
|
||||
'shared': {'allow_post': True, 'allow_put': True,
|
||||
'default': False,
|
||||
'convert_to': converters.convert_to_boolean,
|
||||
'is_visible': True, 'required_by_policy': True,
|
||||
'enforce_policy': True},
|
||||
'protocol': {'allow_post': True, 'allow_put': True,
|
||||
'is_visible': True, 'default': None,
|
||||
'convert_to': convert_protocol,
|
||||
'validate': {'type:values': fw_valid_protocol_values}},
|
||||
'ip_version': {'allow_post': True, 'allow_put': True,
|
||||
'default': 4, 'convert_to': converters.convert_to_int,
|
||||
'validate': {'type:values': [4, 6]},
|
||||
'is_visible': True},
|
||||
'source_ip_address': {'allow_post': True, 'allow_put': True,
|
||||
'validate': {'type:ip_or_subnet_or_none': None},
|
||||
'is_visible': True, 'default': None},
|
||||
'destination_ip_address': {'allow_post': True, 'allow_put': True,
|
||||
'validate': {'type:ip_or_subnet_or_none':
|
||||
None},
|
||||
'is_visible': True, 'default': None},
|
||||
'source_port': {'allow_post': True, 'allow_put': True,
|
||||
'validate': {'type:port_range': None},
|
||||
'convert_to': convert_port_to_string,
|
||||
'default': None, 'is_visible': True},
|
||||
'destination_port': {'allow_post': True, 'allow_put': True,
|
||||
'validate': {'type:port_range': None},
|
||||
'convert_to': convert_port_to_string,
|
||||
'default': None, 'is_visible': True},
|
||||
'position': {'allow_post': False, 'allow_put': False,
|
||||
'default': None, 'is_visible': True},
|
||||
'action': {'allow_post': True, 'allow_put': True,
|
||||
'convert_to': convert_action_to_case_insensitive,
|
||||
'validate': {'type:values': fw_valid_action_values},
|
||||
'is_visible': True, 'default': 'deny'},
|
||||
'enabled': {'allow_post': True, 'allow_put': True,
|
||||
'default': True, 'is_visible': True,
|
||||
'convert_to': converters.convert_to_boolean},
|
||||
},
|
||||
'firewall_policies': {
|
||||
'id': {'allow_post': False, 'allow_put': False,
|
||||
'validate': {'type:uuid': None},
|
||||
'is_visible': True,
|
||||
'primary_key': True},
|
||||
'tenant_id': {'allow_post': True, 'allow_put': False,
|
||||
'required_by_policy': True,
|
||||
'is_visible': True},
|
||||
'name': {'allow_post': True, 'allow_put': True,
|
||||
'validate': {'type:string': db_const.NAME_FIELD_SIZE},
|
||||
'is_visible': True, 'default': ''},
|
||||
'description': {'allow_post': True, 'allow_put': True,
|
||||
'validate': {'type:string':
|
||||
db_const.DESCRIPTION_FIELD_SIZE},
|
||||
'is_visible': True, 'default': ''},
|
||||
'shared': {'allow_post': True, 'allow_put': True,
|
||||
'default': False, 'enforce_policy': True,
|
||||
'convert_to': converters.convert_to_boolean,
|
||||
'is_visible': True, 'required_by_policy': True},
|
||||
'firewall_rules': {'allow_post': True, 'allow_put': True,
|
||||
'validate': {'type:uuid_list': None},
|
||||
'convert_to': converters.convert_none_to_empty_list,
|
||||
'default': None, 'is_visible': True},
|
||||
'audited': {'allow_post': True, 'allow_put': True,
|
||||
'default': False, 'is_visible': True,
|
||||
'convert_to': converters.convert_to_boolean},
|
||||
},
|
||||
'firewalls': {
|
||||
'id': {'allow_post': False, 'allow_put': False,
|
||||
'validate': {'type:uuid': None},
|
||||
'is_visible': True,
|
||||
'primary_key': True},
|
||||
'tenant_id': {'allow_post': True, 'allow_put': False,
|
||||
'required_by_policy': True,
|
||||
'is_visible': True},
|
||||
'name': {'allow_post': True, 'allow_put': True,
|
||||
'validate': {'type:string': db_const.NAME_FIELD_SIZE},
|
||||
'is_visible': True, 'default': ''},
|
||||
'description': {'allow_post': True, 'allow_put': True,
|
||||
'validate': {'type:string':
|
||||
db_const.DESCRIPTION_FIELD_SIZE},
|
||||
'is_visible': True, 'default': ''},
|
||||
'admin_state_up': {'allow_post': True, 'allow_put': True,
|
||||
'default': True, 'is_visible': True,
|
||||
'convert_to': converters.convert_to_boolean},
|
||||
'status': {'allow_post': False, 'allow_put': False,
|
||||
'is_visible': True},
|
||||
'shared': {'allow_post': True, 'allow_put': True,
|
||||
'default': False, 'enforce_policy': True,
|
||||
'convert_to': converters.convert_to_boolean,
|
||||
'is_visible': False, 'required_by_policy': True},
|
||||
'firewall_policy_id': {'allow_post': True, 'allow_put': True,
|
||||
'validate': {'type:uuid_or_none': None},
|
||||
'is_visible': True},
|
||||
},
|
||||
}
|
||||
|
||||
# A tenant may have a unique firewall and policy for each router
|
||||
# when router insertion is used.
|
||||
# Set default quotas to align with default l3 quota_router of 10
|
||||
# though keep as separately controllable.
|
||||
|
||||
firewall_quota_opts = [
|
||||
cfg.IntOpt('quota_firewall',
|
||||
default=10,
|
||||
help=_('Number of firewalls allowed per tenant. '
|
||||
'A negative value means unlimited.')),
|
||||
cfg.IntOpt('quota_firewall_policy',
|
||||
default=10,
|
||||
help=_('Number of firewall policies allowed per tenant. '
|
||||
'A negative value means unlimited.')),
|
||||
cfg.IntOpt('quota_firewall_rule',
|
||||
default=100,
|
||||
help=_('Number of firewall rules allowed per tenant. '
|
||||
'A negative value means unlimited.')),
|
||||
]
|
||||
cfg.CONF.register_opts(firewall_quota_opts, 'QUOTAS')
|
||||
|
||||
|
||||
class Firewall(extensions.ExtensionDescriptor):
|
||||
|
||||
@classmethod
|
||||
def get_name(cls):
|
||||
return "Firewall service"
|
||||
|
||||
@classmethod
|
||||
def get_alias(cls):
|
||||
return "fwaas"
|
||||
|
||||
@classmethod
|
||||
def get_description(cls):
|
||||
return "Extension for Firewall service"
|
||||
|
||||
@classmethod
|
||||
def get_updated(cls):
|
||||
return "2013-02-25T10:00:00-00:00"
|
||||
|
||||
@classmethod
|
||||
def get_resources(cls):
|
||||
special_mappings = {'firewall_policies': 'firewall_policy'}
|
||||
plural_mappings = resource_helper.build_plural_mappings(
|
||||
special_mappings, RESOURCE_ATTRIBUTE_MAP)
|
||||
action_map = {'firewall_policy': {'insert_rule': 'PUT',
|
||||
'remove_rule': 'PUT'}}
|
||||
return resource_helper.build_resource_info(plural_mappings,
|
||||
RESOURCE_ATTRIBUTE_MAP,
|
||||
fwaas_constants.FIREWALL,
|
||||
action_map=action_map,
|
||||
register_quota=True)
|
||||
|
||||
@classmethod
|
||||
def get_plugin_interface(cls):
|
||||
return FirewallPluginBase
|
||||
|
||||
def update_attributes_map(self, attributes):
|
||||
super(Firewall, self).update_attributes_map(
|
||||
attributes, extension_attrs_map=RESOURCE_ATTRIBUTE_MAP)
|
||||
|
||||
def get_extended_resources(self, version):
|
||||
if version == "2.0":
|
||||
return RESOURCE_ATTRIBUTE_MAP
|
||||
else:
|
||||
return {}
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class FirewallPluginBase(service_base.ServicePluginBase):
|
||||
|
||||
def get_plugin_name(self):
|
||||
return fwaas_constants.FIREWALL
|
||||
|
||||
def get_plugin_type(self):
|
||||
return fwaas_constants.FIREWALL
|
||||
|
||||
def get_plugin_description(self):
|
||||
return 'Firewall service plugin'
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_firewalls(self, context, filters=None, fields=None):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_firewall(self, context, id, fields=None):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_firewall(self, context, firewall):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def update_firewall(self, context, id, firewall):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_firewall(self, context, id):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_firewall_rules(self, context, filters=None, fields=None):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_firewall_rule(self, context, id, fields=None):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_firewall_rule(self, context, firewall_rule):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def update_firewall_rule(self, context, id, firewall_rule):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_firewall_rule(self, context, id):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_firewall_policy(self, context, id, fields=None):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_firewall_policies(self, context, filters=None, fields=None):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_firewall_policy(self, context, firewall_policy):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def update_firewall_policy(self, context, id, firewall_policy):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_firewall_policy(self, context, id):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def insert_rule(self, context, id, rule_info):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def remove_rule(self, context, id, rule_info):
|
||||
pass
|
|
@ -1,352 +0,0 @@
|
|||
# Copyright (c) 2016 Mirantis, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import abc
|
||||
|
||||
from debtcollector import moves
|
||||
|
||||
from neutron.api.v2 import resource_helper
|
||||
from neutron_lib.api import converters
|
||||
from neutron_lib.api import extensions
|
||||
from neutron_lib.db import constants as nl_db_constants
|
||||
from neutron_lib.exceptions import firewall_v2 as f_exc
|
||||
from neutron_lib.services import base as service_base
|
||||
import six
|
||||
|
||||
# Import firewall v1 API to get the validators
|
||||
# TODO(shpadubi): pull the validators out of fwaas v1 into a separate file
|
||||
from neutron_fwaas.extensions import firewall as fwaas_v1
|
||||
|
||||
FIREWALL_PREFIX = '/fwaas'
|
||||
|
||||
FIREWALL_CONST = 'FIREWALL_V2'
|
||||
|
||||
FirewallGroupNotFound = moves.moved_class(
|
||||
f_exc.FirewallGroupNotFound, 'FirewallGroupNotFound', __name__)
|
||||
FirewallGroupInUse = moves.moved_class(
|
||||
f_exc.FirewallGroupInUse, 'FirewallGroupInUse', __name__)
|
||||
FirewallGroupInPendingState = moves.moved_class(
|
||||
f_exc.FirewallGroupInPendingState, 'FirewallGroupInPendingState', __name__)
|
||||
FirewallGroupPortInvalid = moves.moved_class(
|
||||
f_exc.FirewallGroupPortInvalid, 'FirewallGroupPortInvalid', __name__)
|
||||
FirewallGroupPortInvalidProject = moves.moved_class(
|
||||
f_exc.FirewallGroupPortInvalidProject, 'FirewallGroupPortInvalidProject',
|
||||
__name__)
|
||||
FirewallGroupPortInUse = moves.moved_class(
|
||||
f_exc.FirewallGroupPortInUse, 'FirewallGroupPortInUse', __name__)
|
||||
FirewallPolicyNotFound = moves.moved_class(
|
||||
f_exc.FirewallPolicyNotFound, 'FirewallPolicyNotFound', __name__)
|
||||
FirewallPolicyInUse = moves.moved_class(
|
||||
f_exc.FirewallPolicyInUse, 'FirewallPolicyInUse', __name__)
|
||||
FirewallPolicyConflict = moves.moved_class(
|
||||
f_exc.FirewallPolicyConflict, 'FirewallPolicyConflict', __name__)
|
||||
FirewallRuleSharingConflict = moves.moved_class(
|
||||
f_exc.FirewallRuleSharingConflict, 'FirewallRuleSharingConflict',
|
||||
__name__)
|
||||
FirewallPolicySharingConflict = moves.moved_class(
|
||||
f_exc.FirewallPolicySharingConflict, 'FirewallPolicySharingConflict',
|
||||
__name__)
|
||||
FirewallRuleNotFound = moves.moved_class(
|
||||
f_exc.FirewallRuleNotFound, 'FirewallRuleNotFound', __name__)
|
||||
FirewallRuleInUse = moves.moved_class(
|
||||
f_exc.FirewallRuleInUse, 'FirewallRuleInUse', __name__)
|
||||
FirewallRuleNotAssociatedWithPolicy = moves.moved_class(
|
||||
f_exc.FirewallRuleNotAssociatedWithPolicy,
|
||||
'FirewallRuleNotAssociatedWithPolicy',
|
||||
__name__)
|
||||
FirewallRuleInvalidProtocol = moves.moved_class(
|
||||
f_exc.FirewallRuleInvalidProtocol, 'FirewallRuleInvalidProtocol',
|
||||
__name__)
|
||||
FirewallRuleInvalidAction = moves.moved_class(
|
||||
f_exc.FirewallRuleInvalidAction, 'FirewallRuleInvalidAction',
|
||||
__name__)
|
||||
FirewallRuleInvalidICMPParameter = moves.moved_class(
|
||||
f_exc.FirewallRuleInvalidICMPParameter,
|
||||
'FirewallRuleInvalidICMPParameter', __name__)
|
||||
FirewallRuleWithPortWithoutProtocolInvalid = moves.moved_class(
|
||||
f_exc.FirewallRuleWithPortWithoutProtocolInvalid,
|
||||
'FirewallRuleWithPortWithoutProtocolInvalid', __name__)
|
||||
FirewallRuleInvalidPortValue = moves.moved_class(
|
||||
f_exc.FirewallRuleInvalidPortValue, 'FirewallRuleInvalidPortValue',
|
||||
__name__)
|
||||
FirewallRuleInfoMissing = moves.moved_class(
|
||||
f_exc.FirewallRuleInfoMissing, 'FirewallRuleInfoMissing', __name__)
|
||||
FirewallIpAddressConflict = moves.moved_class(
|
||||
f_exc.FirewallIpAddressConflict, 'FirewallIpAddressConflict', __name__)
|
||||
FirewallInternalDriverError = moves.moved_class(
|
||||
f_exc.FirewallInternalDriverError, 'FirewallInternalDriverError', __name__)
|
||||
FirewallRuleConflict = moves.moved_class(
|
||||
f_exc.FirewallRuleConflict, 'FirewallRuleConflict', __name__)
|
||||
FirewallRuleAlreadyAssociated = moves.moved_class(
|
||||
f_exc.FirewallRuleAlreadyAssociated, 'FirewallRuleAlreadyAssociated',
|
||||
__name__)
|
||||
|
||||
|
||||
RESOURCE_ATTRIBUTE_MAP = {
|
||||
'firewall_rules': {
|
||||
'id': {'allow_post': False, 'allow_put': False,
|
||||
'validate': {'type:uuid': None},
|
||||
'is_visible': True, 'primary_key': True},
|
||||
'tenant_id': {'allow_post': True, 'allow_put': False,
|
||||
'required_by_policy': True,
|
||||
'validate': {'type:string':
|
||||
nl_db_constants.UUID_FIELD_SIZE},
|
||||
'is_visible': True},
|
||||
'name': {'allow_post': True, 'allow_put': True,
|
||||
'validate': {'type:string': nl_db_constants.NAME_FIELD_SIZE},
|
||||
'is_visible': True, 'default': ''},
|
||||
'description': {'allow_post': True, 'allow_put': True,
|
||||
'validate': {'type:string':
|
||||
nl_db_constants.DESCRIPTION_FIELD_SIZE},
|
||||
'is_visible': True, 'default': ''},
|
||||
'firewall_policy_id': {'allow_post': False, 'allow_put': False,
|
||||
'validate': {'type:uuid_or_none': None},
|
||||
'is_visible': True},
|
||||
'shared': {'allow_post': True, 'allow_put': True,
|
||||
'default': False, 'is_visible': True,
|
||||
'convert_to': converters.convert_to_boolean,
|
||||
'required_by_policy': True, 'enforce_policy': True},
|
||||
'protocol': {'allow_post': True, 'allow_put': True,
|
||||
'is_visible': True, 'default': None,
|
||||
'convert_to': fwaas_v1.convert_protocol,
|
||||
'validate': {'type:values':
|
||||
fwaas_v1.fw_valid_protocol_values}},
|
||||
'ip_version': {'allow_post': True, 'allow_put': True,
|
||||
'default': 4, 'convert_to': converters.convert_to_int,
|
||||
'validate': {'type:values': [4, 6]},
|
||||
'is_visible': True},
|
||||
'source_ip_address': {'allow_post': True, 'allow_put': True,
|
||||
'validate': {'type:ip_or_subnet_or_none': None},
|
||||
'is_visible': True, 'default': None},
|
||||
'destination_ip_address': {'allow_post': True, 'allow_put': True,
|
||||
'validate': {'type:ip_or_subnet_or_none':
|
||||
None},
|
||||
'is_visible': True, 'default': None},
|
||||
'source_port': {'allow_post': True, 'allow_put': True,
|
||||
'validate': {'type:port_range': None},
|
||||
'convert_to': fwaas_v1.convert_port_to_string,
|
||||
'default': None, 'is_visible': True},
|
||||
'destination_port': {'allow_post': True, 'allow_put': True,
|
||||
'validate': {'type:port_range': None},
|
||||
'convert_to': fwaas_v1.convert_port_to_string,
|
||||
'default': None, 'is_visible': True},
|
||||
'position': {'allow_post': False, 'allow_put': False,
|
||||
'default': None, 'is_visible': True},
|
||||
'action': {'allow_post': True, 'allow_put': True,
|
||||
'convert_to': fwaas_v1.convert_action_to_case_insensitive,
|
||||
'validate': {'type:values':
|
||||
fwaas_v1.fw_valid_action_values},
|
||||
'is_visible': True, 'default': 'deny'},
|
||||
'enabled': {'allow_post': True, 'allow_put': True,
|
||||
'convert_to': converters.convert_to_boolean,
|
||||
'default': True, 'is_visible': True},
|
||||
},
|
||||
'firewall_groups': {
|
||||
'id': {'allow_post': False, 'allow_put': False,
|
||||
'validate': {'type:uuid': None},
|
||||
'is_visible': True,
|
||||
'primary_key': True},
|
||||
'name': {'allow_post': True, 'allow_put': True,
|
||||
'validate': {'type:string': nl_db_constants.NAME_FIELD_SIZE},
|
||||
'is_visible': True, 'default': ''},
|
||||
'description': {'allow_post': True, 'allow_put': True,
|
||||
'validate': {'type:string':
|
||||
nl_db_constants.DESCRIPTION_FIELD_SIZE},
|
||||
'is_visible': True, 'default': ''},
|
||||
'admin_state_up': {'allow_post': True, 'allow_put': True,
|
||||
'default': True, 'is_visible': True,
|
||||
'convert_to': converters.convert_to_boolean},
|
||||
'status': {'allow_post': False, 'allow_put': False,
|
||||
'is_visible': True},
|
||||
'shared': {'allow_post': True, 'allow_put': True, 'default': False,
|
||||
'convert_to': converters.convert_to_boolean,
|
||||
'is_visible': True, 'required_by_policy': True,
|
||||
'enforce_policy': True},
|
||||
'ports': {'allow_post': True, 'allow_put': True,
|
||||
'validate': {'type:uuid_list': None},
|
||||
'convert_to': converters.convert_none_to_empty_list,
|
||||
'default': None, 'is_visible': True},
|
||||
'tenant_id': {'allow_post': True, 'allow_put': False,
|
||||
'required_by_policy': True,
|
||||
'validate': {'type:string':
|
||||
nl_db_constants.UUID_FIELD_SIZE},
|
||||
'is_visible': True},
|
||||
'ingress_firewall_policy_id': {'allow_post': True,
|
||||
'allow_put': True,
|
||||
'validate': {'type:uuid_or_none':
|
||||
None},
|
||||
'default': None, 'is_visible': True},
|
||||
'egress_firewall_policy_id': {'allow_post': True,
|
||||
'allow_put': True,
|
||||
'validate': {'type:uuid_or_none':
|
||||
None},
|
||||
'default': None, 'is_visible': True},
|
||||
},
|
||||
'firewall_policies': {
|
||||
'id': {'allow_post': False, 'allow_put': False,
|
||||
'validate': {'type:uuid': None},
|
||||
'is_visible': True,
|
||||
'primary_key': True},
|
||||
'tenant_id': {'allow_post': True, 'allow_put': False,
|
||||
'required_by_policy': True,
|
||||
'validate': {'type:string':
|
||||
nl_db_constants.UUID_FIELD_SIZE},
|
||||
'is_visible': True},
|
||||
'name': {'allow_post': True, 'allow_put': True,
|
||||
'validate': {'type:string': nl_db_constants.NAME_FIELD_SIZE},
|
||||
'is_visible': True, 'default': ''},
|
||||
'description': {'allow_post': True, 'allow_put': True,
|
||||
'validate': {'type:string':
|
||||
nl_db_constants.DESCRIPTION_FIELD_SIZE},
|
||||
'is_visible': True, 'default': ''},
|
||||
'shared': {'allow_post': True, 'allow_put': True, 'default': False,
|
||||
'convert_to': converters.convert_to_boolean,
|
||||
'is_visible': True, 'required_by_policy': True,
|
||||
'enforce_policy': True},
|
||||
'firewall_rules': {'allow_post': True, 'allow_put': True,
|
||||
'validate': {'type:uuid_list': None},
|
||||
'convert_to': converters.convert_none_to_empty_list,
|
||||
'default': None, 'is_visible': True},
|
||||
'audited': {'allow_post': True, 'allow_put': True, 'default': False,
|
||||
'convert_to': converters.convert_to_boolean,
|
||||
'is_visible': True},
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class Firewall_v2(extensions.ExtensionDescriptor):
|
||||
|
||||
@classmethod
|
||||
def get_name(cls):
|
||||
return "Firewall service v2"
|
||||
|
||||
@classmethod
|
||||
def get_alias(cls):
|
||||
return "fwaas_v2"
|
||||
|
||||
@classmethod
|
||||
def get_description(cls):
|
||||
return "Extension for Firewall service v2"
|
||||
|
||||
@classmethod
|
||||
def get_updated(cls):
|
||||
return "2016-08-16T00:00:00-00:00"
|
||||
|
||||
@classmethod
|
||||
def get_resources(cls):
|
||||
special_mappings = {'firewall_policies': 'firewall_policy'}
|
||||
plural_mappings = resource_helper.build_plural_mappings(
|
||||
special_mappings, RESOURCE_ATTRIBUTE_MAP)
|
||||
action_map = {'firewall_policy': {'insert_rule': 'PUT',
|
||||
'remove_rule': 'PUT'}}
|
||||
return resource_helper.build_resource_info(plural_mappings,
|
||||
RESOURCE_ATTRIBUTE_MAP,
|
||||
FIREWALL_CONST,
|
||||
action_map=action_map)
|
||||
|
||||
@classmethod
|
||||
def get_plugin_interface(cls):
|
||||
return Firewallv2PluginBase
|
||||
|
||||
def update_attributes_map(self, attributes):
|
||||
super(Firewall_v2, self).update_attributes_map(
|
||||
attributes, extension_attrs_map=RESOURCE_ATTRIBUTE_MAP)
|
||||
|
||||
def get_extended_resources(self, version):
|
||||
if version == "2.0":
|
||||
return RESOURCE_ATTRIBUTE_MAP
|
||||
else:
|
||||
return {}
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class Firewallv2PluginBase(service_base.ServicePluginBase):
|
||||
|
||||
def get_plugin_name(self):
|
||||
return FIREWALL_CONST
|
||||
|
||||
def get_plugin_type(self):
|
||||
return FIREWALL_CONST
|
||||
|
||||
def get_plugin_description(self):
|
||||
return 'Firewall Service v2 Plugin'
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_firewall_group(self, context, firewall_group):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_firewall_group(self, context, id):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_firewall_group(self, context, id):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_firewall_groups(self, context, filters=None, fields=None):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def update_firewall_group(self, context, id, firewall_group):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_firewall_rules(self, context, filters=None, fields=None):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_firewall_rule(self, context, id, fields=None):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_firewall_rule(self, context, firewall_rule):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def update_firewall_rule(self, context, id, firewall_rule):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_firewall_rule(self, context, id):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_firewall_policy(self, context, id, fields=None):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_firewall_policies(self, context, filters=None, fields=None):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_firewall_policy(self, context, firewall_policy):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def update_firewall_policy(self, context, id, firewall_policy):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_firewall_policy(self, context, id):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def insert_rule(self, context, id, rule_info):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def remove_rule(self, context, id, rule_info):
|
||||
pass
|
|
@ -1,68 +0,0 @@
|
|||
# Copyright 2015 Cisco Systems Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutron_lib.api import extensions
|
||||
from neutron_lib import constants
|
||||
|
||||
|
||||
EXTENDED_ATTRIBUTES_2_0 = {
|
||||
'firewalls': {
|
||||
'router_ids': {'allow_post': True, 'allow_put': True,
|
||||
'validate': {'type:uuid_list': None},
|
||||
'is_visible': True, 'default': constants.ATTR_NOT_SPECIFIED},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Firewallrouterinsertion(extensions.ExtensionDescriptor):
|
||||
"""Extension class supporting Firewall and Router(s) association.
|
||||
|
||||
The extension enables providing an option to specify router-ids of
|
||||
routers where the firewall is to be installed. This is supported in
|
||||
a manner so that the older version of the API continues to be supported.
|
||||
On a CREATE, if the router_ids option is not specified then the firewall
|
||||
is installed on all routers on the tenant. If the router-ids option is
|
||||
provided with a list of routers then the firewall is installed on the
|
||||
specified routers. If the router-ids option is provided with an empty
|
||||
list then the firewall is created but put in an INACTIVE state to reflect
|
||||
that no routers are associated. This firewall can be updated with a list
|
||||
of routers which will then drive the state to ACTIVE after the agent
|
||||
installs and acks back. UPDATE also supports the option in a similar
|
||||
manner. If the router_ids option is not provided, then there is no change
|
||||
to the existing association with the routers. When the router_is option is
|
||||
provided with a list of routers or an empty list - this drives the new
|
||||
set of routers that the firewall is associated with.
|
||||
"""
|
||||
@classmethod
|
||||
def get_name(cls):
|
||||
return "Firewall Router insertion"
|
||||
|
||||
@classmethod
|
||||
def get_alias(cls):
|
||||
return "fwaasrouterinsertion"
|
||||
|
||||
@classmethod
|
||||
def get_description(cls):
|
||||
return "Firewall Router insertion on specified set of routers"
|
||||
|
||||
@classmethod
|
||||
def get_updated(cls):
|
||||
return "2015-01-27T10:00:00-00:00"
|
||||
|
||||
def get_extended_resources(self, version):
|
||||
if version == "2.0":
|
||||
return EXTENDED_ATTRIBUTES_2_0
|
||||
else:
|
||||
return {}
|
|
@ -1,27 +0,0 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import neutron_fwaas.services.firewall.agents.firewall_agent_api
|
||||
|
||||
|
||||
def list_agent_opts():
|
||||
return [
|
||||
('fwaas',
|
||||
neutron_fwaas.services.firewall.agents.firewall_agent_api.FWaaSOpts)
|
||||
]
|
||||
|
||||
|
||||
def list_opts():
|
||||
return [
|
||||
('quotas',
|
||||
neutron_fwaas.extensions.firewall.firewall_quota_opts)
|
||||
]
|
|
@ -1,29 +0,0 @@
|
|||
# Copyright (c) 2017 Thales Services SAS
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo_privsep import capabilities as c
|
||||
from oslo_privsep import priv_context
|
||||
|
||||
# It is expected that most (if not all) neutron-fwaas operations can be
|
||||
# executed with these privileges.
|
||||
default = priv_context.PrivContext(
|
||||
__name__,
|
||||
cfg_section='privsep',
|
||||
pypath=__name__ + '.default',
|
||||
# TODO(gus): CAP_SYS_ADMIN is required (only?) for manipulating
|
||||
# network namespaces. SYS_ADMIN is a lot of scary powers, so
|
||||
# consider breaking this out into a separate minimal context.
|
||||
capabilities=[c.CAP_SYS_ADMIN, c.CAP_NET_ADMIN],
|
||||
)
|
|
@ -1,82 +0,0 @@
|
|||
# Copyright (c) 2017 Fujitsu Limited
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
# Some parts are based on python-conntrack:
|
||||
# Copyright (c) 2009-2011,2015 Andrew Grigorev <andrew@ei-grad.ru>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
|
||||
import socket
|
||||
|
||||
|
||||
CONNTRACK = 0
|
||||
|
||||
NFCT_O_PLAIN = 0
|
||||
|
||||
NFCT_OF_TIME_BIT = 1
|
||||
NFCT_OF_TIME = 1 << NFCT_OF_TIME_BIT
|
||||
|
||||
NFCT_Q_DESTROY = 2
|
||||
NFCT_Q_FLUSH = 4
|
||||
NFCT_Q_DUMP = 5
|
||||
NFCT_T_DESTROY_BIT = 2
|
||||
NFCT_T_DESTROY = 1 << NFCT_T_DESTROY_BIT
|
||||
|
||||
ATTR_IPV4_SRC = 0
|
||||
ATTR_IPV4_DST = 1
|
||||
ATTR_IPV6_SRC = 4
|
||||
ATTR_IPV6_DST = 5
|
||||
ATTR_PORT_SRC = 8
|
||||
ATTR_PORT_DST = 9
|
||||
ATTR_ICMP_TYPE = 12
|
||||
ATTR_ICMP_CODE = 13
|
||||
ATTR_ICMP_ID = 14
|
||||
ATTR_L3PROTO = 15
|
||||
ATTR_L4PROTO = 17
|
||||
|
||||
NFCT_T_NEW_BIT = 0
|
||||
NFCT_T_NEW = 1 << NFCT_T_NEW_BIT
|
||||
NFCT_T_UPDATE_BIT = 1
|
||||
NFCT_T_UPDATE = 1 << NFCT_T_UPDATE_BIT
|
||||
NFCT_T_DESTROY_BIT = 2
|
||||
NFCT_T_DESTROY = 1 << NFCT_T_DESTROY_BIT
|
||||
|
||||
NFCT_T_ALL = NFCT_T_NEW | NFCT_T_UPDATE | NFCT_T_DESTROY
|
||||
|
||||
NFCT_CB_CONTINUE = 1
|
||||
NFCT_CB_FAILURE = -1
|
||||
|
||||
NFNL_SUBSYS_CTNETLINK = 0
|
||||
|
||||
BUFFER = 1024
|
||||
|
||||
IPVERSION_SOCKET = {4: socket.AF_INET, 6: socket.AF_INET6}
|
|
@ -1,268 +0,0 @@
|
|||
# Copyright (c) 2017 Fujitsu Limited
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
# Some parts are based on python-conntrack:
|
||||
# Copyright (c) 2009-2011,2015 Andrew Grigorev <andrew@ei-grad.ru>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
|
||||
import ctypes
|
||||
from ctypes import util
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
from neutron_lib import constants
|
||||
|
||||
from neutron_fwaas._i18n import _, _LW
|
||||
from neutron_fwaas import privileged
|
||||
from neutron_fwaas.privileged import netlink_constants as nl_constants
|
||||
from neutron_fwaas.privileged import utils as fwaas_utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
nfct = ctypes.CDLL(util.find_library('netfilter_conntrack'))
|
||||
libc = ctypes.CDLL(util.find_library('libc.so.6'))
|
||||
|
||||
IP_VERSIONS = [constants.IP_VERSION_4, constants.IP_VERSION_6]
|
||||
DATA_CALLBACK = None
|
||||
|
||||
ATTR_POSITIONS = {
|
||||
'icmp': [('type', 6), ('code', 7), ('src', 4), ('dst', 5), ('id', 8)],
|
||||
'tcp': [('sport', 7), ('dport', 8), ('src', 5), ('dst', 6)],
|
||||
'udp': [('sport', 6), ('dport', 7), ('src', 4), ('dst', 5)]
|
||||
}
|
||||
|
||||
TARGET = {'src': {4: nl_constants.ATTR_IPV4_SRC,
|
||||
6: nl_constants.ATTR_IPV6_SRC},
|
||||
'dst': {4: nl_constants.ATTR_IPV4_DST,
|
||||
6: nl_constants.ATTR_IPV6_DST},
|
||||
'ipversion': {4: nl_constants.ATTR_L3PROTO,
|
||||
6: nl_constants.ATTR_L3PROTO},
|
||||
'protocol': {4: nl_constants.ATTR_L4PROTO,
|
||||
6: nl_constants.ATTR_L4PROTO},
|
||||
'code': {4: nl_constants.ATTR_ICMP_CODE,
|
||||
6: nl_constants.ATTR_ICMP_CODE},
|
||||
'type': {4: nl_constants.ATTR_ICMP_TYPE,
|
||||
6: nl_constants.ATTR_ICMP_TYPE},
|
||||
'id': {4: nl_constants.ATTR_ICMP_ID,
|
||||
6: nl_constants.ATTR_ICMP_ID},
|
||||
'sport': {4: nl_constants.ATTR_PORT_SRC,
|
||||
6: nl_constants.ATTR_PORT_SRC},
|
||||
'dport': {4: nl_constants.ATTR_PORT_DST,
|
||||
6: nl_constants.ATTR_PORT_DST}}
|
||||
|
||||
NFCT_CALLBACK = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int,
|
||||
ctypes.c_void_p, ctypes.c_void_p)
|
||||
|
||||
|
||||
class ConntrackOpenFailedExit(SystemExit):
|
||||
"""Raised if we fail to open a new conntrack or conntrack handler"""
|
||||
|
||||
|
||||
class ConntrackManager(object):
|
||||
def __init__(self, family_socket=None):
|
||||
self.family_socket = family_socket
|
||||
self.set_functions = {
|
||||
'src': {4: nfct.nfct_set_attr_u32,
|
||||
6: nfct.nfct_set_attr_u64},
|
||||
'dst': {4: nfct.nfct_set_attr_u32,
|
||||
6: nfct.nfct_set_attr_u64},
|
||||
'ipversion': {4: nfct.nfct_set_attr_u8,
|
||||
6: nfct.nfct_set_attr_u8},
|
||||
'protocol': {4: nfct.nfct_set_attr_u8,
|
||||
6: nfct.nfct_set_attr_u8},
|
||||
'type': {4: nfct.nfct_set_attr_u8,
|
||||
6: nfct.nfct_set_attr_u8},
|
||||
'code': {4: nfct.nfct_set_attr_u8,
|
||||
6: nfct.nfct_set_attr_u8},
|
||||
'id': {4: nfct.nfct_set_attr_u16,
|
||||
6: nfct.nfct_set_attr_u16},
|
||||
'sport': {4: nfct.nfct_set_attr_u16,
|
||||
6: nfct.nfct_set_attr_u16},
|
||||
'dport': {4: nfct.nfct_set_attr_u16,
|
||||
6: nfct.nfct_set_attr_u16}, }
|
||||
self.converters = {'src': libc.inet_addr,
|
||||
'dst': libc.inet_addr,
|
||||
'ipversion': nl_constants.IPVERSION_SOCKET.get,
|
||||
'protocol': constants.IP_PROTOCOL_MAP.get,
|
||||
'code': int,
|
||||
'type': int,
|
||||
'id': libc.htons,
|
||||
'sport': libc.htons,
|
||||
'dport': libc.htons, }
|
||||
|
||||
def list_entries(self):
|
||||
entries = []
|
||||
raw_entry = ctypes.create_string_buffer(nl_constants.BUFFER)
|
||||
|
||||
@NFCT_CALLBACK
|
||||
def callback(type_, conntrack, data):
|
||||
nfct.nfct_snprintf(raw_entry, nl_constants.BUFFER,
|
||||
conntrack, type_,
|
||||
nl_constants.NFCT_O_PLAIN,
|
||||
nl_constants.NFCT_OF_TIME)
|
||||
entries.append(raw_entry.value)
|
||||
return nl_constants.NFCT_CB_CONTINUE
|
||||
|
||||
self._callback_register(nl_constants.NFCT_T_ALL,
|
||||
callback, DATA_CALLBACK)
|
||||
|
||||
data_ref = self._get_ref(self.family_socket or
|
||||
nl_constants.IPVERSION_SOCKET[4])
|
||||
self._query(nl_constants.NFCT_Q_DUMP, data_ref)
|
||||
return entries
|
||||
|
||||
def delete_entries(self, entries):
|
||||
conntrack = nfct.nfct_new()
|
||||
try:
|
||||
for entry in entries:
|
||||
self._set_attributes(conntrack, entry)
|
||||
self._query(nl_constants.NFCT_Q_DESTROY, conntrack)
|
||||
except Exception as e:
|
||||
msg = _("Failed to delete conntrack entries %s") % e
|
||||
LOG.critical(msg)
|
||||
raise ConntrackOpenFailedExit(msg)
|
||||
finally:
|
||||
nfct.nfct_destroy(conntrack)
|
||||
|
||||
def flush_entries(self):
|
||||
data_ref = self._get_ref(self.family_socket or
|
||||
nl_constants.IPVERSION_SOCKET[4])
|
||||
self._query(nl_constants.NFCT_Q_FLUSH, data_ref)
|
||||
|
||||
def _query(self, query_type, query_data):
|
||||
result = nfct.nfct_query(self.conntrack_handler, query_type,
|
||||
query_data)
|
||||
if result == nl_constants.NFCT_CB_FAILURE:
|
||||
LOG.warning(_LW("Netlink query failed"))
|
||||
|
||||
def _set_attributes(self, conntrack, entry):
|
||||
ipversion = entry.get('ipversion', 4)
|
||||
for attr, value in entry.items():
|
||||
set_function = self.set_functions[attr][ipversion]
|
||||
target = TARGET[attr][ipversion]
|
||||
converter = self.converters[attr]
|
||||
set_function(conntrack, target, converter(value))
|
||||
|
||||
def _callback_register(self, message_type, callback_func, data):
|
||||
nfct.nfct_callback_register(self.conntrack_handler,
|
||||
message_type, callback_func, data)
|
||||
|
||||
def _get_ref(self, data):
|
||||
return ctypes.byref(ctypes.c_int(data))
|
||||
|
||||
def __enter__(self):
|
||||
self.conntrack_handler = nfct.nfct_open(
|
||||
nl_constants.CONNTRACK,
|
||||
nl_constants.NFNL_SUBSYS_CTNETLINK)
|
||||
if not self.conntrack_handler:
|
||||
msg = _("Failed to open new conntrack handler")
|
||||
LOG.critical(msg)
|
||||
raise ConntrackOpenFailedExit(msg)
|
||||
return self
|
||||
|
||||
def __exit__(self, *args):
|
||||
nfct.nfct_close(self.conntrack_handler)
|
||||
|
||||
|
||||
def _parse_entry(entry, ipversion):
|
||||
"""Parse entry from text to Python tuple
|
||||
|
||||
:param entry: conntrack entry in text
|
||||
:param ipversion: ipversion 4 or 6
|
||||
:return: conntrack entry in Python tuple
|
||||
example: (4, 'tcp', '1', '2', '1.1.1.1', '2.2.2.2')
|
||||
The attributes are ordered to be easy to compare with other entries
|
||||
and compare with firewall rule
|
||||
"""
|
||||
protocol = entry[1]
|
||||
parsed_entry = [ipversion, protocol]
|
||||
for attr, position in ATTR_POSITIONS[protocol]:
|
||||
val = entry[position].partition('=')[2]
|
||||
parsed_entry.append(int(val) if attr in ['sport', 'dport', 'type',
|
||||
'code', 'id'] else val)
|
||||
return tuple(parsed_entry)
|
||||
|
||||
|
||||
@privileged.default.entrypoint
|
||||
def flush_entries(namespace=None):
|
||||
"""Delete all conntrack entries
|
||||
|
||||
:param namespace: namespace to delete conntrack entries
|
||||
:return: None
|
||||
"""
|
||||
with fwaas_utils.in_namespace(namespace):
|
||||
for ipversion in IP_VERSIONS:
|
||||
with ConntrackManager(nl_constants.IPVERSION_SOCKET[ipversion]) \
|
||||
as conntrack:
|
||||
conntrack.flush_entries()
|
||||
|
||||
|
||||
@privileged.default.entrypoint
|
||||
def list_entries(namespace=None):
|
||||
"""List and parse all conntrack entries
|
||||
|
||||
:param namespace: namespace to get conntrack entries
|
||||
:return: sorted list of conntrack entries in Python tuple
|
||||
example: [(4, 'icmp', '8', '0', '1.1.1.1', '2.2.2.2', '1234'),
|
||||
(4, 'tcp', '1', '2', '1.1.1.1', '2.2.2.2')]
|
||||
"""
|
||||
parsed_entries = []
|
||||
with fwaas_utils.in_namespace(namespace):
|
||||
for ipversion in IP_VERSIONS:
|
||||
with ConntrackManager(nl_constants.IPVERSION_SOCKET[ipversion]) \
|
||||
as conntrack:
|
||||
raw_entries = conntrack.list_entries()
|
||||
for raw_entry in raw_entries:
|
||||
parsed_entry = _parse_entry(raw_entry.split(), ipversion)
|
||||
parsed_entries.append(parsed_entry)
|
||||
return sorted(parsed_entries)
|
||||
|
||||
|
||||
@privileged.default.entrypoint
|
||||
def delete_entries(entries, namespace=None):
|
||||
"""Delete selected entries
|
||||
|
||||
:param entries: list of parsed (as tuple) entries to delete
|
||||
:param namespace: namespace to delete conntrack entries
|
||||
:return: None
|
||||
"""
|
||||
entry_args = []
|
||||
for entry in entries:
|
||||
entry_arg = {'ipversion': entry[0], 'protocol': entry[1]}
|
||||
for idx, attr in enumerate(ATTR_POSITIONS[entry_arg['protocol']]):
|
||||
entry_arg[attr[0]] = entry[idx + 2]
|
||||
entry_args.append(entry_arg)
|
||||
|
||||
with fwaas_utils.in_namespace(namespace):
|
||||
with ConntrackManager() as conntrack:
|
||||
conntrack.delete_entries(entry_args)
|
|
@ -1,29 +0,0 @@
|
|||
# Copyright (c) 2017 Thales Services SAS
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo_utils import uuidutils
|
||||
from pyroute2 import netns as pynetns
|
||||
|
||||
from neutron_fwaas import privileged
|
||||
|
||||
|
||||
# TODO(cby): move this method in neutron.tests.functional.privileged associated
|
||||
# to a new privsep context.
|
||||
@privileged.default.entrypoint
|
||||
def dummy():
|
||||
"""This method aim is to validate that we can use privsep in functests."""
|
||||
namespace = 'dummy-%s' % uuidutils.generate_uuid()
|
||||
pynetns.create(namespace)
|
||||
pynetns.remove(namespace)
|
|
@ -1,36 +0,0 @@
|
|||
# Copyright (c) 2017 Thales Services SAS
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
from neutron_fwaas import privileged
|
||||
from neutron_fwaas.privileged import utils
|
||||
|
||||
|
||||
def get_my_netns_inode():
|
||||
link = os.readlink(utils.PROCESS_NETNS)
|
||||
|
||||
# NOTE(cby): link respects the format "net:[<inode>]"
|
||||
return int(re.match('net:\[(\d+)\]', link).group(1))
|
||||
|
||||
|
||||
@privileged.default.entrypoint
|
||||
def get_in_namespace_netns_inodes(namespace):
|
||||
before = get_my_netns_inode()
|
||||
with utils.in_namespace(namespace):
|
||||
inside = get_my_netns_inode()
|
||||
after = get_my_netns_inode()
|
||||
return before, inside, after
|
|
@ -1,64 +0,0 @@
|
|||
# Copyright (c) 2017 Thales Services SAS
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import contextlib
|
||||
import os
|
||||
|
||||
from oslo_log import log as logging
|
||||
from pyroute2 import netns as pynetns
|
||||
|
||||
from neutron_fwaas._i18n import _
|
||||
|
||||
|
||||
PROCESS_NETNS = '/proc/self/ns/net'
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BackInNamespaceExit(SystemExit):
|
||||
"""Raised if we fail to moved back process in its original namespace."""
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def in_namespace(namespace):
|
||||
"""Move current process in a specific namespace.
|
||||
|
||||
This contextmanager moves current process in a specific namespace and
|
||||
ensures to move it back in original namespace or kills it if we fail to
|
||||
move back in original namespace.
|
||||
"""
|
||||
if not namespace:
|
||||
yield
|
||||
return
|
||||
|
||||
org_netns_fd = os.open(PROCESS_NETNS, os.O_RDONLY)
|
||||
try:
|
||||
new_netns_fd = pynetns.setns(namespace)
|
||||
try:
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
try:
|
||||
# NOTE(cby): this code is not executed only if we fail to
|
||||
# move in target namespace
|
||||
pynetns.setns(org_netns_fd)
|
||||
except Exception as e:
|
||||
msg = _('Failed to move back in original netns: %s') % e
|
||||
LOG.critical(msg)
|
||||
raise BackInNamespaceExit(msg)
|
||||
finally:
|
||||
os.close(new_netns_fd)
|
||||
finally:
|
||||
os.close(org_netns_fd)
|
|
@ -1,86 +0,0 @@
|
|||
# Copyright (c) 2013 OpenStack Foundation
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutron.common import rpc as n_rpc
|
||||
from oslo_config import cfg
|
||||
import oslo_messaging
|
||||
|
||||
from neutron_fwaas._i18n import _
|
||||
|
||||
|
||||
FWAAS_V1 = "v1"
|
||||
FWAAS_V2 = "v2"
|
||||
|
||||
|
||||
FWaaSOpts = [
|
||||
cfg.StrOpt(
|
||||
'driver',
|
||||
default='',
|
||||
help=_("Name of the FWaaS Driver")),
|
||||
cfg.BoolOpt(
|
||||
'enabled',
|
||||
default=False,
|
||||
help=_("Enable FWaaS")),
|
||||
cfg.StrOpt(
|
||||
'agent_version',
|
||||
default=FWAAS_V1,
|
||||
help=_("Firewall agent class")),
|
||||
cfg.StrOpt(
|
||||
'conntrack_driver',
|
||||
default='conntrack',
|
||||
help=_("Name of the FWaaS Conntrack Driver")),
|
||||
]
|
||||
cfg.CONF.register_opts(FWaaSOpts, 'fwaas')
|
||||
|
||||
|
||||
class FWaaSPluginApiMixin(object):
|
||||
"""Agent side of the FWaaS agent to FWaaS Plugin RPC API."""
|
||||
|
||||
def __init__(self, topic, host):
|
||||
self.host = host
|
||||
target = oslo_messaging.Target(topic=topic, version='1.0')
|
||||
self.client = n_rpc.get_client(target)
|
||||
|
||||
def set_firewall_status(self, context, firewall_id, status):
|
||||
"""Make a RPC to set the status of a firewall."""
|
||||
cctxt = self.client.prepare()
|
||||
return cctxt.call(context, 'set_firewall_status', host=self.host,
|
||||
firewall_id=firewall_id, status=status)
|
||||
|
||||
def firewall_deleted(self, context, firewall_id):
|
||||
"""Make a RPC to indicate that the firewall resources are deleted."""
|
||||
cctxt = self.client.prepare()
|
||||
return cctxt.call(context, 'firewall_deleted', host=self.host,
|
||||
firewall_id=firewall_id)
|
||||
|
||||
|
||||
class FWaaSAgentRpcCallbackMixin(object):
|
||||
"""Mixin for FWaaS agent Implementations."""
|
||||
|
||||
def __init__(self, host):
|
||||
|
||||
super(FWaaSAgentRpcCallbackMixin, self).__init__(host)
|
||||
|
||||
def create_firewall(self, context, firewall, host):
|
||||
"""Handle RPC cast from plugin to create a firewall."""
|
||||
pass
|
||||
|
||||
def update_firewall(self, context, firewall, host):
|
||||
"""Handle RPC cast from plugin to update a firewall."""
|
||||
pass
|
||||
|
||||
def delete_firewall(self, context, firewall, host):
|
||||
"""Handle RPC cast from plugin to delete a firewall."""
|
||||
pass
|
|
@ -1,44 +0,0 @@
|
|||
# Copyright 2014 OpenStack Foundation.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutron.services import provider_configuration as provconf
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import importutils
|
||||
|
||||
from neutron_fwaas._i18n import _
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
FIREWALL_DRIVERS = 'firewall_drivers'
|
||||
|
||||
|
||||
class FirewallService(object):
|
||||
"""Firewall Service observer."""
|
||||
|
||||
def load_device_drivers(self):
|
||||
"""Loads a single device driver for FWaaS."""
|
||||
device_driver = provconf.get_provider_driver_class(
|
||||
cfg.CONF.fwaas.driver, FIREWALL_DRIVERS)
|
||||
try:
|
||||
driver = importutils.import_object(device_driver)
|
||||
LOG.debug('Loaded FWaaS device driver: %s', device_driver)
|
||||
return driver
|
||||
except ImportError:
|
||||
msg = _('Error importing FWaaS device driver: %s')
|
||||
raise ImportError(msg % device_driver)
|
||||
except ValueError:
|
||||
msg = _('Configuration error - no FWaaS device_driver specified')
|
||||
raise ValueError(msg)
|
|
@ -1,440 +0,0 @@
|
|||
# Copyright (c) 2013 OpenStack Foundation.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutron.common import rpc as n_rpc
|
||||
from oslo_config import cfg
|
||||
from oslo_log import helpers as log_helpers
|
||||
from oslo_log import log as logging
|
||||
|
||||
from neutron_fwaas._i18n import _, _LE
|
||||
from neutron_fwaas.common import fwaas_constants
|
||||
from neutron_fwaas.common import resources as f_resources
|
||||
from neutron_fwaas.extensions import firewall as fw_ext
|
||||
from neutron_fwaas.services.firewall.agents import firewall_agent_api as api
|
||||
from neutron_fwaas.services.firewall.agents import firewall_service
|
||||
from neutron_lib.agent import l3_extension
|
||||
from neutron_lib import constants as nl_constants
|
||||
from neutron_lib import context
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
#TODO(njohnston): There needs to be some extrapolation of the common code
|
||||
# between this module and firewall_l3_agent_v2.py.
|
||||
|
||||
|
||||
class FWaaSL3PluginApi(api.FWaaSPluginApiMixin):
|
||||
"""Agent side of the FWaaS agent to FWaaS Plugin RPC API."""
|
||||
def __init__(self, topic, host):
|
||||
super(FWaaSL3PluginApi, self).__init__(topic, host)
|
||||
|
||||
def get_firewalls_for_tenant(self, context, **kwargs):
|
||||
"""Get the Firewalls with rules from the Plugin to send to driver."""
|
||||
LOG.debug("Retrieve Firewall with rules from Plugin")
|
||||
cctxt = self.client.prepare()
|
||||
return cctxt.call(context, 'get_firewalls_for_tenant', host=self.host)
|
||||
|
||||
def get_tenants_with_firewalls(self, context, **kwargs):
|
||||
"""Get all Tenants that have Firewalls configured from plugin."""
|
||||
LOG.debug("Retrieve Tenants with Firewalls configured from Plugin")
|
||||
cctxt = self.client.prepare()
|
||||
return cctxt.call(context,
|
||||
'get_tenants_with_firewalls', host=self.host)
|
||||
|
||||
|
||||
class FWaaSL3AgentExtension(l3_extension.L3AgentExtension):
|
||||
"""FWaaS Agent support to be used by Neutron L3 agent."""
|
||||
|
||||
SUPPORTED_RESOURCE_TYPES = [f_resources.FIREWALL_GROUP,
|
||||
f_resources.FIREWALL_POLICY,
|
||||
f_resources.FIREWALL_RULE]
|
||||
|
||||
def initialize(self, connection, driver_type):
|
||||
self._register_rpc_consumers(connection)
|
||||
|
||||
def consume_api(self, agent_api):
|
||||
LOG.debug("FWaaS consume_api call occurred with %s", agent_api)
|
||||
self.agent_api = agent_api
|
||||
|
||||
def _register_rpc_consumers(self, connection):
|
||||
#TODO(njohnston): Add RPC consumer connection loading here.
|
||||
pass
|
||||
|
||||
def start_rpc_listeners(self, conf):
|
||||
self.endpoints = [self]
|
||||
|
||||
self.conn = n_rpc.create_connection()
|
||||
self.conn.create_consumer(
|
||||
fwaas_constants.FW_AGENT, self.endpoints, fanout=False)
|
||||
return self.conn.consume_in_threads()
|
||||
|
||||
def __init__(self, host, conf):
|
||||
LOG.debug("Initializing firewall agent")
|
||||
self.agent_api = None
|
||||
self.neutron_service_plugins = None
|
||||
self.conf = conf
|
||||
self.fwaas_enabled = cfg.CONF.fwaas.enabled
|
||||
self.start_rpc_listeners(conf)
|
||||
|
||||
# None means l3-agent has no information on the server
|
||||
# configuration due to the lack of RPC support.
|
||||
if self.neutron_service_plugins is not None:
|
||||
fwaas_plugin_configured = (fwaas_constants.FIREWALL
|
||||
in self.neutron_service_plugins)
|
||||
if fwaas_plugin_configured and not self.fwaas_enabled:
|
||||
msg = _("FWaaS plugin is configured in the server side, but "
|
||||
"FWaaS is disabled in L3-agent.")
|
||||
LOG.error(msg)
|
||||
raise SystemExit(1)
|
||||
self.fwaas_enabled = self.fwaas_enabled and fwaas_plugin_configured
|
||||
|
||||
if self.fwaas_enabled:
|
||||
# NOTE: Temp location for creating service and loading driver
|
||||
self.fw_service = firewall_service.FirewallService()
|
||||
self.fwaas_driver = self.fw_service.load_device_drivers()
|
||||
self.services_sync_needed = False
|
||||
# setup RPC to msg fwaas plugin
|
||||
self.fwplugin_rpc = FWaaSL3PluginApi(fwaas_constants.FIREWALL_PLUGIN,
|
||||
host)
|
||||
|
||||
def _has_router_insertion_fields(self, fw):
|
||||
return 'add-router-ids' in fw
|
||||
|
||||
def _get_router_ids_for_fw(self, context, fw, to_delete=False):
|
||||
"""Return the router_ids either from fw dict or tenant routers."""
|
||||
if self._has_router_insertion_fields(fw):
|
||||
# it is a new version of plugin
|
||||
return (fw['del-router-ids'] if to_delete
|
||||
else fw['add-router-ids'])
|
||||
else:
|
||||
return [router['id'] for router in
|
||||
self.agent_api.get_routers_in_project(fw['tenant_id'])]
|
||||
|
||||
def _get_routers_in_project(self, project_id):
|
||||
if self.agent_api is None:
|
||||
LOG.exception(_LE("FWaaS RPC call failed; L3 agent_api failure"))
|
||||
router_info = self.agent_api._router_info
|
||||
if project_id:
|
||||
return [ri for ri in router_info.values()
|
||||
if ri.router['tenant_id'] == project_id]
|
||||
else:
|
||||
return []
|
||||
|
||||
def _get_router_info_list_for_tenant(self, router_ids, tenant_id):
|
||||
"""Returns the list of router info objects on which to apply the fw."""
|
||||
return [ri for ri in self._get_routers_in_project(tenant_id)
|
||||
if ri.router_id in router_ids and
|
||||
self.agent_api.is_router_in_namespace(ri.router_id)]
|
||||
|
||||
def _invoke_driver_for_sync_from_plugin(self, ctx, router_info_list, fw):
|
||||
"""Invoke the delete driver method for status of PENDING_DELETE and
|
||||
update method for all other status to (re)apply on driver which is
|
||||
Idempotent.
|
||||
"""
|
||||
if fw['status'] == nl_constants.PENDING_DELETE:
|
||||
try:
|
||||
self.fwaas_driver.delete_firewall(
|
||||
self.conf.agent_mode,
|
||||
router_info_list,
|
||||
fw)
|
||||
self.fwplugin_rpc.firewall_deleted(
|
||||
ctx,
|
||||
fw['id'])
|
||||
except fw_ext.FirewallInternalDriverError:
|
||||
LOG.error(_LE("Firewall Driver Error on fw state %(fwmsg)s "
|
||||
"for fw: %(fwid)s"),
|
||||
{'fwmsg': fw['status'], 'fwid': fw['id']})
|
||||
self.fwplugin_rpc.set_firewall_status(
|
||||
ctx,
|
||||
fw['id'],
|
||||
nl_constants.ERROR)
|
||||
else:
|
||||
# PENDING_UPDATE, PENDING_CREATE, ...
|
||||
try:
|
||||
self.fwaas_driver.update_firewall(
|
||||
self.conf.agent_mode,
|
||||
router_info_list,
|
||||
fw)
|
||||
if fw['admin_state_up']:
|
||||
status = nl_constants.ACTIVE
|
||||
else:
|
||||
status = nl_constants.DOWN
|
||||
except fw_ext.FirewallInternalDriverError:
|
||||
LOG.error(_LE("Firewall Driver Error on fw state %(fwmsg)s "
|
||||
"for fw: %(fwid)s"),
|
||||
{'fwmsg': fw['status'], 'fwid': fw['id']})
|
||||
status = nl_constants.ERROR
|
||||
|
||||
self.fwplugin_rpc.set_firewall_status(
|
||||
ctx,
|
||||
fw['id'],
|
||||
status)
|
||||
|
||||
def _process_router_add(self, router):
|
||||
"""On router add, get fw with rules from plugin and update driver."""
|
||||
LOG.debug("Process router add, router_id: '%s'", router['id'])
|
||||
router_ids = router['id']
|
||||
router_info_list = self._get_router_info_list_for_tenant(
|
||||
[router_ids],
|
||||
router['tenant_id'])
|
||||
if router_info_list:
|
||||
# Get the firewall with rules
|
||||
# for the tenant the router is on.
|
||||
ctx = context.Context('', router['tenant_id'])
|
||||
fw_list = self.fwplugin_rpc.get_firewalls_for_tenant(ctx)
|
||||
for fw in fw_list:
|
||||
if self._has_router_insertion_fields(fw):
|
||||
# if router extension present apply only if router in fw
|
||||
if (not (router_ids in fw['add-router-ids']) and
|
||||
not (router_ids in fw['del-router-ids'])):
|
||||
continue
|
||||
self._invoke_driver_for_sync_from_plugin(
|
||||
ctx,
|
||||
router_info_list,
|
||||
fw)
|
||||
# router can be present only on one fw
|
||||
return
|
||||
|
||||
def add_router(self, context, new_router):
|
||||
"""On router add, get fw with rules from plugin and update driver.
|
||||
|
||||
Handles agent restart, when a router is added, query the plugin to
|
||||
check if this router is in the router list for any firewall. If so
|
||||
install firewall rules on this router.
|
||||
"""
|
||||
# avoid msg to plugin when fwaas is not configured
|
||||
if not self.fwaas_enabled:
|
||||
return
|
||||
try:
|
||||
self._process_router_add(new_router)
|
||||
except Exception:
|
||||
LOG.exception(
|
||||
_LE("FWaaS RPC info call failed for '%s'."),
|
||||
new_router['id'])
|
||||
self.services_sync_needed = True
|
||||
|
||||
def update_router(self, context, updated_router):
|
||||
"""The update_router method is just a synonym for add_router"""
|
||||
self.add_router(context, updated_router)
|
||||
|
||||
def delete_router(self, context, new_router):
|
||||
"""Handles router deletion. There is basically nothing to do for this
|
||||
in the context of FWaaS with an IPTables driver; the namespace will
|
||||
already have been deleted, taking the IPTables rules with it.
|
||||
"""
|
||||
#TODO(njohnston): When another firewall driver is implemented, look at
|
||||
# expanding this out so that the driver can handle deletion calls.
|
||||
pass
|
||||
|
||||
def process_services_sync(self, ctx):
|
||||
if not self.services_sync_needed:
|
||||
return
|
||||
|
||||
"""On RPC issues sync with plugin and apply the sync data."""
|
||||
# avoid msg to plugin when fwaas is not configured
|
||||
if not self.fwaas_enabled:
|
||||
return
|
||||
try:
|
||||
# get the list of tenants with firewalls configured
|
||||
# from the plugin
|
||||
tenant_ids = self.fwplugin_rpc.get_tenants_with_firewalls(ctx)
|
||||
LOG.debug("Tenants with Firewalls: '%s'", tenant_ids)
|
||||
for tenant_id in tenant_ids:
|
||||
ctx = context.Context('', tenant_id)
|
||||
fw_list = self.fwplugin_rpc.get_firewalls_for_tenant(ctx)
|
||||
for fw in fw_list:
|
||||
if fw['status'] == nl_constants.PENDING_DELETE:
|
||||
self.delete_firewall(ctx, fw, self.host)
|
||||
# no need to apply sync data for ACTIVE fw
|
||||
elif fw['status'] != nl_constants.ACTIVE:
|
||||
self.update_firewall(ctx, fw, self.host)
|
||||
self.services_sync_needed = False
|
||||
except Exception:
|
||||
LOG.exception(_LE("Failed fwaas process services sync"))
|
||||
self.services_sync_needed = True
|
||||
|
||||
@log_helpers.log_method_call
|
||||
def create_firewall(self, context, firewall, host):
|
||||
"""Handle Rpc from plugin to create a firewall."""
|
||||
|
||||
router_ids = self._get_router_ids_for_fw(context, firewall)
|
||||
if not router_ids:
|
||||
return
|
||||
router_info_list = self._get_router_info_list_for_tenant(
|
||||
router_ids,
|
||||
firewall['tenant_id'])
|
||||
LOG.debug("Create: Add firewall on Router List: '%s'",
|
||||
[ri.router['id'] for ri in router_info_list])
|
||||
# call into the driver
|
||||
try:
|
||||
self.fwaas_driver.create_firewall(
|
||||
self.conf.agent_mode,
|
||||
router_info_list,
|
||||
firewall)
|
||||
if firewall['admin_state_up']:
|
||||
status = nl_constants.ACTIVE
|
||||
else:
|
||||
status = nl_constants.DOWN
|
||||
except fw_ext.FirewallInternalDriverError:
|
||||
LOG.error(_LE("Firewall Driver Error for create_firewall "
|
||||
"for firewall: %(fwid)s"),
|
||||
{'fwid': firewall['id']})
|
||||
status = nl_constants.ERROR
|
||||
|
||||
try:
|
||||
# send status back to plugin
|
||||
self.fwplugin_rpc.set_firewall_status(
|
||||
context,
|
||||
firewall['id'],
|
||||
status)
|
||||
except Exception:
|
||||
LOG.exception(
|
||||
_LE("FWaaS RPC failure in create_firewall "
|
||||
"for firewall: %(fwid)s"),
|
||||
{'fwid': firewall['id']})
|
||||
self.services_sync_needed = True
|
||||
|
||||
@log_helpers.log_method_call
|
||||
def update_firewall(self, context, firewall, host):
|
||||
"""Handle Rpc from plugin to update a firewall."""
|
||||
|
||||
status = ""
|
||||
if self._has_router_insertion_fields(firewall):
|
||||
# with the router_ids extension, we may need to delete and add
|
||||
# based on the list of routers. On the older version, we just
|
||||
# update (add) all routers on the tenant - delete not needed.
|
||||
router_ids = self._get_router_ids_for_fw(
|
||||
context, firewall, to_delete=True)
|
||||
if router_ids:
|
||||
router_info_list = self._get_router_info_list_for_tenant(
|
||||
router_ids,
|
||||
firewall['tenant_id'])
|
||||
# remove the firewall from this set of routers
|
||||
# but no ack sent yet, check if we need to add
|
||||
LOG.debug("Update: Delete firewall on Router List: '%s'",
|
||||
[ri.router['id'] for ri in router_info_list])
|
||||
try:
|
||||
self.fwaas_driver.delete_firewall(
|
||||
self.conf.agent_mode,
|
||||
router_info_list,
|
||||
firewall)
|
||||
if firewall['last-router']:
|
||||
status = nl_constants.INACTIVE
|
||||
elif firewall['admin_state_up']:
|
||||
status = nl_constants.ACTIVE
|
||||
else:
|
||||
status = nl_constants.DOWN
|
||||
except fw_ext.FirewallInternalDriverError:
|
||||
LOG.error(_LE("Firewall Driver Error for "
|
||||
"update_firewall for firewall: "
|
||||
"%(fwid)s"),
|
||||
{'fwid': firewall['id']})
|
||||
status = nl_constants.ERROR
|
||||
|
||||
# handle the add router and/or rule, policy, firewall
|
||||
# attribute updates
|
||||
if status not in (nl_constants.ERROR, nl_constants.INACTIVE):
|
||||
router_ids = self._get_router_ids_for_fw(context, firewall)
|
||||
if router_ids or firewall['router_ids']:
|
||||
router_info_list = self._get_router_info_list_for_tenant(
|
||||
router_ids + firewall['router_ids'],
|
||||
firewall['tenant_id'])
|
||||
LOG.debug("Update: Add firewall on Router List: '%s'",
|
||||
[ri.router['id'] for ri in router_info_list])
|
||||
# call into the driver
|
||||
try:
|
||||
self.fwaas_driver.update_firewall(
|
||||
self.conf.agent_mode,
|
||||
router_info_list,
|
||||
firewall)
|
||||
if firewall['admin_state_up']:
|
||||
status = nl_constants.ACTIVE
|
||||
else:
|
||||
status = nl_constants.DOWN
|
||||
except fw_ext.FirewallInternalDriverError:
|
||||
LOG.error(_LE("Firewall Driver Error for "
|
||||
"update_firewall for firewall: "
|
||||
"%(fwid)s"),
|
||||
{'fwid': firewall['id']})
|
||||
status = nl_constants.ERROR
|
||||
else:
|
||||
status = nl_constants.INACTIVE
|
||||
try:
|
||||
# send status back to plugin
|
||||
self.fwplugin_rpc.set_firewall_status(
|
||||
context,
|
||||
firewall['id'],
|
||||
status)
|
||||
except Exception:
|
||||
LOG.exception(
|
||||
_LE("FWaaS RPC failure in update_firewall "
|
||||
"for firewall: %(fwid)s"),
|
||||
{'fwid': firewall['id']})
|
||||
self.services_sync_needed = True
|
||||
|
||||
@log_helpers.log_method_call
|
||||
def delete_firewall(self, context, firewall, host):
|
||||
"""Handle Rpc from plugin to delete a firewall."""
|
||||
|
||||
router_ids = self._get_router_ids_for_fw(
|
||||
context, firewall, to_delete=True)
|
||||
if router_ids:
|
||||
router_info_list = self._get_router_info_list_for_tenant(
|
||||
router_ids,
|
||||
firewall['tenant_id'])
|
||||
LOG.debug(
|
||||
"Delete firewall %(fw)s on routers: '%(routers)s'",
|
||||
{'fw': firewall['id'],
|
||||
'routers': [ri.router['id'] for ri in router_info_list]})
|
||||
# call into the driver
|
||||
try:
|
||||
self.fwaas_driver.delete_firewall(
|
||||
self.conf.agent_mode,
|
||||
router_info_list,
|
||||
firewall)
|
||||
if firewall['admin_state_up']:
|
||||
status = nl_constants.ACTIVE
|
||||
else:
|
||||
status = nl_constants.DOWN
|
||||
except fw_ext.FirewallInternalDriverError:
|
||||
LOG.error(_LE("Firewall Driver Error for delete_firewall "
|
||||
"for firewall: %(fwid)s"),
|
||||
{'fwid': firewall['id']})
|
||||
status = nl_constants.ERROR
|
||||
|
||||
try:
|
||||
# send status back to plugin
|
||||
if status in [nl_constants.ACTIVE, nl_constants.DOWN]:
|
||||
self.fwplugin_rpc.firewall_deleted(context, firewall['id'])
|
||||
else:
|
||||
self.fwplugin_rpc.set_firewall_status(
|
||||
context,
|
||||
firewall['id'],
|
||||
status)
|
||||
except Exception:
|
||||
LOG.exception(
|
||||
_LE("FWaaS RPC failure in delete_firewall "
|
||||
"for firewall: %(fwid)s"),
|
||||
{'fwid': firewall['id']})
|
||||
self.services_sync_needed = True
|
||||
|
||||
|
||||
class L3WithFWaaS(FWaaSL3AgentExtension):
|
||||
|
||||
def __init__(self, conf=None):
|
||||
if conf:
|
||||
self.conf = conf
|
||||
else:
|
||||
self.conf = cfg.CONF
|
||||
super(L3WithFWaaS, self).__init__(host=self.conf.host, conf=self.conf)
|
|
@ -1,512 +0,0 @@
|
|||
# Copyright (c) 2016
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutron.agent.linux import ip_lib
|
||||
from neutron.common import rpc as n_rpc
|
||||
from neutron_lib import constants as nl_constants
|
||||
from neutron_lib import context
|
||||
from oslo_config import cfg
|
||||
from oslo_log import helpers as log_helpers
|
||||
from oslo_log import log as logging
|
||||
|
||||
from neutron_fwaas._i18n import _, _LE
|
||||
from neutron_fwaas.common import fwaas_constants
|
||||
from neutron_fwaas.common import resources as f_resources
|
||||
from neutron_fwaas.extensions import firewall as fw_ext
|
||||
from neutron_fwaas.services.firewall.agents import firewall_agent_api as api
|
||||
from neutron_fwaas.services.firewall.agents import firewall_service
|
||||
from neutron_lib.agent import l3_extension
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FWaaSL3PluginApi(api.FWaaSPluginApiMixin):
|
||||
"""Agent side of the FWaaS agent-to-plugin RPC API."""
|
||||
def __init__(self, topic, host):
|
||||
super(FWaaSL3PluginApi, self).__init__(topic, host)
|
||||
|
||||
def get_firewall_groups_for_project(self, context, **kwargs):
|
||||
"""Fetches a project's firewall groups from the plugin."""
|
||||
LOG.debug("Fetch firewall groups from plugin")
|
||||
cctxt = self.client.prepare()
|
||||
return cctxt.call(context, 'get_firewall_groups_for_project',
|
||||
host=self.host)
|
||||
|
||||
def get_projects_with_firewall_groups(self, context, **kwargs):
|
||||
"""Fetches from the plugin all projects that have firewall groups
|
||||
configured.
|
||||
"""
|
||||
LOG.debug("Fetch from plugin projects that have firewall groups "
|
||||
"configured")
|
||||
cctxt = self.client.prepare()
|
||||
return cctxt.call(context,
|
||||
'get_projects_with_firewall_groups', host=self.host)
|
||||
|
||||
def firewall_group_deleted(self, context, fwg_id, **kwargs):
|
||||
"""Notifies the plugin that a firewall group has been deleted."""
|
||||
LOG.debug("Notify plugin that firewall group has been deleted")
|
||||
cctxt = self.client.prepare()
|
||||
return cctxt.call(context, 'firewall_group_deleted', fwg_id=fwg_id,
|
||||
host=self.host)
|
||||
|
||||
def set_firewall_group_status(self, context, fwg_id, status, **kwargs):
|
||||
"""Sets firewall group's status on the plugin."""
|
||||
LOG.debug("Set firewall groups from plugin")
|
||||
cctxt = self.client.prepare()
|
||||
return cctxt.call(context, 'set_firewall_group_status',
|
||||
fwg_id=fwg_id, status=status, host=self.host)
|
||||
|
||||
|
||||
class FWaaSL3AgentExtension(l3_extension.L3AgentExtension):
|
||||
"""FWaaS agent extension."""
|
||||
|
||||
SUPPORTED_RESOURCE_TYPES = [f_resources.FIREWALL_GROUP,
|
||||
f_resources.FIREWALL_POLICY,
|
||||
f_resources.FIREWALL_RULE]
|
||||
|
||||
def initialize(self, connection, driver_type):
|
||||
self._register_rpc_consumers(connection)
|
||||
|
||||
def consume_api(self, agent_api):
|
||||
LOG.debug("FWaaS consume_api call occurred with %s", agent_api)
|
||||
self.agent_api = agent_api
|
||||
|
||||
def _register_rpc_consumers(self, connection):
|
||||
#TODO(njohnston): Add RPC consumer connection loading here.
|
||||
pass
|
||||
|
||||
def start_rpc_listeners(self, host, conf):
|
||||
self.endpoints = [self]
|
||||
|
||||
self.conn = n_rpc.create_connection()
|
||||
self.conn.create_consumer(
|
||||
fwaas_constants.FW_AGENT, self.endpoints, fanout=False)
|
||||
return self.conn.consume_in_threads()
|
||||
|
||||
def __init__(self, host, conf):
|
||||
LOG.debug("Initializing firewall group agent")
|
||||
self.agent_api = None
|
||||
self.neutron_service_plugins = None
|
||||
self.conf = conf
|
||||
self.fwaas_enabled = cfg.CONF.fwaas.enabled
|
||||
|
||||
self.start_rpc_listeners(host, conf)
|
||||
# None means l3-agent has no information on the server
|
||||
# configuration due to the lack of RPC support.
|
||||
if self.neutron_service_plugins is not None:
|
||||
fwaas_plugin_configured = (fwaas_constants.FIREWALL
|
||||
in self.neutron_service_plugins)
|
||||
if fwaas_plugin_configured and not self.fwaas_enabled:
|
||||
msg = _("FWaaS plugin is configured in the server side, but "
|
||||
"FWaaS is disabled in L3-agent.")
|
||||
LOG.error(msg)
|
||||
raise SystemExit(1)
|
||||
self.fwaas_enabled = self.fwaas_enabled and fwaas_plugin_configured
|
||||
|
||||
if self.fwaas_enabled:
|
||||
# NOTE: Temp location for creating service and loading driver
|
||||
self.fw_service = firewall_service.FirewallService()
|
||||
self.fwaas_driver = self.fw_service.load_device_drivers()
|
||||
|
||||
self.services_sync_needed = False
|
||||
self.fwplugin_rpc = FWaaSL3PluginApi(fwaas_constants.FIREWALL_PLUGIN,
|
||||
host)
|
||||
super(FWaaSL3AgentExtension, self).__init__()
|
||||
|
||||
@property
|
||||
def _local_namespaces(self):
|
||||
root_ip = ip_lib.IPWrapper()
|
||||
local_ns_list = root_ip.get_namespaces()
|
||||
return local_ns_list
|
||||
|
||||
def _has_port_insertion_fields(self, firewall_group):
|
||||
"""The presence of the 'add-port-ids' key in the firewall group dict
|
||||
shows we are using the current version of the plugin. If this key
|
||||
is absent, we are in an upgrade and message is from an older
|
||||
version of the plugin.
|
||||
"""
|
||||
return 'add-port-ids' in firewall_group
|
||||
|
||||
def _get_firewall_group_ports(self, context, firewall_group,
|
||||
to_delete=False, require_new_plugin=False):
|
||||
"""Returns in-namespace ports, either from firewall group dict if newer
|
||||
version of plugin or from project routers otherwise.
|
||||
|
||||
NOTE: Vernacular move from "tenant" to "project" doesn't yet appear
|
||||
as a key in router or firewall group objects.
|
||||
"""
|
||||
fwg_port_ids = []
|
||||
if self._has_port_insertion_fields(firewall_group):
|
||||
if to_delete:
|
||||
fwg_port_ids = firewall_group['del-port-ids']
|
||||
else:
|
||||
fwg_port_ids = firewall_group['add-port-ids']
|
||||
elif not require_new_plugin:
|
||||
routers = self.agent_api.get_routers_in_project(
|
||||
firewall_group['tenant_id'])
|
||||
for router in routers:
|
||||
if router.router['tenant_id'] == firewall_group['tenant_id']:
|
||||
fwg_port_ids.extend([p['id'] for p in
|
||||
router.internal_ports])
|
||||
|
||||
# Return in-namespace port objects.
|
||||
return self._get_in_ns_ports(fwg_port_ids)
|
||||
|
||||
def _get_in_ns_ports(self, port_ids):
|
||||
"""Returns port objects in the local namespace, along with their
|
||||
router_info.
|
||||
"""
|
||||
in_ns_ports = {} # This will be converted to a list later.
|
||||
if port_ids and self.agent_api:
|
||||
for port_id in port_ids:
|
||||
# This fetched router_info is guaranteed to be in_namespace.
|
||||
router_info = self.agent_api.get_router_hosting_port(port_id)
|
||||
if router_info:
|
||||
if router_info in in_ns_ports:
|
||||
in_ns_ports[router_info].append(port_id)
|
||||
else:
|
||||
in_ns_ports[router_info] = [port_id]
|
||||
return list(in_ns_ports.items())
|
||||
|
||||
def _invoke_driver_for_sync_from_plugin(self, ctx, port, firewall_group):
|
||||
"""Calls the FWaaS driver's delete_firewall_group method if firewall
|
||||
group has status of PENDING_DELETE; calls driver's
|
||||
update_firewall_group method for all other statuses. Both of these
|
||||
methods are idempotent.
|
||||
"""
|
||||
port_list = self._get_in_ns_ports([port['id']])
|
||||
if firewall_group['status'] == nl_constants.PENDING_DELETE:
|
||||
try:
|
||||
self.fwaas_driver.delete_firewall_group(
|
||||
self.conf.agent_mode, port_list, firewall_group)
|
||||
self.fwplugin_rpc.firewall_group_deleted(
|
||||
ctx, firewall_group['id'])
|
||||
except fw_ext.FirewallInternalDriverError:
|
||||
msg = _LE("FWaaS driver error on %(status)s "
|
||||
"for firewall group: %(fwg_id)s")
|
||||
LOG.exception(msg, {'status': firewall_group['status'],
|
||||
'fwg_id': firewall_group['id']})
|
||||
self.fwplugin_rpc.set_firewall_group_status(
|
||||
ctx, firewall_group['id'], nl_constants.ERROR)
|
||||
else: # PENDING_UPDATE, PENDING_CREATE, ...
|
||||
|
||||
# Prepare firewall group status to return to plugin; may be
|
||||
# overwritten if call to driver fails.
|
||||
if firewall_group['admin_state_up']:
|
||||
status = nl_constants.ACTIVE
|
||||
else:
|
||||
status = nl_constants.DOWN
|
||||
|
||||
# Call the driver.
|
||||
try:
|
||||
self.fwaas_driver.update_firewall_group(
|
||||
self.conf.agent_mode, port_list, firewall_group)
|
||||
except fw_ext.FirewallInternalDriverError:
|
||||
msg = _LE("FWaaS driver error on %(status)s for firewall "
|
||||
"group: "
|
||||
"%(fwg_id)s")
|
||||
LOG.exception(msg, {'status': firewall_group['status'],
|
||||
'fwg_id': firewall_group['id']})
|
||||
status = nl_constants.ERROR
|
||||
|
||||
# Notify the plugin of firewall group's status.
|
||||
self.fwplugin_rpc.set_firewall_group_status(
|
||||
ctx, firewall_group['id'], status)
|
||||
|
||||
def _process_router_update(self, updated_router):
|
||||
"""If a new or existing router in the local namespace is updated,
|
||||
queries the plugin to get the firewall groups for the project in
|
||||
question and then sees if the router has any ports for any firewall
|
||||
group that is configured for that project. If so, installs firewall
|
||||
group rules on the requested ports on this router.
|
||||
"""
|
||||
LOG.debug("Process router update, router_id: %s tenant: %s.",
|
||||
updated_router['id'], updated_router['tenant_id'])
|
||||
router_id = updated_router['id']
|
||||
if not self.agent_api.is_router_in_namespace(router_id):
|
||||
return
|
||||
|
||||
# Get the firewall groups for the new router's project.
|
||||
# NOTE: Vernacular move from "tenant" to "project" doesn't yet appear
|
||||
# as a key in router or firewall group objects.
|
||||
ctx = context.Context('', updated_router['tenant_id'])
|
||||
fwg_list = self.fwplugin_rpc.get_firewall_groups_for_project(ctx)
|
||||
|
||||
# Apply a firewall group, as requested, to ports on the new router.
|
||||
if nl_constants.INTERFACE_KEY in updated_router:
|
||||
for port in updated_router[nl_constants.INTERFACE_KEY]:
|
||||
for firewall_group in fwg_list:
|
||||
if (self._has_port_insertion_fields(firewall_group) and
|
||||
(port['id'] in firewall_group['add-port-ids'] or
|
||||
port['id'] in firewall_group['del-port-ids'])):
|
||||
self._invoke_driver_for_sync_from_plugin(ctx, port,
|
||||
firewall_group)
|
||||
# A port can have at most one firewall group.
|
||||
break
|
||||
|
||||
def add_router(self, context, new_router):
|
||||
"""Handles agent restart and router add. Fetches firewall groups from
|
||||
plugin and updates driver.
|
||||
"""
|
||||
if not self.fwaas_enabled:
|
||||
return
|
||||
|
||||
try:
|
||||
self._process_router_update(new_router)
|
||||
except Exception:
|
||||
LOG.exception(_LE("FWaaS router add RPC info call failed for %s"),
|
||||
new_router['id'])
|
||||
self.services_sync_needed = True
|
||||
|
||||
def update_router(self, context, updated_router):
|
||||
"""Handles agent restart and router add. Fetches firewall groups from
|
||||
plugin and updates driver.
|
||||
"""
|
||||
if not self.fwaas_enabled:
|
||||
return
|
||||
|
||||
try:
|
||||
self._process_router_update(updated_router)
|
||||
except Exception:
|
||||
#TODO(njohnston): This repr should be replaced.
|
||||
LOG.exception(
|
||||
_LE("FWaaS router update RPC info call failed for %s"),
|
||||
repr(updated_router))
|
||||
self.services_sync_needed = True
|
||||
|
||||
def delete_router(self, context, new_router):
|
||||
"""Handles router deletion. There is basically nothing to do for this
|
||||
in the context of FWaaS with an IPTables driver; the namespace will
|
||||
already have been deleted, taking the IPTables rules with it.
|
||||
"""
|
||||
#TODO(njohnston): When another firewall driver is implemented, look at
|
||||
# expanding this out so that the driver can handle deletion calls.
|
||||
pass
|
||||
|
||||
def process_services_sync(self, ctx):
|
||||
"""Syncs with plugin and applies the sync data.
|
||||
"""
|
||||
|
||||
if not self.services_sync_needed or not self.fwaas_enabled:
|
||||
return
|
||||
|
||||
try:
|
||||
# Fetch from the plugin the list of projects with firewall groups.
|
||||
project_ids = \
|
||||
self.fwplugin_rpc.get_projects_with_firewall_groups(ctx)
|
||||
LOG.debug("Projects with firewall groups: %s",
|
||||
', '.join(project_ids))
|
||||
for project_id in project_ids:
|
||||
ctx = context.Context('', project_id)
|
||||
fwg_list = \
|
||||
self.fwplugin_rpc.get_firewall_groups_for_project(ctx)
|
||||
for firewall_group in fwg_list:
|
||||
if firewall_group['status'] == nl_constants.PENDING_DELETE:
|
||||
self.delete_firewall_group(ctx, firewall_group,
|
||||
self.host)
|
||||
# No need to apply sync data for ACTIVE firewall group.
|
||||
elif firewall_group['status'] != nl_constants.ACTIVE:
|
||||
self.update_firewall_group(ctx, firewall_group,
|
||||
self.host)
|
||||
self.services_sync_needed = False
|
||||
except Exception:
|
||||
LOG.exception(_LE("Failed FWaaS process services sync."))
|
||||
self.services_sync_needed = True
|
||||
|
||||
@log_helpers.log_method_call
|
||||
def create_firewall_group(self, context, firewall_group, host):
|
||||
"""Handles RPC from plugin to create a firewall group.
|
||||
"""
|
||||
|
||||
# Get the in-namespace ports to which to add the firewall group.
|
||||
ports_for_fwg = self._get_firewall_group_ports(context, firewall_group)
|
||||
if not ports_for_fwg:
|
||||
return
|
||||
|
||||
LOG.debug("Create firewall group %(fwg_id)s on ports: %(ports)s",
|
||||
{'fwg_id': firewall_group['id'],
|
||||
'ports': ', '.join([p for ri_ports in ports_for_fwg
|
||||
for p in ri_ports[1]])})
|
||||
|
||||
# Set firewall group status; will be overwritten if call to driver
|
||||
# fails.
|
||||
if firewall_group['admin_state_up']:
|
||||
status = nl_constants.ACTIVE
|
||||
else:
|
||||
status = nl_constants.DOWN
|
||||
|
||||
# Call the driver.
|
||||
try:
|
||||
self.fwaas_driver.create_firewall_group(self.conf.agent_mode,
|
||||
ports_for_fwg,
|
||||
firewall_group)
|
||||
except fw_ext.FirewallInternalDriverError:
|
||||
msg = _LE("FWaaS driver error in create_firewall_group "
|
||||
"for firewall group: %(fwg_id)s")
|
||||
LOG.exception(msg, {'fwg_id': firewall_group['id']})
|
||||
status = nl_constants.ERROR
|
||||
|
||||
# Send firewall group's status to plugin.
|
||||
try:
|
||||
self.fwplugin_rpc.set_firewall_group_status(context,
|
||||
firewall_group['id'], status)
|
||||
except Exception:
|
||||
msg = _LE("FWaaS RPC failure in create_firewall_group "
|
||||
"for firewall group: %(fwg_id)s")
|
||||
LOG.exception(msg, {'fwg_id': firewall_group['id']})
|
||||
self.services_sync_needed = True
|
||||
|
||||
@log_helpers.log_method_call
|
||||
def update_firewall_group(self, context, firewall_group, host):
|
||||
"""Handles RPC from plugin to update a firewall group.
|
||||
"""
|
||||
|
||||
# Initialize firewall group status.
|
||||
status = ""
|
||||
|
||||
# Get the list of in-namespace ports from which to delete the firewall
|
||||
# group.
|
||||
ports_for_fwg = self._get_firewall_group_ports(context, firewall_group,
|
||||
to_delete=True, require_new_plugin=True)
|
||||
|
||||
# Remove firewall group from ports if requested.
|
||||
if ports_for_fwg:
|
||||
fw_ports = [p for ri_ports in ports_for_fwg for p in ri_ports[1]]
|
||||
LOG.debug("Update (delete) firewall group %(fwg_id)s on ports: "
|
||||
"%(ports)s",
|
||||
{'fwg_id': firewall_group['id'],
|
||||
'ports': ', '.join(fw_ports)})
|
||||
|
||||
# Set firewall group's status; will be overwritten if call to
|
||||
# driver fails.
|
||||
|
||||
if firewall_group['admin_state_up']:
|
||||
status = nl_constants.ACTIVE
|
||||
if firewall_group['last-port']:
|
||||
status = nl_constants.INACTIVE
|
||||
else:
|
||||
status = nl_constants.DOWN
|
||||
|
||||
# Call the driver.
|
||||
try:
|
||||
self.fwaas_driver.delete_firewall_group(self.conf.agent_mode,
|
||||
ports_for_fwg,
|
||||
firewall_group)
|
||||
except fw_ext.FirewallInternalDriverError:
|
||||
msg = _LE("FWaaS driver error in update_firewall_group "
|
||||
"(add) for firewall group: %s")
|
||||
LOG.exception(msg, firewall_group['id'])
|
||||
status = nl_constants.ERROR
|
||||
|
||||
# Handle the add router and/or rule, policy, firewall group attribute
|
||||
# updates.
|
||||
if status not in (nl_constants.ERROR, nl_constants.INACTIVE):
|
||||
ports_for_fwg = self._get_firewall_group_ports(context,
|
||||
firewall_group)
|
||||
if ports_for_fwg:
|
||||
fw_ports = [p for ri_ports in ports_for_fwg
|
||||
for p in ri_ports[1]]
|
||||
LOG.debug("Update (create) firewall group %(fwg_id)s on "
|
||||
"ports: %(ports)s",
|
||||
{'fwg_id': firewall_group['id'],
|
||||
'ports': ', '.join(fw_ports)})
|
||||
|
||||
# Set firewall group status, which will be overwritten if call
|
||||
# to driver fails.
|
||||
if firewall_group['admin_state_up']:
|
||||
status = nl_constants.ACTIVE
|
||||
else:
|
||||
status = nl_constants.DOWN
|
||||
|
||||
# Call the driver.
|
||||
try:
|
||||
self.fwaas_driver.update_firewall_group(
|
||||
self.conf.agent_mode, ports_for_fwg,
|
||||
firewall_group)
|
||||
except fw_ext.FirewallInternalDriverError:
|
||||
msg = _LE("FWaaS driver error in update_firewall_group "
|
||||
"for firewall group: %s")
|
||||
LOG.exception(msg, firewall_group['id'])
|
||||
status = nl_constants.ERROR
|
||||
else:
|
||||
status = nl_constants.INACTIVE
|
||||
|
||||
# Return status to plugin.
|
||||
try:
|
||||
self.fwplugin_rpc.set_firewall_group_status(context,
|
||||
firewall_group['id'], status)
|
||||
except Exception:
|
||||
LOG.exception(_LE("FWaaS RPC failure in update_firewall_group "
|
||||
"for firewall group: %(fwg_id)s"),
|
||||
{'fwg_id': firewall_group['id']})
|
||||
self.services_sync_needed = True
|
||||
|
||||
@log_helpers.log_method_call
|
||||
def delete_firewall_group(self, context, firewall_group, host):
|
||||
"""Handles RPC from plugin to delete a firewall group.
|
||||
"""
|
||||
|
||||
ports_for_fwg = self._get_firewall_group_ports(context, firewall_group,
|
||||
to_delete=True)
|
||||
|
||||
if not ports_for_fwg:
|
||||
return
|
||||
|
||||
fw_ports = [p for ri_ports in ports_for_fwg for p in ri_ports[1]]
|
||||
LOG.debug("Delete firewall group %(fwg_id)s on ports: %(ports)s",
|
||||
{'fwg_id': firewall_group['id'],
|
||||
'ports': ', '.join(fw_ports)})
|
||||
|
||||
# Set the firewall group's status to return to plugin; status may be
|
||||
# overwritten if call to driver fails.
|
||||
if firewall_group['admin_state_up']:
|
||||
status = nl_constants.ACTIVE
|
||||
else:
|
||||
status = nl_constants.DOWN
|
||||
try:
|
||||
self.fwaas_driver.delete_firewall_group(self.conf.agent_mode,
|
||||
ports_for_fwg,
|
||||
firewall_group)
|
||||
# Call the driver.
|
||||
except fw_ext.FirewallInternalDriverError:
|
||||
LOG.exception(_LE("FWaaS driver error in delete_firewall_group "
|
||||
"for firewall group: %(fwg_id)s"),
|
||||
{'fwg_id': firewall_group['id']})
|
||||
status = nl_constants.ERROR
|
||||
|
||||
# Notify plugin of deletion or return firewall group's status to
|
||||
# plugin, as appropriate.
|
||||
try:
|
||||
if status in [nl_constants.ACTIVE, nl_constants.DOWN]:
|
||||
self.fwplugin_rpc.firewall_group_deleted(context,
|
||||
firewall_group['id'])
|
||||
else:
|
||||
self.fwplugin_rpc.set_firewall_group_status(context,
|
||||
firewall_group['id'], status)
|
||||
except Exception:
|
||||
LOG.exception(_LE("FWaaS RPC failure in delete_firewall_group "
|
||||
"for firewall group: %(fwg_id)s"),
|
||||
{'fwg_id': firewall_group['id']})
|
||||
self.services_sync_needed = True
|
||||
|
||||
|
||||
class L3WithFWaaS(FWaaSL3AgentExtension):
|
||||
|
||||
def __init__(self, conf=None):
|
||||
if conf:
|
||||
self.conf = conf
|
||||
else:
|
||||
self.conf = cfg.CONF
|
||||
super(L3WithFWaaS, self).__init__(host=self.conf.host, conf=self.conf)
|
|
@ -1,35 +0,0 @@
|
|||
# Copyright (c) 2017 Fujitsu Limited
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import abc
|
||||
|
||||
import six
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class ConntrackDriverBase(object):
|
||||
"""Base Driver for Conntrack"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def initialize(self, *args, **kwargs):
|
||||
"""Initialize the driver"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_entries(self, rules, namespace):
|
||||
"""Delete conntrack entries specified by list of rules"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def flush_entries(self, namespace):
|
||||
"""Delete all conntrack entries within namespace"""
|
|
@ -1,120 +0,0 @@
|
|||
# Copyright 2013 Dell Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import abc
|
||||
|
||||
import six
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class FwaasDriverBase(object):
|
||||
"""Firewall as a Service Driver base class.
|
||||
|
||||
Using FwaasDriver Class, an instance of L3 perimeter Firewall
|
||||
can be created. The firewall co-exists with the L3 agent.
|
||||
|
||||
One instance is created for each tenant. One firewall policy
|
||||
is associated with each tenant (in the Havana release).
|
||||
|
||||
The Firewall can be visualized as having two zones (in Havana
|
||||
release), trusted and untrusted.
|
||||
|
||||
All the 'internal' interfaces of Neutron Router is treated as trusted. The
|
||||
interface connected to 'external network' is treated as untrusted.
|
||||
|
||||
The policy is applied on traffic ingressing/egressing interfaces on
|
||||
the trusted zone. This implies that policy will be applied for traffic
|
||||
passing from
|
||||
|
||||
- trusted to untrusted zones
|
||||
- untrusted to trusted zones
|
||||
- trusted to trusted zones
|
||||
|
||||
Policy WILL NOT be applied for traffic from untrusted to untrusted zones.
|
||||
This is not a problem in Havana release as there is only one interface
|
||||
connected to external network.
|
||||
|
||||
Since the policy is applied on the internal interfaces, the traffic
|
||||
will be not be NATed to floating IP. For incoming traffic, the
|
||||
traffic will get NATed to internal IP address before it hits
|
||||
the firewall rules. So, while writing the rules, care should be
|
||||
taken if using rules based on floating IP.
|
||||
|
||||
The firewall rule addition/deletion/insertion/update are done by the
|
||||
management console. When the policy is sent to the driver, the complete
|
||||
policy is sent and the whole policy has to be applied atomically. The
|
||||
firewall rules will not get updated individually. This is to avoid problems
|
||||
related to out-of-order notifications or inconsistent behaviour by partial
|
||||
application of rules. Argument agent_mode indicates the l3 agent in DVR or
|
||||
DVR_SNAT or LEGACY mode.
|
||||
"""
|
||||
# TODO(Margaret): Remove the first 3 methods and make the second three
|
||||
# @abc.abstractmethod
|
||||
def create_firewall(self, agent_mode, apply_list, firewall):
|
||||
"""Create the Firewall with default (drop all) policy.
|
||||
|
||||
The default policy will be applied on all the interfaces of
|
||||
trusted zone.
|
||||
"""
|
||||
pass
|
||||
|
||||
def delete_firewall(self, agent_mode, apply_list, firewall):
|
||||
"""Delete firewall.
|
||||
|
||||
Removes all policies created by this instance and frees up
|
||||
all the resources.
|
||||
"""
|
||||
pass
|
||||
|
||||
def update_firewall(self, agent_mode, apply_list, firewall):
|
||||
"""Apply the policy on all trusted interfaces.
|
||||
|
||||
Remove previous policy and apply the new policy on all trusted
|
||||
interfaces.
|
||||
"""
|
||||
pass
|
||||
|
||||
def create_firewall_group(self, agent_mode, apply_list, firewall):
|
||||
"""Create the Firewall with default (drop all) policy.
|
||||
|
||||
The default policy will be applied on all the interfaces of
|
||||
trusted zone.
|
||||
"""
|
||||
pass
|
||||
|
||||
def delete_firewall_group(self, agent_mode, apply_list, firewall):
|
||||
"""Delete firewall.
|
||||
|
||||
Removes all policies created by this instance and frees up
|
||||
all the resources.
|
||||
"""
|
||||
pass
|
||||
|
||||
def update_firewall_group(self, agent_mode, apply_list, firewall):
|
||||
"""Apply the policy on all trusted interfaces.
|
||||
|
||||
Remove previous policy and apply the new policy on all trusted
|
||||
interfaces.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def apply_default_policy(self, agent_mode, apply_list, firewall):
|
||||
"""Apply the default policy on all trusted interfaces.
|
||||
|
||||
Remove current policy and apply the default policy on all trusted
|
||||
interfaces.
|
||||
"""
|
||||
pass
|
|
@ -1,97 +0,0 @@
|
|||
# Copyright (c) 2016
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import abc
|
||||
|
||||
import six
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class FwaasDriverBase(object):
|
||||
"""Firewall as a Service Driver base class.
|
||||
|
||||
Using FwaasDriver Class, an instance of L3 perimeter Firewall
|
||||
can be created. The firewall co-exists with the L3 agent.
|
||||
|
||||
One instance is created for each tenant. One firewall policy
|
||||
is associated with each tenant (in the Havana release).
|
||||
|
||||
The Firewall can be visualized as having two zones (in Havana
|
||||
release), trusted and untrusted.
|
||||
|
||||
All the 'internal' interfaces of Neutron Router is treated as trusted. The
|
||||
interface connected to 'external network' is treated as untrusted.
|
||||
|
||||
The policy is applied on traffic ingressing/egressing interfaces on
|
||||
the trusted zone. This implies that policy will be applied for traffic
|
||||
passing from
|
||||
|
||||
- trusted to untrusted zones
|
||||
- untrusted to trusted zones
|
||||
- trusted to trusted zones
|
||||
|
||||
Policy WILL NOT be applied for traffic from untrusted to untrusted zones.
|
||||
This is not a problem in Havana release as there is only one interface
|
||||
connected to external network.
|
||||
|
||||
Since the policy is applied on the internal interfaces, the traffic
|
||||
will be not be NATed to floating IP. For incoming traffic, the
|
||||
traffic will get NATed to internal IP address before it hits
|
||||
the firewall rules. So, while writing the rules, care should be
|
||||
taken if using rules based on floating IP.
|
||||
|
||||
The firewall rule addition/deletion/insertion/update are done by the
|
||||
management console. When the policy is sent to the driver, the complete
|
||||
policy is sent and the whole policy has to be applied atomically. The
|
||||
firewall rules will not get updated individually. This is to avoid problems
|
||||
related to out-of-order notifications or inconsistent behaviour by partial
|
||||
application of rules. Argument agent_mode indicates the l3 agent in DVR or
|
||||
DVR_SNAT or LEGACY mode.
|
||||
"""
|
||||
@abc.abstractmethod
|
||||
def create_firewall_group(self, agent_mode, apply_list, firewall):
|
||||
"""Create the Firewall with default (drop all) policy.
|
||||
|
||||
The default policy will be applied on all the interfaces of
|
||||
trusted zone.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_firewall_group(self, agent_mode, apply_list, firewall):
|
||||
"""Delete firewall.
|
||||
|
||||
Removes all policies created by this instance and frees up
|
||||
all the resources.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def update_firewall_group(self, agent_mode, apply_list, firewall):
|
||||
"""Apply the policy on all trusted interfaces.
|
||||
|
||||
Remove previous policy and apply the new policy on all trusted
|
||||
interfaces.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def apply_default_policy(self, agent_mode, apply_list, firewall):
|
||||
"""Apply the default policy on all trusted interfaces.
|
||||
|
||||
Remove current policy and apply the default policy on all trusted
|
||||
interfaces.
|
||||
"""
|
||||
pass
|
|
@ -1,445 +0,0 @@
|
|||
# Copyright 2013 Dell Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import excutils
|
||||
|
||||
from neutron.agent.linux import iptables_manager
|
||||
from neutron.common import utils
|
||||
from neutron_fwaas._i18n import _LE
|
||||
from neutron_fwaas.common import fwaas_constants as f_const
|
||||
from neutron_fwaas.services.firewall.drivers import fwaas_base
|
||||
from neutron_lib.exceptions import firewall_v2 as f_exc
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
FWAAS_DRIVER_NAME = 'Fwaas iptables driver'
|
||||
FWAAS_DEFAULT_CHAIN = 'fwaas-default-policy'
|
||||
|
||||
FWAAS_TO_IPTABLE_ACTION_MAP = {'allow': 'ACCEPT',
|
||||
'deny': 'DROP',
|
||||
'reject': 'REJECT'}
|
||||
INGRESS_DIRECTION = 'ingress'
|
||||
EGRESS_DIRECTION = 'egress'
|
||||
CHAIN_NAME_PREFIX = {INGRESS_DIRECTION: 'i',
|
||||
EGRESS_DIRECTION: 'o'}
|
||||
|
||||
""" Firewall rules are applied on internal-interfaces of Neutron router.
|
||||
The packets ingressing tenant's network will be on the output
|
||||
direction on internal-interfaces.
|
||||
"""
|
||||
IPTABLES_DIR = {INGRESS_DIRECTION: '-o',
|
||||
EGRESS_DIRECTION: '-i'}
|
||||
IPV4 = 'ipv4'
|
||||
IPV6 = 'ipv6'
|
||||
IP_VER_TAG = {IPV4: 'v4',
|
||||
IPV6: 'v6'}
|
||||
|
||||
INTERNAL_DEV_PREFIX = 'qr-'
|
||||
SNAT_INT_DEV_PREFIX = 'sg-'
|
||||
ROUTER_2_FIP_DEV_PREFIX = 'rfp-'
|
||||
|
||||
|
||||
class IptablesFwaasDriver(fwaas_base.FwaasDriverBase):
|
||||
"""IPTables driver for Firewall As A Service."""
|
||||
|
||||
def __init__(self):
|
||||
LOG.debug("Initializing fwaas iptables driver")
|
||||
self.pre_firewall = None
|
||||
conntrack_cls = self._load_firewall_extension_driver(
|
||||
'neutron_fwaas.services.firewall.drivers.linux',
|
||||
cfg.CONF.fwaas.conntrack_driver)
|
||||
self.conntrack = conntrack_cls()
|
||||
self.conntrack.initialize()
|
||||
|
||||
@staticmethod
|
||||
def _load_firewall_extension_driver(namespace, driver):
|
||||
"""Loads driver using alias or class name
|
||||
:param namespace: namespace where alias is defined
|
||||
:param driver: driver alias or class name
|
||||
:returns driver that is loaded
|
||||
:raises ImportError if fails to load driver
|
||||
"""
|
||||
|
||||
try:
|
||||
return utils.load_class_by_alias_or_classname(namespace,
|
||||
driver)
|
||||
except ImportError:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(_LE("Driver '%s' not found."), driver)
|
||||
|
||||
def create_firewall(self, agent_mode, apply_list, firewall):
|
||||
LOG.debug('Creating firewall %(fw_id)s for tenant %(tid)s',
|
||||
{'fw_id': firewall['id'], 'tid': firewall['tenant_id']})
|
||||
try:
|
||||
if firewall['admin_state_up']:
|
||||
self._setup_firewall(agent_mode, apply_list, firewall)
|
||||
self._remove_conntrack_new_firewall(agent_mode,
|
||||
apply_list, firewall)
|
||||
self.pre_firewall = dict(firewall)
|
||||
else:
|
||||
self.apply_default_policy(agent_mode, apply_list, firewall)
|
||||
except (LookupError, RuntimeError):
|
||||
# catch known library exceptions and raise Fwaas generic exception
|
||||
LOG.exception(_LE("Failed to create firewall: %s"), firewall['id'])
|
||||
raise f_exc.FirewallInternalDriverError(driver=FWAAS_DRIVER_NAME)
|
||||
|
||||
def _get_ipt_mgrs_with_if_prefix(self, agent_mode, router_info):
|
||||
"""Gets the iptables manager along with the if prefix to apply rules.
|
||||
|
||||
With DVR we can have differing namespaces depending on which agent
|
||||
(on Network or Compute node). Also, there is an associated i/f for
|
||||
each namespace. The iptables on the relevant namespace and matching
|
||||
i/f are provided. On the Network node we could have both the snat
|
||||
namespace and a fip so this is provided back as a list - so in that
|
||||
scenario rules can be applied on both.
|
||||
"""
|
||||
if not router_info.router.get('distributed'):
|
||||
return [{'ipt': router_info.iptables_manager,
|
||||
'if_prefix': INTERNAL_DEV_PREFIX}]
|
||||
ipt_mgrs = []
|
||||
# TODO(sridar): refactor to get strings to a common location.
|
||||
if agent_mode == 'dvr_snat':
|
||||
if router_info.snat_iptables_manager:
|
||||
ipt_mgrs.append({'ipt': router_info.snat_iptables_manager,
|
||||
'if_prefix': SNAT_INT_DEV_PREFIX})
|
||||
if router_info.dist_fip_count:
|
||||
# handle the fip case on n/w or compute node.
|
||||
ipt_mgrs.append({'ipt': router_info.iptables_manager,
|
||||
'if_prefix': ROUTER_2_FIP_DEV_PREFIX})
|
||||
return ipt_mgrs
|
||||
|
||||
def delete_firewall(self, agent_mode, apply_list, firewall):
|
||||
LOG.debug('Deleting firewall %(fw_id)s for tenant %(tid)s',
|
||||
{'fw_id': firewall['id'], 'tid': firewall['tenant_id']})
|
||||
fwid = firewall['id']
|
||||
try:
|
||||
for router_info in apply_list:
|
||||
ipt_if_prefix_list = self._get_ipt_mgrs_with_if_prefix(
|
||||
agent_mode, router_info)
|
||||
for ipt_if_prefix in ipt_if_prefix_list:
|
||||
ipt_mgr = ipt_if_prefix['ipt']
|
||||
self._remove_chains(fwid, ipt_mgr)
|
||||
self._remove_default_chains(ipt_mgr)
|
||||
# apply the changes immediately (no defer in firewall path)
|
||||
ipt_mgr.defer_apply_off()
|
||||
self.pre_firewall = None
|
||||
except (LookupError, RuntimeError):
|
||||
# catch known library exceptions and raise Fwaas generic exception
|
||||
LOG.exception(_LE("Failed to delete firewall: %s"), fwid)
|
||||
raise f_exc.FirewallInternalDriverError(driver=FWAAS_DRIVER_NAME)
|
||||
|
||||
def update_firewall(self, agent_mode, apply_list, firewall):
|
||||
LOG.debug('Updating firewall %(fw_id)s for tenant %(tid)s',
|
||||
{'fw_id': firewall['id'], 'tid': firewall['tenant_id']})
|
||||
try:
|
||||
if firewall['admin_state_up']:
|
||||
self._setup_firewall(agent_mode, apply_list, firewall)
|
||||
if self.pre_firewall:
|
||||
self._remove_conntrack_updated_firewall(agent_mode,
|
||||
apply_list, self.pre_firewall, firewall)
|
||||
else:
|
||||
self._remove_conntrack_new_firewall(agent_mode,
|
||||
apply_list, firewall)
|
||||
else:
|
||||
self.apply_default_policy(agent_mode, apply_list, firewall)
|
||||
self.pre_firewall = dict(firewall)
|
||||
except (LookupError, RuntimeError):
|
||||
# catch known library exceptions and raise Fwaas generic exception
|
||||
LOG.exception(_LE("Failed to update firewall: %s"), firewall['id'])
|
||||
raise f_exc.FirewallInternalDriverError(driver=FWAAS_DRIVER_NAME)
|
||||
|
||||
def apply_default_policy(self, agent_mode, apply_list, firewall):
|
||||
LOG.debug('Applying firewall %(fw_id)s for tenant %(tid)s',
|
||||
{'fw_id': firewall['id'], 'tid': firewall['tenant_id']})
|
||||
fwid = firewall['id']
|
||||
try:
|
||||
for router_info in apply_list:
|
||||
ipt_if_prefix_list = self._get_ipt_mgrs_with_if_prefix(
|
||||
agent_mode, router_info)
|
||||
for ipt_if_prefix in ipt_if_prefix_list:
|
||||
# the following only updates local memory; no hole in FW
|
||||
ipt_mgr = ipt_if_prefix['ipt']
|
||||
self._remove_chains(fwid, ipt_mgr)
|
||||
self._remove_default_chains(ipt_mgr)
|
||||
|
||||
# create default 'DROP ALL' policy chain
|
||||
self._add_default_policy_chain_v4v6(ipt_mgr)
|
||||
self._enable_policy_chain(fwid, ipt_if_prefix)
|
||||
|
||||
# apply the changes immediately (no defer in firewall path)
|
||||
ipt_mgr.defer_apply_off()
|
||||
except (LookupError, RuntimeError):
|
||||
# catch known library exceptions and raise Fwaas generic exception
|
||||
LOG.exception(
|
||||
_LE("Failed to apply default policy on firewall: %s"), fwid)
|
||||
raise f_exc.FirewallInternalDriverError(driver=FWAAS_DRIVER_NAME)
|
||||
|
||||
def _setup_firewall(self, agent_mode, apply_list, firewall):
|
||||
fwid = firewall['id']
|
||||
for router_info in apply_list:
|
||||
ipt_if_prefix_list = self._get_ipt_mgrs_with_if_prefix(
|
||||
agent_mode, router_info)
|
||||
for ipt_if_prefix in ipt_if_prefix_list:
|
||||
ipt_mgr = ipt_if_prefix['ipt']
|
||||
# the following only updates local memory; no hole in FW
|
||||
self._remove_chains(fwid, ipt_mgr)
|
||||
self._remove_default_chains(ipt_mgr)
|
||||
|
||||
# create default 'DROP ALL' policy chain
|
||||
self._add_default_policy_chain_v4v6(ipt_mgr)
|
||||
#create chain based on configured policy
|
||||
self._setup_chains(firewall, ipt_if_prefix)
|
||||
|
||||
# apply the changes immediately (no defer in firewall path)
|
||||
ipt_mgr.defer_apply_off()
|
||||
|
||||
def _get_chain_name(self, fwid, ver, direction):
|
||||
return '%s%s%s' % (CHAIN_NAME_PREFIX[direction],
|
||||
IP_VER_TAG[ver],
|
||||
fwid)
|
||||
|
||||
def _setup_chains(self, firewall, ipt_if_prefix):
|
||||
"""Create Fwaas chain using the rules in the policy
|
||||
"""
|
||||
fw_rules_list = firewall['firewall_rule_list']
|
||||
fwid = firewall['id']
|
||||
ipt_mgr = ipt_if_prefix['ipt']
|
||||
|
||||
#default rules for invalid packets and established sessions
|
||||
invalid_rule = self._drop_invalid_packets_rule()
|
||||
est_rule = self._allow_established_rule()
|
||||
|
||||
for ver in [IPV4, IPV6]:
|
||||
if ver == IPV4:
|
||||
table = ipt_mgr.ipv4['filter']
|
||||
else:
|
||||
table = ipt_mgr.ipv6['filter']
|
||||
ichain_name = self._get_chain_name(fwid, ver, INGRESS_DIRECTION)
|
||||
ochain_name = self._get_chain_name(fwid, ver, EGRESS_DIRECTION)
|
||||
for name in [ichain_name, ochain_name]:
|
||||
table.add_chain(name)
|
||||
table.add_rule(name, invalid_rule)
|
||||
table.add_rule(name, est_rule)
|
||||
|
||||
for rule in fw_rules_list:
|
||||
if not rule['enabled']:
|
||||
continue
|
||||
iptbl_rule = self._convert_fwaas_to_iptables_rule(rule)
|
||||
if rule['ip_version'] == 4:
|
||||
ver = IPV4
|
||||
table = ipt_mgr.ipv4['filter']
|
||||
else:
|
||||
ver = IPV6
|
||||
table = ipt_mgr.ipv6['filter']
|
||||
ichain_name = self._get_chain_name(fwid, ver, INGRESS_DIRECTION)
|
||||
ochain_name = self._get_chain_name(fwid, ver, EGRESS_DIRECTION)
|
||||
table.add_rule(ichain_name, iptbl_rule)
|
||||
table.add_rule(ochain_name, iptbl_rule)
|
||||
self._enable_policy_chain(fwid, ipt_if_prefix)
|
||||
|
||||
def _find_changed_rules(self, pre_firewall, firewall):
|
||||
"""Find the rules changed between the current firewall
|
||||
and the updating rule
|
||||
"""
|
||||
changed_rules = []
|
||||
fw_rules_list = firewall[f_const.FIREWALL_RULE_LIST]
|
||||
pre_fw_rules_list = pre_firewall[f_const.FIREWALL_RULE_LIST]
|
||||
for pre_fw_rule in pre_fw_rules_list:
|
||||
for fw_rule in fw_rules_list:
|
||||
if (pre_fw_rule.get('id') == fw_rule.get('id') and
|
||||
pre_fw_rule != fw_rule):
|
||||
changed_rules.append(pre_fw_rule)
|
||||
changed_rules.append(fw_rule)
|
||||
return changed_rules
|
||||
|
||||
def _find_removed_rules(self, pre_firewall, firewall):
|
||||
fw_rules_list = firewall[f_const.FIREWALL_RULE_LIST]
|
||||
pre_fw_rules_list = pre_firewall[f_const.FIREWALL_RULE_LIST]
|
||||
fw_rule_ids = [fw_rule.get('id') for fw_rule in fw_rules_list]
|
||||
removed_rules = [pre_fw_rule for pre_fw_rule in
|
||||
pre_fw_rules_list if pre_fw_rule.get('id') not in fw_rule_ids]
|
||||
return removed_rules
|
||||
|
||||
def _find_new_rules(self, pre_firewall, firewall):
|
||||
return self._find_removed_rules(firewall, pre_firewall)
|
||||
|
||||
def _remove_conntrack_new_firewall(self, agent_mode, apply_list, firewall):
|
||||
"""Remove conntrack when create new firewall"""
|
||||
routers_list = list(set(apply_list))
|
||||
for router_info in routers_list:
|
||||
ipt_if_prefix_list = self._get_ipt_mgrs_with_if_prefix(
|
||||
agent_mode, router_info)
|
||||
for ipt_if_prefix in ipt_if_prefix_list:
|
||||
ipt_mgr = ipt_if_prefix['ipt']
|
||||
self.conntrack.flush_entries(ipt_mgr.namespace)
|
||||
|
||||
def _remove_conntrack_updated_firewall(self, agent_mode,
|
||||
apply_list, pre_firewall, firewall):
|
||||
"""Remove conntrack when updated firewall"""
|
||||
router_list = list(set(apply_list))
|
||||
for router_info in router_list:
|
||||
ipt_if_prefix_list = self._get_ipt_mgrs_with_if_prefix(
|
||||
agent_mode, router_info)
|
||||
for ipt_if_prefix in ipt_if_prefix_list:
|
||||
ipt_mgr = ipt_if_prefix['ipt']
|
||||
ch_rules = self._find_changed_rules(pre_firewall,
|
||||
firewall)
|
||||
i_rules = self._find_new_rules(pre_firewall, firewall)
|
||||
r_rules = self._find_removed_rules(pre_firewall, firewall)
|
||||
removed_conntrack_rules_list = ch_rules + i_rules + r_rules
|
||||
self.conntrack.delete_entries(removed_conntrack_rules_list,
|
||||
ipt_mgr.namespace)
|
||||
|
||||
def _remove_default_chains(self, nsid):
|
||||
"""Remove fwaas default policy chain."""
|
||||
self._remove_chain_by_name(IPV4, FWAAS_DEFAULT_CHAIN, nsid)
|
||||
self._remove_chain_by_name(IPV6, FWAAS_DEFAULT_CHAIN, nsid)
|
||||
|
||||
def _remove_chains(self, fwid, ipt_mgr):
|
||||
"""Remove fwaas policy chain."""
|
||||
for ver in [IPV4, IPV6]:
|
||||
for direction in [INGRESS_DIRECTION, EGRESS_DIRECTION]:
|
||||
chain_name = self._get_chain_name(fwid, ver, direction)
|
||||
self._remove_chain_by_name(ver, chain_name, ipt_mgr)
|
||||
|
||||
def _add_default_policy_chain_v4v6(self, ipt_mgr):
|
||||
ipt_mgr.ipv4['filter'].add_chain(FWAAS_DEFAULT_CHAIN)
|
||||
ipt_mgr.ipv4['filter'].add_rule(FWAAS_DEFAULT_CHAIN, '-j DROP')
|
||||
ipt_mgr.ipv6['filter'].add_chain(FWAAS_DEFAULT_CHAIN)
|
||||
ipt_mgr.ipv6['filter'].add_rule(FWAAS_DEFAULT_CHAIN, '-j DROP')
|
||||
|
||||
def _remove_chain_by_name(self, ver, chain_name, ipt_mgr):
|
||||
if ver == IPV4:
|
||||
ipt_mgr.ipv4['filter'].remove_chain(chain_name)
|
||||
else:
|
||||
ipt_mgr.ipv6['filter'].remove_chain(chain_name)
|
||||
|
||||
def _add_rules_to_chain(self, ipt_mgr, ver, chain_name, rules):
|
||||
if ver == IPV4:
|
||||
table = ipt_mgr.ipv4['filter']
|
||||
else:
|
||||
table = ipt_mgr.ipv6['filter']
|
||||
for rule in rules:
|
||||
table.add_rule(chain_name, rule)
|
||||
|
||||
def _enable_policy_chain(self, fwid, ipt_if_prefix):
|
||||
bname = iptables_manager.binary_name
|
||||
ipt_mgr = ipt_if_prefix['ipt']
|
||||
if_prefix = ipt_if_prefix['if_prefix']
|
||||
|
||||
for (ver, tbl) in [(IPV4, ipt_mgr.ipv4['filter']),
|
||||
(IPV6, ipt_mgr.ipv6['filter'])]:
|
||||
for direction in [INGRESS_DIRECTION, EGRESS_DIRECTION]:
|
||||
chain_name = self._get_chain_name(fwid, ver, direction)
|
||||
chain_name = iptables_manager.get_chain_name(chain_name)
|
||||
if chain_name in tbl.chains:
|
||||
jump_rule = ['%s %s+ -j %s-%s' % (IPTABLES_DIR[direction],
|
||||
if_prefix, bname, chain_name)]
|
||||
self._add_rules_to_chain(ipt_mgr,
|
||||
ver, 'FORWARD', jump_rule)
|
||||
|
||||
#jump to DROP_ALL policy
|
||||
chain_name = iptables_manager.get_chain_name(FWAAS_DEFAULT_CHAIN)
|
||||
jump_rule = ['-o %s+ -j %s-%s' % (if_prefix, bname, chain_name)]
|
||||
self._add_rules_to_chain(ipt_mgr, IPV4, 'FORWARD', jump_rule)
|
||||
self._add_rules_to_chain(ipt_mgr, IPV6, 'FORWARD', jump_rule)
|
||||
|
||||
#jump to DROP_ALL policy
|
||||
chain_name = iptables_manager.get_chain_name(FWAAS_DEFAULT_CHAIN)
|
||||
jump_rule = ['-i %s+ -j %s-%s' % (if_prefix, bname, chain_name)]
|
||||
self._add_rules_to_chain(ipt_mgr, IPV4, 'FORWARD', jump_rule)
|
||||
self._add_rules_to_chain(ipt_mgr, IPV6, 'FORWARD', jump_rule)
|
||||
|
||||
def _convert_fwaas_to_iptables_rule(self, rule):
|
||||
action = FWAAS_TO_IPTABLE_ACTION_MAP[rule.get('action')]
|
||||
|
||||
# Output ordering is important here as it must exactly match what
|
||||
# is returned by iptables-save. If not we risk unnecessarily removing
|
||||
# and readding rules.
|
||||
args = []
|
||||
|
||||
args += self._protocol_arg(rule.get('protocol'))
|
||||
|
||||
args += self._ip_prefix_arg('s', rule.get('source_ip_address'))
|
||||
args += self._ip_prefix_arg('d', rule.get('destination_ip_address'))
|
||||
|
||||
# iptables adds '-m protocol' when any source
|
||||
# or destination port number is specified
|
||||
if not((rule.get('source_port') is None)
|
||||
and (rule.get('destination_port') is None)):
|
||||
args += self._match_arg(rule.get('protocol'))
|
||||
|
||||
args += self._port_arg('sport',
|
||||
rule.get('protocol'),
|
||||
rule.get('source_port'))
|
||||
|
||||
args += self._port_arg('dport',
|
||||
rule.get('protocol'),
|
||||
rule.get('destination_port'))
|
||||
|
||||
args += self._action_arg(action)
|
||||
|
||||
iptables_rule = ' '.join(args)
|
||||
return iptables_rule
|
||||
|
||||
def _drop_invalid_packets_rule(self):
|
||||
return '-m state --state INVALID -j DROP'
|
||||
|
||||
def _allow_established_rule(self):
|
||||
return '-m state --state RELATED,ESTABLISHED -j ACCEPT'
|
||||
|
||||
def _action_arg(self, action):
|
||||
if not action:
|
||||
return []
|
||||
|
||||
args = ['-j', action]
|
||||
|
||||
return args
|
||||
|
||||
def _protocol_arg(self, protocol):
|
||||
if not protocol:
|
||||
return []
|
||||
|
||||
args = ['-p', protocol]
|
||||
|
||||
return args
|
||||
|
||||
def _match_arg(self, protocol):
|
||||
if not protocol:
|
||||
return []
|
||||
|
||||
protocol_modules = {'udp': 'udp', 'tcp': 'tcp',
|
||||
'icmp': 'icmp', 'ipv6-icmp': 'icmp6'}
|
||||
# iptables adds '-m protocol' when the port number is specified
|
||||
args = ['-m', protocol_modules[protocol]]
|
||||
|
||||
return args
|
||||
|
||||
def _port_arg(self, direction, protocol, port):
|
||||
if (protocol not in ['udp', 'tcp']
|
||||
or port is None):
|
||||
return []
|
||||
|
||||
args = ['--%s' % direction, '%s' % port]
|
||||
|
||||
return args
|
||||
|
||||
def _ip_prefix_arg(self, direction, ip_prefix):
|
||||
if not(ip_prefix):
|
||||
return []
|
||||
|
||||
args = ['-%s' % direction, '%s' % utils.ip_to_cidr(ip_prefix)]
|
||||
return args
|
|
@ -1,499 +0,0 @@
|
|||
# Copyright (c) 2016
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutron.agent.linux import iptables_manager
|
||||
from neutron.agent.linux import utils as linux_utils
|
||||
from oslo_log import log as logging
|
||||
|
||||
from neutron.common import utils
|
||||
from neutron_fwaas._i18n import _LE
|
||||
from neutron_fwaas.extensions import firewall as fw_ext
|
||||
from neutron_fwaas.services.firewall.drivers import fwaas_base_v2
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
FWAAS_DRIVER_NAME = 'Fwaas iptables driver'
|
||||
FWAAS_DEFAULT_CHAIN = 'fwaas-default-policy'
|
||||
|
||||
FWAAS_TO_IPTABLE_ACTION_MAP = {'allow': 'ACCEPT',
|
||||
'deny': 'DROP',
|
||||
'reject': 'REJECT'}
|
||||
INGRESS_DIRECTION = 'ingress'
|
||||
EGRESS_DIRECTION = 'egress'
|
||||
CHAIN_NAME_PREFIX = {INGRESS_DIRECTION: 'i',
|
||||
EGRESS_DIRECTION: 'o'}
|
||||
|
||||
""" Firewall rules are applied on internal-interfaces of Neutron router.
|
||||
The packets ingressing tenant's network will be on the output
|
||||
direction on internal-interfaces.
|
||||
"""
|
||||
IPTABLES_DIR = {INGRESS_DIRECTION: '-o',
|
||||
EGRESS_DIRECTION: '-i'}
|
||||
IPV4 = 'ipv4'
|
||||
IPV6 = 'ipv6'
|
||||
IP_VER_TAG = {IPV4: 'v4',
|
||||
IPV6: 'v6'}
|
||||
|
||||
INTERNAL_DEV_PREFIX = 'qr-'
|
||||
SNAT_INT_DEV_PREFIX = 'sg-'
|
||||
ROUTER_2_FIP_DEV_PREFIX = 'rfp-'
|
||||
|
||||
MAX_INTF_NAME_LEN = 14
|
||||
|
||||
|
||||
class IptablesFwaasDriver(fwaas_base_v2.FwaasDriverBase):
|
||||
"""IPTables driver for Firewall As A Service."""
|
||||
|
||||
def __init__(self):
|
||||
LOG.debug("Initializing fwaas iptables driver")
|
||||
self.pre_firewall = None
|
||||
|
||||
def initialize(self):
|
||||
pass
|
||||
|
||||
def _get_intf_name(self, if_prefix, port_id):
|
||||
_name = "%s%s" % (if_prefix, port_id)
|
||||
return _name[:MAX_INTF_NAME_LEN]
|
||||
|
||||
def create_firewall_group(self, agent_mode, apply_list, firewall):
|
||||
LOG.debug('Creating firewall %(fw_id)s for tenant %(tid)s',
|
||||
{'fw_id': firewall['id'], 'tid': firewall['tenant_id']})
|
||||
try:
|
||||
if firewall['admin_state_up']:
|
||||
self._setup_firewall(agent_mode, apply_list, firewall)
|
||||
self._remove_conntrack_new_firewall(agent_mode,
|
||||
apply_list, firewall)
|
||||
self.pre_firewall = dict(firewall)
|
||||
else:
|
||||
self.apply_default_policy(agent_mode, apply_list, firewall)
|
||||
except (LookupError, RuntimeError):
|
||||
# catch known library exceptions and raise Fwaas generic exception
|
||||
LOG.exception(_LE("Failed to create firewall: %s"), firewall['id'])
|
||||
raise fw_ext.FirewallInternalDriverError(driver=FWAAS_DRIVER_NAME)
|
||||
|
||||
def _get_ipt_mgrs_with_if_prefix(self, agent_mode, ri):
|
||||
"""Gets the iptables manager along with the if prefix to apply rules.
|
||||
|
||||
With DVR we can have differing namespaces depending on which agent
|
||||
(on Network or Compute node). Also, there is an associated i/f for
|
||||
each namespace. The iptables on the relevant namespace and matching
|
||||
i/f are provided. On the Network node we could have both the snat
|
||||
namespace and a fip so this is provided back as a list - so in that
|
||||
scenario rules can be applied on both.
|
||||
"""
|
||||
if not ri.router.get('distributed'):
|
||||
return [{'ipt': ri.iptables_manager,
|
||||
'if_prefix': INTERNAL_DEV_PREFIX}]
|
||||
ipt_mgrs = []
|
||||
# TODO(sridar): refactor to get strings to a common location.
|
||||
if agent_mode == 'dvr_snat':
|
||||
if ri.snat_iptables_manager:
|
||||
ipt_mgrs.append({'ipt': ri.snat_iptables_manager,
|
||||
'if_prefix': SNAT_INT_DEV_PREFIX})
|
||||
if ri.dist_fip_count:
|
||||
# handle the fip case on n/w or compute node.
|
||||
ipt_mgrs.append({'ipt': ri.iptables_manager,
|
||||
'if_prefix': ROUTER_2_FIP_DEV_PREFIX})
|
||||
return ipt_mgrs
|
||||
|
||||
def delete_firewall_group(self, agent_mode, apply_list, firewall):
|
||||
LOG.debug('Deleting firewall %(fw_id)s for tenant %(tid)s',
|
||||
{'fw_id': firewall['id'], 'tid': firewall['tenant_id']})
|
||||
fwid = firewall['id']
|
||||
try:
|
||||
for ri, router_fw_ports in apply_list:
|
||||
ipt_if_prefix_list = self._get_ipt_mgrs_with_if_prefix(
|
||||
agent_mode, ri)
|
||||
for ipt_if_prefix in ipt_if_prefix_list:
|
||||
ipt_mgr = ipt_if_prefix['ipt']
|
||||
self._remove_chains(fwid, ipt_mgr)
|
||||
self._remove_default_chains(ipt_mgr)
|
||||
# apply the changes immediately (no defer in firewall path)
|
||||
ipt_mgr.defer_apply_off()
|
||||
self.pre_firewall = None
|
||||
except (LookupError, RuntimeError):
|
||||
# catch known library exceptions and raise Fwaas generic exception
|
||||
LOG.exception(_LE("Failed to delete firewall: %s"), fwid)
|
||||
raise fw_ext.FirewallInternalDriverError(driver=FWAAS_DRIVER_NAME)
|
||||
|
||||
def update_firewall_group(self, agent_mode, apply_list, firewall):
|
||||
LOG.debug('Updating firewall %(fw_id)s for tenant %(tid)s',
|
||||
{'fw_id': firewall['id'], 'tid': firewall['tenant_id']})
|
||||
try:
|
||||
if firewall['admin_state_up']:
|
||||
self._setup_firewall(agent_mode, apply_list, firewall)
|
||||
if self.pre_firewall:
|
||||
self._remove_conntrack_updated_firewall(agent_mode,
|
||||
apply_list, self.pre_firewall, firewall)
|
||||
else:
|
||||
self._remove_conntrack_new_firewall(agent_mode,
|
||||
apply_list, firewall)
|
||||
else:
|
||||
self.apply_default_policy(agent_mode, apply_list, firewall)
|
||||
self.pre_firewall = dict(firewall)
|
||||
except (LookupError, RuntimeError):
|
||||
# catch known library exceptions and raise Fwaas generic exception
|
||||
LOG.exception(_LE("Failed to update firewall: %s"), firewall['id'])
|
||||
raise fw_ext.FirewallInternalDriverError(driver=FWAAS_DRIVER_NAME)
|
||||
|
||||
def apply_default_policy(self, agent_mode, apply_list, firewall):
|
||||
LOG.debug('Applying firewall %(fw_id)s for tenant %(tid)s',
|
||||
{'fw_id': firewall['id'], 'tid': firewall['tenant_id']})
|
||||
fwid = firewall['id']
|
||||
try:
|
||||
for ri, router_fw_ports in apply_list:
|
||||
ipt_if_prefix_list = self._get_ipt_mgrs_with_if_prefix(
|
||||
agent_mode, ri)
|
||||
for ipt_if_prefix in ipt_if_prefix_list:
|
||||
# the following only updates local memory; no hole in FW
|
||||
ipt_mgr = ipt_if_prefix['ipt']
|
||||
self._remove_chains(fwid, ipt_mgr)
|
||||
self._remove_default_chains(ipt_mgr)
|
||||
|
||||
# create default 'DROP ALL' policy chain
|
||||
self._add_default_policy_chain_v4v6(ipt_mgr)
|
||||
self._enable_policy_chain(fwid, ipt_if_prefix,
|
||||
router_fw_ports)
|
||||
|
||||
# apply the changes immediately (no defer in firewall path)
|
||||
ipt_mgr.defer_apply_off()
|
||||
except (LookupError, RuntimeError):
|
||||
# catch known library exceptions and raise Fwaas generic exception
|
||||
LOG.exception(
|
||||
_LE("Failed to apply default policy on firewall: %s"), fwid)
|
||||
raise fw_ext.FirewallInternalDriverError(driver=FWAAS_DRIVER_NAME)
|
||||
|
||||
def _setup_firewall(self, agent_mode, apply_list, firewall):
|
||||
fwid = firewall['id']
|
||||
for ri, router_fw_ports in apply_list:
|
||||
ipt_if_prefix_list = self._get_ipt_mgrs_with_if_prefix(
|
||||
agent_mode, ri)
|
||||
for ipt_if_prefix in ipt_if_prefix_list:
|
||||
ipt_mgr = ipt_if_prefix['ipt']
|
||||
# the following only updates local memory; no hole in FW
|
||||
self._remove_chains(fwid, ipt_mgr)
|
||||
self._remove_default_chains(ipt_mgr)
|
||||
|
||||
# create default 'DROP ALL' policy chain
|
||||
self._add_default_policy_chain_v4v6(ipt_mgr)
|
||||
# create chain based on configured policy
|
||||
self._setup_chains(firewall, ipt_if_prefix, router_fw_ports)
|
||||
|
||||
# apply the changes immediately (no defer in firewall path)
|
||||
ipt_mgr.defer_apply_off()
|
||||
|
||||
def _get_chain_name(self, fwid, ver, direction):
|
||||
return '%s%s%s' % (CHAIN_NAME_PREFIX[direction],
|
||||
IP_VER_TAG[ver],
|
||||
fwid)
|
||||
|
||||
def _setup_chains(self, firewall, ipt_if_prefix, router_fw_ports):
|
||||
"""Create Fwaas chain using the rules in the policy
|
||||
"""
|
||||
egress_rule_list = firewall['egress_rule_list']
|
||||
ingress_rule_list = firewall['ingress_rule_list']
|
||||
fwid = firewall['id']
|
||||
ipt_mgr = ipt_if_prefix['ipt']
|
||||
|
||||
# default rules for invalid packets and established sessions
|
||||
invalid_rule = self._drop_invalid_packets_rule()
|
||||
est_rule = self._allow_established_rule()
|
||||
|
||||
for ver in [IPV4, IPV6]:
|
||||
if ver == IPV4:
|
||||
table = ipt_mgr.ipv4['filter']
|
||||
else:
|
||||
table = ipt_mgr.ipv6['filter']
|
||||
ichain_name = self._get_chain_name(fwid, ver, INGRESS_DIRECTION)
|
||||
ochain_name = self._get_chain_name(fwid, ver, EGRESS_DIRECTION)
|
||||
for name in [ichain_name, ochain_name]:
|
||||
table.add_chain(name)
|
||||
table.add_rule(name, invalid_rule)
|
||||
table.add_rule(name, est_rule)
|
||||
|
||||
for rule in ingress_rule_list:
|
||||
if not rule['enabled']:
|
||||
continue
|
||||
iptbl_rule = self._convert_fwaas_to_iptables_rule(rule)
|
||||
if rule['ip_version'] == 4:
|
||||
ver = IPV4
|
||||
table = ipt_mgr.ipv4['filter']
|
||||
else:
|
||||
ver = IPV6
|
||||
table = ipt_mgr.ipv6['filter']
|
||||
ichain_name = self._get_chain_name(fwid, ver, INGRESS_DIRECTION)
|
||||
table.add_rule(ichain_name, iptbl_rule)
|
||||
|
||||
for rule in egress_rule_list:
|
||||
if not rule['enabled']:
|
||||
continue
|
||||
iptbl_rule = self._convert_fwaas_to_iptables_rule(rule)
|
||||
if rule['ip_version'] == 4:
|
||||
ver = IPV4
|
||||
table = ipt_mgr.ipv4['filter']
|
||||
else:
|
||||
ver = IPV6
|
||||
table = ipt_mgr.ipv6['filter']
|
||||
ochain_name = self._get_chain_name(fwid, ver, EGRESS_DIRECTION)
|
||||
table.add_rule(ochain_name, iptbl_rule)
|
||||
|
||||
self._enable_policy_chain(fwid, ipt_if_prefix, router_fw_ports)
|
||||
|
||||
def _find_changed_rules(self, pre_firewall, firewall):
|
||||
"""Find the rules changed between the current firewall
|
||||
and the updating rule
|
||||
"""
|
||||
changed_rules = []
|
||||
for fw_rule_list in ['egress_rule_list', 'ingress_rule_list']:
|
||||
pre_fw_rules = pre_firewall[fw_rule_list]
|
||||
fw_rules = firewall[fw_rule_list]
|
||||
for pre_fw_rule in pre_fw_rules:
|
||||
for fw_rule in fw_rules:
|
||||
if (pre_fw_rule.get('id') == fw_rule.get('id') and
|
||||
pre_fw_rule != fw_rule):
|
||||
changed_rules.append(pre_fw_rule)
|
||||
changed_rules.append(fw_rule)
|
||||
return changed_rules
|
||||
|
||||
def _find_removed_rules(self, pre_firewall, firewall):
|
||||
removed_rules = []
|
||||
for fw_rule_list in ['egress_rule_list', 'ingress_rule_list']:
|
||||
pre_fw_rules = pre_firewall[fw_rule_list]
|
||||
fw_rules = firewall[fw_rule_list]
|
||||
fw_rule_ids = [fw_rule['id'] for fw_rule in fw_rules]
|
||||
removed_rules.extend([pre_fw_rule for pre_fw_rule in pre_fw_rules
|
||||
if pre_fw_rule['id'] not in fw_rule_ids])
|
||||
return removed_rules
|
||||
|
||||
def _find_new_rules(self, pre_firewall, firewall):
|
||||
return self._find_removed_rules(firewall, pre_firewall)
|
||||
|
||||
def _get_conntrack_cmd_from_rule(self, ipt_mgr, rule=None):
|
||||
prefixcmd = ['ip', 'netns', 'exec'] + [ipt_mgr.namespace]
|
||||
cmd = ['conntrack', '-D']
|
||||
if rule:
|
||||
conntrack_filter = self._get_conntrack_filter_from_rule(rule)
|
||||
exec_cmd = prefixcmd + cmd + conntrack_filter
|
||||
else:
|
||||
exec_cmd = prefixcmd + cmd
|
||||
return exec_cmd
|
||||
|
||||
def _remove_conntrack_by_cmd(self, cmd):
|
||||
if cmd:
|
||||
try:
|
||||
linux_utils.execute(cmd, run_as_root=True,
|
||||
check_exit_code=True,
|
||||
extra_ok_codes=[1])
|
||||
except RuntimeError:
|
||||
LOG.exception(
|
||||
_LE("Failed execute conntrack command %s"), str(cmd))
|
||||
|
||||
def _remove_conntrack_new_firewall(self, agent_mode, apply_list, firewall):
|
||||
"""Remove conntrack when create new firewall"""
|
||||
routers_list = list(set([apply_info[0] for apply_info in apply_list]))
|
||||
for ri in routers_list:
|
||||
ipt_if_prefix_list = self._get_ipt_mgrs_with_if_prefix(
|
||||
agent_mode, ri)
|
||||
for ipt_if_prefix in ipt_if_prefix_list:
|
||||
ipt_mgr = ipt_if_prefix['ipt']
|
||||
cmd = self._get_conntrack_cmd_from_rule(ipt_mgr)
|
||||
self._remove_conntrack_by_cmd(cmd)
|
||||
|
||||
def _remove_conntrack_updated_firewall(self, agent_mode,
|
||||
apply_list, pre_firewall, firewall):
|
||||
"""Remove conntrack when updated firewall"""
|
||||
routers_list = list(set([apply_info[0] for apply_info in apply_list]))
|
||||
for ri in routers_list:
|
||||
ipt_if_prefix_list = self._get_ipt_mgrs_with_if_prefix(
|
||||
agent_mode, ri)
|
||||
for ipt_if_prefix in ipt_if_prefix_list:
|
||||
ipt_mgr = ipt_if_prefix['ipt']
|
||||
ch_rules = self._find_changed_rules(pre_firewall,
|
||||
firewall)
|
||||
i_rules = self._find_new_rules(pre_firewall, firewall)
|
||||
r_rules = self._find_removed_rules(pre_firewall, firewall)
|
||||
removed_conntrack_rules_list = ch_rules + i_rules + r_rules
|
||||
for rule in removed_conntrack_rules_list:
|
||||
cmd = self._get_conntrack_cmd_from_rule(ipt_mgr, rule)
|
||||
self._remove_conntrack_by_cmd(cmd)
|
||||
|
||||
def _get_conntrack_filter_from_rule(self, rule):
|
||||
"""Get conntrack filter from rule.
|
||||
The key for get conntrack filter is protocol, destination_port
|
||||
and source_port. If we want to take more keys, add to the list.
|
||||
"""
|
||||
conntrack_filter = []
|
||||
keys = [['-p', 'protocol'], ['-f', 'ip_version'],
|
||||
['--dport', 'destination_port'], ['--sport', 'source_port']]
|
||||
for key in keys:
|
||||
if rule.get(key[1]):
|
||||
if key[1] == 'ip_version':
|
||||
conntrack_filter.append(key[0])
|
||||
conntrack_filter.append('ipv' + str(rule.get(key[1])))
|
||||
else:
|
||||
conntrack_filter.append(key[0])
|
||||
conntrack_filter.append(rule.get(key[1]))
|
||||
return conntrack_filter
|
||||
|
||||
def _remove_default_chains(self, nsid):
|
||||
"""Remove fwaas default policy chain."""
|
||||
self._remove_chain_by_name(IPV4, FWAAS_DEFAULT_CHAIN, nsid)
|
||||
self._remove_chain_by_name(IPV6, FWAAS_DEFAULT_CHAIN, nsid)
|
||||
|
||||
def _remove_chains(self, fwid, ipt_mgr):
|
||||
"""Remove fwaas policy chain."""
|
||||
for ver in [IPV4, IPV6]:
|
||||
for direction in [INGRESS_DIRECTION, EGRESS_DIRECTION]:
|
||||
chain_name = self._get_chain_name(fwid, ver, direction)
|
||||
self._remove_chain_by_name(ver, chain_name, ipt_mgr)
|
||||
|
||||
def _add_default_policy_chain_v4v6(self, ipt_mgr):
|
||||
ipt_mgr.ipv4['filter'].add_chain(FWAAS_DEFAULT_CHAIN)
|
||||
ipt_mgr.ipv4['filter'].add_rule(FWAAS_DEFAULT_CHAIN, '-j DROP')
|
||||
ipt_mgr.ipv6['filter'].add_chain(FWAAS_DEFAULT_CHAIN)
|
||||
ipt_mgr.ipv6['filter'].add_rule(FWAAS_DEFAULT_CHAIN, '-j DROP')
|
||||
|
||||
def _remove_chain_by_name(self, ver, chain_name, ipt_mgr):
|
||||
if ver == IPV4:
|
||||
ipt_mgr.ipv4['filter'].remove_chain(chain_name)
|
||||
else:
|
||||
ipt_mgr.ipv6['filter'].remove_chain(chain_name)
|
||||
|
||||
def _add_rules_to_chain(self, ipt_mgr, ver, chain_name, rules):
|
||||
if ver == IPV4:
|
||||
table = ipt_mgr.ipv4['filter']
|
||||
else:
|
||||
table = ipt_mgr.ipv6['filter']
|
||||
for rule in rules:
|
||||
table.add_rule(chain_name, rule)
|
||||
|
||||
def _enable_policy_chain(self, fwid, ipt_if_prefix, router_fw_ports):
|
||||
bname = iptables_manager.binary_name
|
||||
ipt_mgr = ipt_if_prefix['ipt']
|
||||
if_prefix = ipt_if_prefix['if_prefix']
|
||||
|
||||
for (ver, tbl) in [(IPV4, ipt_mgr.ipv4['filter']),
|
||||
(IPV6, ipt_mgr.ipv6['filter'])]:
|
||||
for direction in [INGRESS_DIRECTION, EGRESS_DIRECTION]:
|
||||
chain_name = self._get_chain_name(fwid, ver, direction)
|
||||
chain_name = iptables_manager.get_chain_name(chain_name)
|
||||
if chain_name in tbl.chains:
|
||||
for router_fw_port in router_fw_ports:
|
||||
intf_name = self._get_intf_name(if_prefix,
|
||||
router_fw_port)
|
||||
jump_rule = ['%s %s -j %s-%s' % (
|
||||
IPTABLES_DIR[direction], intf_name,
|
||||
bname, chain_name)]
|
||||
self._add_rules_to_chain(ipt_mgr, ver,
|
||||
'FORWARD', jump_rule)
|
||||
|
||||
# jump to DROP_ALL policy
|
||||
chain_name = iptables_manager.get_chain_name(FWAAS_DEFAULT_CHAIN)
|
||||
for router_fw_port in router_fw_ports:
|
||||
intf_name = self._get_intf_name(if_prefix,
|
||||
router_fw_port)
|
||||
jump_rule = ['-o %s -j %s-%s' % (intf_name, bname, chain_name)]
|
||||
self._add_rules_to_chain(ipt_mgr, IPV4, 'FORWARD', jump_rule)
|
||||
self._add_rules_to_chain(ipt_mgr, IPV6, 'FORWARD', jump_rule)
|
||||
|
||||
# jump to DROP_ALL policy
|
||||
chain_name = iptables_manager.get_chain_name(FWAAS_DEFAULT_CHAIN)
|
||||
for router_fw_port in router_fw_ports:
|
||||
intf_name = self._get_intf_name(if_prefix,
|
||||
router_fw_port)
|
||||
jump_rule = ['-i %s -j %s-%s' % (intf_name, bname, chain_name)]
|
||||
self._add_rules_to_chain(ipt_mgr, IPV4, 'FORWARD', jump_rule)
|
||||
self._add_rules_to_chain(ipt_mgr, IPV6, 'FORWARD', jump_rule)
|
||||
|
||||
def _convert_fwaas_to_iptables_rule(self, rule):
|
||||
action = FWAAS_TO_IPTABLE_ACTION_MAP[rule.get('action')]
|
||||
|
||||
# Output ordering is important here as it must exactly match what
|
||||
# is returned by iptables-save. If not we risk unnecessarily removing
|
||||
# and readding rules.
|
||||
args = []
|
||||
|
||||
args += self._protocol_arg(rule.get('protocol'))
|
||||
|
||||
args += self._ip_prefix_arg('s', rule.get('source_ip_address'))
|
||||
args += self._ip_prefix_arg('d', rule.get('destination_ip_address'))
|
||||
|
||||
# iptables adds '-m protocol' when any source
|
||||
# or destination port number is specified
|
||||
if not((rule.get('source_port') is None)
|
||||
and (rule.get('destination_port') is None)):
|
||||
args += self._match_arg(rule.get('protocol'))
|
||||
|
||||
args += self._port_arg('sport',
|
||||
rule.get('protocol'),
|
||||
rule.get('source_port'))
|
||||
|
||||
args += self._port_arg('dport',
|
||||
rule.get('protocol'),
|
||||
rule.get('destination_port'))
|
||||
|
||||
args += self._action_arg(action)
|
||||
|
||||
iptables_rule = ' '.join(args)
|
||||
return iptables_rule
|
||||
|
||||
def _drop_invalid_packets_rule(self):
|
||||
return '-m state --state INVALID -j DROP'
|
||||
|
||||
def _allow_established_rule(self):
|
||||
return '-m state --state RELATED,ESTABLISHED -j ACCEPT'
|
||||
|
||||
def _action_arg(self, action):
|
||||
if not action:
|
||||
return []
|
||||
|
||||
args = ['-j', action]
|
||||
|
||||
return args
|
||||
|
||||
def _protocol_arg(self, protocol):
|
||||
if not protocol:
|
||||
return []
|
||||
|
||||
args = ['-p', protocol]
|
||||
|
||||
return args
|
||||
|
||||
def _match_arg(self, protocol):
|
||||
if not protocol:
|
||||
return []
|
||||
|
||||
protocol_modules = {'udp': 'udp', 'tcp': 'tcp',
|
||||
'icmp': 'icmp', 'ipv6-icmp': 'icmp6'}
|
||||
# iptables adds '-m protocol' when the port number is specified
|
||||
args = ['-m', protocol_modules[protocol]]
|
||||
|
||||
return args
|
||||
|
||||
def _port_arg(self, direction, protocol, port):
|
||||
if (protocol not in ['udp', 'tcp']
|
||||
or port is None):
|
||||
return []
|
||||
|
||||
args = ['--%s' % direction, '%s' % port]
|
||||
|
||||
return args
|
||||
|
||||
def _ip_prefix_arg(self, direction, ip_prefix):
|
||||
|
||||
if not(ip_prefix):
|
||||
return []
|
||||
|
||||
args = ['-%s' % direction, '%s' % utils.ip_to_cidr(ip_prefix)]
|
||||
return args
|
|
@ -1,81 +0,0 @@
|
|||
# Copyright (c) 2017 Fujitsu Limited
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
from neutron.agent.linux import utils as linux_utils
|
||||
|
||||
from neutron_fwaas._i18n import _
|
||||
from neutron_fwaas.services.firewall.drivers import conntrack_base
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ConntrackLegacy(conntrack_base.ConntrackDriverBase):
|
||||
def initialize(self, execute=None):
|
||||
LOG.debug('Initialize Conntrack Legacy')
|
||||
self.execute = execute or linux_utils.execute
|
||||
|
||||
def flush_entries(self, namespace):
|
||||
prefixcmd = ['ip', 'netns', 'exec', namespace] if namespace else []
|
||||
cmd = prefixcmd + ['conntrack', '-D']
|
||||
self._execute_command(cmd)
|
||||
|
||||
def delete_entries(self, rules, namespace):
|
||||
for rule in rules:
|
||||
cmd = self._get_conntrack_cmd_from_rule(rule, namespace)
|
||||
self._execute_command(cmd)
|
||||
|
||||
def _execute_command(self, cmd):
|
||||
try:
|
||||
self.execute(cmd,
|
||||
run_as_root=True,
|
||||
check_exit_code=True,
|
||||
extra_ok_codes=[1])
|
||||
except RuntimeError:
|
||||
msg = _("Failed execute conntrack command %s") % cmd
|
||||
raise RuntimeError(msg)
|
||||
|
||||
def _get_conntrack_cmd_from_rule(self, rule, namespace):
|
||||
prefixcmd = (['ip', 'netns', 'exec', namespace]
|
||||
if namespace else [])
|
||||
cmd = ['conntrack', '-D']
|
||||
if rule:
|
||||
conntrack_filter = self._get_conntrack_filter_from_rule(rule)
|
||||
exec_cmd = prefixcmd + cmd + conntrack_filter
|
||||
else:
|
||||
exec_cmd = prefixcmd + cmd
|
||||
return exec_cmd
|
||||
|
||||
def _get_conntrack_filter_from_rule(self, rule):
|
||||
"""Get conntrack filter from rule
|
||||
|
||||
The key for get conntrack filter is protocol, destination_port
|
||||
and source_port. If we want to take more keys, add to the list.
|
||||
"""
|
||||
conntrack_filter = []
|
||||
keys = [['-p', 'protocol'], ['-f', 'ip_version'],
|
||||
['--dport', 'destination_port'], ['--sport', 'source_port']]
|
||||
for arg_key, rule_key in keys:
|
||||
val = rule.get(rule_key)
|
||||
if val:
|
||||
if rule_key == 'ip_version':
|
||||
conntrack_filter.append(arg_key)
|
||||
conntrack_filter.append('ipv' + str(val))
|
||||
else:
|
||||
conntrack_filter.append(arg_key)
|
||||
conntrack_filter.append(val)
|
||||
return conntrack_filter
|
|
@ -1,137 +0,0 @@
|
|||
# Copyright (c) 2017 Fujitsu Limited
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
from neutron_fwaas.privileged import netlink_lib as nl_lib
|
||||
from neutron_fwaas.services.firewall.drivers import conntrack_base
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ConntrackNetlink(conntrack_base.ConntrackDriverBase):
|
||||
def initialize(self, *args, **kwargs):
|
||||
LOG.debug('Conntrack Netlink loaded')
|
||||
|
||||
def flush_entries(self, namespace):
|
||||
"""Flush all conntrack entries within the namespace
|
||||
|
||||
:param namespace: namespace to flush
|
||||
:return: None
|
||||
"""
|
||||
nl_lib.flush_entries(namespace)
|
||||
|
||||
def delete_entries(self, rules, namespace):
|
||||
rule_filters = (self._get_filter_from_rule(r) for r in rules)
|
||||
rule_filters = sorted(rule_filters)
|
||||
entries = nl_lib.list_entries(namespace)
|
||||
delete_entries = self._get_entries_to_delete(rule_filters, entries)
|
||||
if delete_entries:
|
||||
nl_lib.delete_entries(delete_entries, namespace)
|
||||
|
||||
def _get_entries_to_delete(self, rule_filters, entries):
|
||||
"""Specify conntrack entries to delete
|
||||
|
||||
:param rule_filters: List of filters parsed from firewall rules
|
||||
:param entries: all entries within namespace
|
||||
:return: conntrack entries to delete
|
||||
"""
|
||||
# List all entries from namespace, they are already parsed
|
||||
# to a list of tuples:
|
||||
# [(4, 'icmp', 8, 0, '1.1.1.1', '2.2.2.2', 1234),
|
||||
# (4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2')]
|
||||
delete_entries = []
|
||||
entry_index = 0
|
||||
entry_number = len(entries)
|
||||
for rule_filter in rule_filters:
|
||||
while entry_index < entry_number:
|
||||
# Compare entry with rule
|
||||
comp = self._compare_entry_and_rule(rule_filter,
|
||||
entries[entry_index])
|
||||
# Increase entry_index when entry is under rule
|
||||
if comp < 0:
|
||||
entry_index += 1
|
||||
# Append entry to delete_entry if it matches with rule
|
||||
elif comp == 0:
|
||||
delete_entries.append(entries[entry_index])
|
||||
entry_index += 1
|
||||
# Switch to new higher rule
|
||||
else:
|
||||
break
|
||||
return delete_entries
|
||||
|
||||
@staticmethod
|
||||
def _get_filter_from_rule(rule):
|
||||
"""Parse the firewall rule to a tuple
|
||||
|
||||
:param rule: firewall rule
|
||||
:return: a tuple of parsed information
|
||||
"""
|
||||
rule_filter = []
|
||||
keys = ['ip_version', 'protocol',
|
||||
'source_port', 'destination_port',
|
||||
'source_ip_address', 'destination_ip_address']
|
||||
for key in keys:
|
||||
if key in ['source_port', 'destination_port']:
|
||||
port_range = rule.get(key, [])
|
||||
if port_range:
|
||||
port_lower, sep, port_upper = port_range.partition(':')
|
||||
port_upper = port_upper if sep else port_lower
|
||||
port_range = [port_lower, port_upper]
|
||||
rule_filter.append(port_range or [])
|
||||
else:
|
||||
rule_filter.append(rule.get(key, []))
|
||||
return tuple(rule_filter)
|
||||
|
||||
@staticmethod
|
||||
def _compare_entry_and_rule(rule_filter, entry):
|
||||
"""Define that the entry should be deleted or not
|
||||
|
||||
:param rule_filter: filter that is parsed from a firewall rule
|
||||
ex: (4, 'tcp', 1, 2)
|
||||
:param entry: parsed conntrack entry,
|
||||
ex: (4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2')
|
||||
:return: -1 if entry is lower than rule, 0 if entry matches rule,
|
||||
1 if entry is higher than rule
|
||||
"""
|
||||
ENTRY_IS_LOWER = -1
|
||||
ENTRY_MATCHES = 0
|
||||
ENTRY_IS_HIGHER = 1
|
||||
rule_ipversion = rule_filter[0]
|
||||
if entry[0] < rule_ipversion:
|
||||
return ENTRY_IS_LOWER
|
||||
elif entry[0] > rule_ipversion:
|
||||
return ENTRY_IS_HIGHER
|
||||
rule_protocol = rule_filter[1]
|
||||
if rule_protocol:
|
||||
if entry[1] < rule_protocol:
|
||||
return ENTRY_IS_LOWER
|
||||
elif entry[1] > rule_protocol:
|
||||
return ENTRY_IS_HIGHER
|
||||
sport_range = rule_filter[2]
|
||||
if sport_range:
|
||||
sport_range = [int(port) for port in sport_range]
|
||||
if entry[2] < min(sport_range[0], sport_range[-1]):
|
||||
return ENTRY_IS_LOWER
|
||||
elif entry[2] > max(sport_range[0], sport_range[-1]):
|
||||
return ENTRY_IS_HIGHER
|
||||
dport_range = rule_filter[3]
|
||||
if dport_range:
|
||||
dport_range = [int(port) for port in dport_range]
|
||||
if entry[3] < min(dport_range[0], dport_range[-1]):
|
||||
return ENTRY_IS_LOWER
|
||||
elif entry[3] > max(dport_range[0], dport_range[-1]):
|
||||
return ENTRY_IS_HIGHER
|
||||
return ENTRY_MATCHES
|
|
@ -1,443 +0,0 @@
|
|||
# Copyright 2013 Big Switch Networks, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from neutron_lib import constants as nl_constants
|
||||
from neutron_lib import context as neutron_context
|
||||
from neutron_lib.exceptions import firewall_v1 as f_exc
|
||||
from neutron_lib.plugins import constants as plugin_constants
|
||||
from neutron_lib.plugins import directory
|
||||
|
||||
from neutron.common import rpc as n_rpc
|
||||
from neutron.common import utils as n_utils
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
import oslo_messaging
|
||||
|
||||
from neutron_fwaas._i18n import _LI, _LW
|
||||
from neutron_fwaas.common import fwaas_constants as f_const
|
||||
from neutron_fwaas.db.firewall import firewall_db
|
||||
from neutron_fwaas.db.firewall import firewall_router_insertion_db
|
||||
from neutron_fwaas.extensions import firewall as fw_ext
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FirewallCallbacks(object):
|
||||
target = oslo_messaging.Target(version='1.0')
|
||||
|
||||
def __init__(self, plugin):
|
||||
super(FirewallCallbacks, self).__init__()
|
||||
self.plugin = plugin
|
||||
|
||||
def set_firewall_status(self, context, firewall_id, status, **kwargs):
|
||||
"""Agent uses this to set a firewall's status."""
|
||||
LOG.debug("Setting firewall %s to status: %s", firewall_id, status)
|
||||
# Sanitize status first
|
||||
if status in (nl_constants.ACTIVE, nl_constants.DOWN,
|
||||
nl_constants.INACTIVE):
|
||||
to_update = status
|
||||
else:
|
||||
to_update = nl_constants.ERROR
|
||||
# ignore changing status if firewall expects to be deleted
|
||||
# That case means that while some pending operation has been
|
||||
# performed on the backend, neutron server received delete request
|
||||
# and changed firewall status to PENDING_DELETE
|
||||
updated = self.plugin.update_firewall_status(
|
||||
context, firewall_id, to_update,
|
||||
not_in=(nl_constants.PENDING_DELETE,))
|
||||
if updated:
|
||||
LOG.debug("firewall %s status set: %s", firewall_id, to_update)
|
||||
return updated and to_update != nl_constants.ERROR
|
||||
|
||||
def firewall_deleted(self, context, firewall_id, **kwargs):
|
||||
"""Agent uses this to indicate firewall is deleted."""
|
||||
LOG.debug("firewall_deleted() called")
|
||||
try:
|
||||
with context.session.begin(subtransactions=True):
|
||||
fw_db = self.plugin._get_firewall(context, firewall_id)
|
||||
# allow to delete firewalls in ERROR state
|
||||
if fw_db.status in (nl_constants.PENDING_DELETE,
|
||||
nl_constants.ERROR):
|
||||
self.plugin.delete_db_firewall_object(context, firewall_id)
|
||||
return True
|
||||
else:
|
||||
LOG.warning(_LW('Firewall %(fw)s unexpectedly deleted by '
|
||||
'agent, status was %(status)s'),
|
||||
{'fw': firewall_id, 'status': fw_db.status})
|
||||
fw_db.update({"status": nl_constants.ERROR})
|
||||
return False
|
||||
except f_exc.FirewallNotFound:
|
||||
LOG.info(_LI('Firewall %s already deleted'), firewall_id)
|
||||
return True
|
||||
|
||||
def get_firewalls_for_tenant(self, context, **kwargs):
|
||||
"""Agent uses this to get all firewalls and rules for a tenant."""
|
||||
LOG.debug("get_firewalls_for_tenant() called")
|
||||
fw_list = []
|
||||
for fw in self.plugin.get_firewalls(context):
|
||||
fw_with_rules = self.plugin._make_firewall_dict_with_rules(
|
||||
context, fw['id'])
|
||||
if fw['status'] == nl_constants.PENDING_DELETE:
|
||||
fw_with_rules['add-router-ids'] = []
|
||||
fw_with_rules['del-router-ids'] = fw['router_ids']
|
||||
else:
|
||||
fw_with_rules['add-router-ids'] = fw['router_ids']
|
||||
fw_with_rules['del-router-ids'] = []
|
||||
fw_list.append(fw_with_rules)
|
||||
return fw_list
|
||||
|
||||
def get_tenants_with_firewalls(self, context, **kwargs):
|
||||
"""Agent uses this to get all tenants that have firewalls."""
|
||||
LOG.debug("get_tenants_with_firewalls() called")
|
||||
host = kwargs['host']
|
||||
ctx = neutron_context.get_admin_context()
|
||||
tenant_ids = self.plugin.get_firewall_tenant_ids_on_host(ctx, host)
|
||||
return tenant_ids
|
||||
|
||||
|
||||
class FirewallAgentApi(object):
|
||||
"""Plugin side of plugin to agent RPC API."""
|
||||
|
||||
def __init__(self, topic, host):
|
||||
self.host = host
|
||||
target = oslo_messaging.Target(topic=topic, version='1.0')
|
||||
self.client = n_rpc.get_client(target)
|
||||
|
||||
def _prepare_rpc_client(self, host=None):
|
||||
if host:
|
||||
return self.client.prepare(server=host)
|
||||
else:
|
||||
# historical behaviour (RPC broadcast)
|
||||
return self.client.prepare(fanout=True)
|
||||
|
||||
def create_firewall(self, context, firewall, host=None):
|
||||
cctxt = self._prepare_rpc_client(host)
|
||||
# TODO(blallau) host param is not used on agent side (to be removed)
|
||||
cctxt.cast(context, 'create_firewall', firewall=firewall,
|
||||
host=self.host)
|
||||
|
||||
def update_firewall(self, context, firewall, host=None):
|
||||
cctxt = self._prepare_rpc_client(host)
|
||||
# TODO(blallau) host param is not used on agent side (to be removed)
|
||||
cctxt.cast(context, 'update_firewall', firewall=firewall,
|
||||
host=self.host)
|
||||
|
||||
def delete_firewall(self, context, firewall, host=None):
|
||||
cctxt = self._prepare_rpc_client(host)
|
||||
# TODO(blallau) host param is not used on agent side (to be removed)
|
||||
cctxt.cast(context, 'delete_firewall', firewall=firewall,
|
||||
host=self.host)
|
||||
|
||||
|
||||
class FirewallPlugin(
|
||||
firewall_db.Firewall_db_mixin,
|
||||
firewall_router_insertion_db.FirewallRouterInsertionDbMixin):
|
||||
|
||||
"""Implementation of the Neutron Firewall Service Plugin.
|
||||
|
||||
This class manages the workflow of FWaaS request/response.
|
||||
Most DB related works are implemented in class
|
||||
firewall_db.Firewall_db_mixin.
|
||||
"""
|
||||
supported_extension_aliases = ["fwaas", "fwaasrouterinsertion"]
|
||||
path_prefix = fw_ext.FIREWALL_PREFIX
|
||||
|
||||
def __init__(self):
|
||||
"""Do the initialization for the firewall service plugin here."""
|
||||
self.start_rpc_listeners()
|
||||
|
||||
self.agent_rpc = FirewallAgentApi(
|
||||
f_const.FW_AGENT,
|
||||
cfg.CONF.host
|
||||
)
|
||||
firewall_db.subscribe()
|
||||
|
||||
def start_rpc_listeners(self):
|
||||
self.endpoints = [FirewallCallbacks(self)]
|
||||
|
||||
self.conn = n_rpc.create_connection()
|
||||
self.conn.create_consumer(
|
||||
f_const.FIREWALL_PLUGIN, self.endpoints, fanout=False)
|
||||
return self.conn.consume_in_threads()
|
||||
|
||||
def _get_hosts_to_notify(self, context, router_ids):
|
||||
"""Returns all hosts to send notification about firewall update"""
|
||||
l3_plugin = directory.get_plugin(plugin_constants.L3)
|
||||
no_broadcast = (
|
||||
n_utils.is_extension_supported(
|
||||
l3_plugin, nl_constants.L3_AGENT_SCHEDULER_EXT_ALIAS) and
|
||||
getattr(l3_plugin, 'get_l3_agents_hosting_routers', False))
|
||||
if no_broadcast:
|
||||
agents = l3_plugin.get_l3_agents_hosting_routers(
|
||||
context, router_ids, admin_state_up=True, active=True)
|
||||
return [a.host for a in agents]
|
||||
|
||||
# NOTE(blallau): default: FirewallAgentAPI performs RPC broadcast
|
||||
return [None]
|
||||
|
||||
def _rpc_update_firewall(self, context, firewall_id):
|
||||
status_update = {"firewall": {"status": nl_constants.PENDING_UPDATE}}
|
||||
super(FirewallPlugin, self).update_firewall(context, firewall_id,
|
||||
status_update)
|
||||
fw_with_rules = self._make_firewall_dict_with_rules(context,
|
||||
firewall_id)
|
||||
# this is triggered on an update to fw rule or policy, no
|
||||
# change in associated routers.
|
||||
fw_update_rtrs = self.get_firewall_routers(context, firewall_id)
|
||||
fw_with_rules['add-router-ids'] = fw_update_rtrs
|
||||
fw_with_rules['del-router-ids'] = []
|
||||
|
||||
hosts = self._get_hosts_to_notify(context, fw_update_rtrs)
|
||||
for host in hosts:
|
||||
self.agent_rpc.update_firewall(context, fw_with_rules,
|
||||
host=host)
|
||||
|
||||
def _rpc_update_firewall_policy(self, context, firewall_policy_id):
|
||||
firewall_policy = self.get_firewall_policy(context, firewall_policy_id)
|
||||
if firewall_policy:
|
||||
for firewall_id in firewall_policy['firewall_list']:
|
||||
self._rpc_update_firewall(context, firewall_id)
|
||||
|
||||
def _ensure_update_firewall(self, context, firewall_id):
|
||||
fwall = self.get_firewall(context, firewall_id)
|
||||
if fwall['status'] in [nl_constants.PENDING_CREATE,
|
||||
nl_constants.PENDING_UPDATE,
|
||||
nl_constants.PENDING_DELETE]:
|
||||
raise f_exc.FirewallInPendingState(firewall_id=firewall_id,
|
||||
pending_state=fwall['status'])
|
||||
|
||||
def _ensure_update_firewall_policy(self, context, firewall_policy_id):
|
||||
firewall_policy = self.get_firewall_policy(context, firewall_policy_id)
|
||||
if firewall_policy and 'firewall_list' in firewall_policy:
|
||||
for firewall_id in firewall_policy['firewall_list']:
|
||||
self._ensure_update_firewall(context, firewall_id)
|
||||
|
||||
def _ensure_update_firewall_rule(self, context, firewall_rule_id):
|
||||
fw_rule = self.get_firewall_rule(context, firewall_rule_id)
|
||||
if 'firewall_policy_id' in fw_rule and fw_rule['firewall_policy_id']:
|
||||
self._ensure_update_firewall_policy(context,
|
||||
fw_rule['firewall_policy_id'])
|
||||
|
||||
def _get_routers_for_create_firewall(self, tenant_id, context, firewall):
|
||||
# pop router_id as this goes in the router association db
|
||||
# and not firewall db
|
||||
router_ids = firewall['firewall'].pop('router_ids', None)
|
||||
if router_ids == nl_constants.ATTR_NOT_SPECIFIED:
|
||||
# old semantics router-ids keyword not specified pick up
|
||||
# all routers on tenant.
|
||||
l3_plugin = directory.get_plugin(plugin_constants.L3)
|
||||
ctx = neutron_context.get_admin_context()
|
||||
routers = l3_plugin.get_routers(ctx)
|
||||
router_ids = [
|
||||
router['id']
|
||||
for router in routers
|
||||
if router['tenant_id'] == tenant_id]
|
||||
# validation can still fail this if there is another fw
|
||||
# which is associated with one of these routers.
|
||||
self.validate_firewall_routers_not_in_use(context, router_ids)
|
||||
return router_ids
|
||||
else:
|
||||
if not router_ids:
|
||||
# This indicates that user specifies no routers.
|
||||
return []
|
||||
else:
|
||||
# some router(s) provided.
|
||||
self.validate_firewall_routers_not_in_use(context, router_ids)
|
||||
return router_ids
|
||||
|
||||
def create_firewall(self, context, firewall):
|
||||
LOG.debug("create_firewall() called")
|
||||
|
||||
fw_new_rtrs = self._get_routers_for_create_firewall(
|
||||
firewall['firewall']['tenant_id'], context, firewall)
|
||||
|
||||
if not fw_new_rtrs:
|
||||
# no messaging to agent needed, and fw needs to go
|
||||
# to INACTIVE(no associated rtrs) state.
|
||||
status = nl_constants.INACTIVE
|
||||
fw = super(FirewallPlugin, self).create_firewall(
|
||||
context, firewall, status)
|
||||
fw['router_ids'] = []
|
||||
return fw
|
||||
else:
|
||||
fw = super(FirewallPlugin, self).create_firewall(
|
||||
context, firewall)
|
||||
fw['router_ids'] = fw_new_rtrs
|
||||
|
||||
fw_with_rules = (
|
||||
self._make_firewall_dict_with_rules(context, fw['id']))
|
||||
|
||||
fw_with_rtrs = {'fw_id': fw['id'],
|
||||
'router_ids': fw_new_rtrs}
|
||||
self.set_routers_for_firewall(context, fw_with_rtrs)
|
||||
fw_with_rules['add-router-ids'] = fw_new_rtrs
|
||||
fw_with_rules['del-router-ids'] = []
|
||||
|
||||
hosts = self._get_hosts_to_notify(context, fw_new_rtrs)
|
||||
for host in hosts:
|
||||
self.agent_rpc.create_firewall(context, fw_with_rules,
|
||||
host=host)
|
||||
return fw
|
||||
|
||||
def update_firewall(self, context, id, firewall):
|
||||
LOG.debug("update_firewall() called on firewall %s", id)
|
||||
|
||||
self._ensure_update_firewall(context, id)
|
||||
# pop router_id as this goes in the router association db
|
||||
# and not firewall db
|
||||
router_ids = firewall['firewall'].pop('router_ids', None)
|
||||
fw_current_rtrs = fw_new_rtrs = self.get_firewall_routers(
|
||||
context, id)
|
||||
if router_ids is not None:
|
||||
if router_ids == []:
|
||||
# This indicates that user is indicating no routers.
|
||||
fw_new_rtrs = []
|
||||
else:
|
||||
self.validate_firewall_routers_not_in_use(
|
||||
context, router_ids, id)
|
||||
fw_new_rtrs = router_ids
|
||||
self.update_firewall_routers(context, {'fw_id': id,
|
||||
'router_ids': fw_new_rtrs})
|
||||
|
||||
if not fw_new_rtrs and not fw_current_rtrs:
|
||||
# no messaging to agent needed, and we need to continue
|
||||
# in INACTIVE state
|
||||
firewall['firewall']['status'] = nl_constants.INACTIVE
|
||||
fw = super(FirewallPlugin, self).update_firewall(
|
||||
context, id, firewall)
|
||||
fw['router_ids'] = []
|
||||
return fw
|
||||
else:
|
||||
firewall['firewall']['status'] = nl_constants.PENDING_UPDATE
|
||||
fw = super(FirewallPlugin, self).update_firewall(
|
||||
context, id, firewall)
|
||||
fw['router_ids'] = fw_new_rtrs
|
||||
|
||||
fw_with_rules = (
|
||||
self._make_firewall_dict_with_rules(context, fw['id']))
|
||||
|
||||
# determine rtrs to add fw to and del from
|
||||
fw_with_rules['add-router-ids'] = fw_new_rtrs
|
||||
fw_with_rules['del-router-ids'] = list(
|
||||
set(fw_current_rtrs).difference(set(fw_new_rtrs)))
|
||||
|
||||
# last-router drives agent to ack with status to set state to INACTIVE
|
||||
fw_with_rules['last-router'] = not fw_new_rtrs
|
||||
|
||||
LOG.debug("update_firewall %s: Add Routers: %s, Del Routers: %s",
|
||||
fw['id'],
|
||||
fw_with_rules['add-router-ids'],
|
||||
fw_with_rules['del-router-ids'])
|
||||
|
||||
hosts = self._get_hosts_to_notify(context, list(
|
||||
set(fw_new_rtrs).union(set(fw_current_rtrs))))
|
||||
for host in hosts:
|
||||
self.agent_rpc.update_firewall(context, fw_with_rules,
|
||||
host=host)
|
||||
return fw
|
||||
|
||||
def delete_db_firewall_object(self, context, id):
|
||||
super(FirewallPlugin, self).delete_firewall(context, id)
|
||||
|
||||
def delete_firewall(self, context, id):
|
||||
LOG.debug("delete_firewall() called on firewall %s", id)
|
||||
fw_with_rules = (
|
||||
self._make_firewall_dict_with_rules(context, id))
|
||||
fw_delete_rtrs = self.get_firewall_routers(context, id)
|
||||
fw_with_rules['del-router-ids'] = fw_delete_rtrs
|
||||
fw_with_rules['add-router-ids'] = []
|
||||
if not fw_with_rules['del-router-ids']:
|
||||
# no routers to delete on the agent side
|
||||
self.delete_db_firewall_object(context, id)
|
||||
else:
|
||||
status = {"firewall": {"status": nl_constants.PENDING_DELETE}}
|
||||
super(FirewallPlugin, self).update_firewall(context, id, status)
|
||||
# Reflect state change in fw_with_rules
|
||||
fw_with_rules['status'] = status['firewall']['status']
|
||||
hosts = self._get_hosts_to_notify(context, fw_delete_rtrs)
|
||||
if hosts:
|
||||
for host in hosts:
|
||||
self.agent_rpc.delete_firewall(context, fw_with_rules,
|
||||
host=host)
|
||||
else:
|
||||
# NOTE(blallau): we directly delete the firewall
|
||||
# if router is not associated to an agent
|
||||
self.delete_db_firewall_object(context, id)
|
||||
|
||||
def update_firewall_policy(self, context, id, firewall_policy):
|
||||
LOG.debug("update_firewall_policy() called")
|
||||
self._ensure_update_firewall_policy(context, id)
|
||||
fwp = super(FirewallPlugin,
|
||||
self).update_firewall_policy(context, id, firewall_policy)
|
||||
self._rpc_update_firewall_policy(context, id)
|
||||
return fwp
|
||||
|
||||
def update_firewall_rule(self, context, id, firewall_rule):
|
||||
LOG.debug("update_firewall_rule() called")
|
||||
self._ensure_update_firewall_rule(context, id)
|
||||
fwr = super(FirewallPlugin,
|
||||
self).update_firewall_rule(context, id, firewall_rule)
|
||||
firewall_policy_id = fwr['firewall_policy_id']
|
||||
if firewall_policy_id:
|
||||
self._rpc_update_firewall_policy(context, firewall_policy_id)
|
||||
return fwr
|
||||
|
||||
def _notify_firewall_updates(self, context, resource, update_info):
|
||||
notifier = n_rpc.get_notifier('network')
|
||||
notifier.info(context, resource, update_info)
|
||||
|
||||
def insert_rule(self, context, id, rule_info):
|
||||
LOG.debug("insert_rule() called")
|
||||
self._ensure_update_firewall_policy(context, id)
|
||||
fwp = super(FirewallPlugin,
|
||||
self).insert_rule(context, id, rule_info)
|
||||
self._rpc_update_firewall_policy(context, id)
|
||||
resource = 'firewall_policy.update.insert_rule'
|
||||
self._notify_firewall_updates(context, resource, rule_info)
|
||||
return fwp
|
||||
|
||||
def remove_rule(self, context, id, rule_info):
|
||||
LOG.debug("remove_rule() called")
|
||||
self._ensure_update_firewall_policy(context, id)
|
||||
fwp = super(FirewallPlugin,
|
||||
self).remove_rule(context, id, rule_info)
|
||||
self._rpc_update_firewall_policy(context, id)
|
||||
resource = 'firewall_policy.update.remove_rule'
|
||||
self._notify_firewall_updates(context, resource, rule_info)
|
||||
return fwp
|
||||
|
||||
def get_firewalls(self, context, filters=None, fields=None):
|
||||
LOG.debug("fwaas get_firewalls() called")
|
||||
has_id_field = not fields or 'id' in fields
|
||||
if not has_id_field:
|
||||
fields = fields + ['id']
|
||||
fw_list = super(FirewallPlugin, self).get_firewalls(
|
||||
context, filters, fields)
|
||||
if not fields or 'router_ids' in fields:
|
||||
for fw in fw_list:
|
||||
fw['router_ids'] = self.get_firewall_routers(context, fw['id'])
|
||||
if not has_id_field:
|
||||
for fw in fw_list:
|
||||
del fw['id']
|
||||
return fw_list
|
||||
|
||||
def get_firewall(self, context, id, fields=None):
|
||||
LOG.debug("fwaas get_firewall() called")
|
||||
res = super(FirewallPlugin, self).get_firewall(
|
||||
context, id, fields)
|
||||
fw_current_rtrs = self.get_firewall_routers(context, id)
|
||||
res['router_ids'] = fw_current_rtrs
|
||||
return res
|
|
@ -1,392 +0,0 @@
|
|||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutron_lib import context as neutron_context
|
||||
from neutron_lib.exceptions import firewall_v2 as f_exc
|
||||
from neutron_lib.plugins import directory
|
||||
|
||||
from neutron.common import rpc as n_rpc
|
||||
from neutron_lib import constants as nl_constants
|
||||
from neutron_lib.plugins import constants as plugin_const
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
import oslo_messaging
|
||||
|
||||
from neutron.db import servicetype_db as st_db
|
||||
from neutron.services import provider_configuration as provider_conf
|
||||
|
||||
from neutron_fwaas._i18n import _LI
|
||||
from neutron_fwaas.common import fwaas_constants
|
||||
from neutron_fwaas.db.firewall.v2 import firewall_db_v2
|
||||
from neutron_fwaas.extensions import firewall_v2 as fw_ext
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def add_provider_configuration(type_manager, service_type):
|
||||
type_manager.add_provider_configuration(
|
||||
service_type,
|
||||
provider_conf.ProviderConfiguration('neutron_fwaas'))
|
||||
|
||||
|
||||
class FirewallAgentApi(object):
|
||||
"""Plugin side of plugin to agent RPC API."""
|
||||
|
||||
def __init__(self, topic, host):
|
||||
self.host = host
|
||||
target = oslo_messaging.Target(topic=topic, version='1.0')
|
||||
self.client = n_rpc.get_client(target)
|
||||
|
||||
def create_firewall_group(self, context, firewall_group):
|
||||
cctxt = self.client.prepare(fanout=True)
|
||||
cctxt.cast(context, 'create_firewall_group',
|
||||
firewall_group=firewall_group,
|
||||
host=self.host)
|
||||
|
||||
def update_firewall_group(self, context, firewall_group):
|
||||
cctxt = self.client.prepare(fanout=True)
|
||||
cctxt.cast(context, 'update_firewall_group',
|
||||
firewall_group=firewall_group,
|
||||
host=self.host)
|
||||
|
||||
def delete_firewall_group(self, context, firewall_group):
|
||||
cctxt = self.client.prepare(fanout=True)
|
||||
cctxt.cast(context, 'delete_firewall_group',
|
||||
firewall_group=firewall_group,
|
||||
host=self.host)
|
||||
|
||||
|
||||
class FirewallCallbacks(object):
|
||||
target = oslo_messaging.Target(version='1.0')
|
||||
|
||||
def __init__(self, plugin):
|
||||
super(FirewallCallbacks, self).__init__()
|
||||
self.plugin = plugin
|
||||
|
||||
def set_firewall_group_status(self, context, fwg_id, status, **kwargs):
|
||||
"""Agent uses this to set a firewall_group's status."""
|
||||
LOG.debug("Setting firewall_group %s to status: %s",
|
||||
fwg_id, status)
|
||||
# Sanitize status first
|
||||
if status in (nl_constants.ACTIVE, nl_constants.DOWN,
|
||||
nl_constants.INACTIVE):
|
||||
to_update = status
|
||||
else:
|
||||
to_update = nl_constants.ERROR
|
||||
# ignore changing status if firewall_group expects to be deleted
|
||||
# That case means that while some pending operation has been
|
||||
# performed on the backend, neutron server received delete request
|
||||
# and changed firewall status to PENDING_DELETE
|
||||
updated = self.plugin.update_firewall_group_status(
|
||||
context, fwg_id, to_update, not_in=(nl_constants.PENDING_DELETE,))
|
||||
if updated:
|
||||
LOG.debug("firewall %s status set: %s", fwg_id, to_update)
|
||||
return updated and to_update != nl_constants.ERROR
|
||||
|
||||
def firewall_group_deleted(self, context, fwg_id, **kwargs):
|
||||
"""Agent uses this to indicate firewall is deleted."""
|
||||
LOG.debug("firewall_group_deleted() called")
|
||||
try:
|
||||
with context.session.begin(subtransactions=True):
|
||||
fwg_db = self.plugin._get_firewall_group(context, fwg_id)
|
||||
# allow to delete firewalls in ERROR state
|
||||
if fwg_db.status in (nl_constants.PENDING_DELETE,
|
||||
nl_constants.ERROR):
|
||||
self.plugin.delete_db_firewall_group_object(context,
|
||||
fwg_id)
|
||||
return True
|
||||
else:
|
||||
LOG.warning(('Firewall %(fwg)s unexpectedly deleted by '
|
||||
'agent, status was %(status)s'),
|
||||
{'fwg': fwg_id, 'status': fwg_db.status})
|
||||
fwg_db.update({"status": nl_constants.ERROR})
|
||||
return False
|
||||
except f_exc.FirewallGroupNotFound:
|
||||
LOG.info(_LI('Firewall group %s already deleted'), fwg_id)
|
||||
return True
|
||||
|
||||
def get_firewall_groups_for_project(self, context, **kwargs):
|
||||
"""Gets all firewall_groups and rules on a project."""
|
||||
LOG.debug("get_firewall_groups_for_project() called")
|
||||
fwg_list = []
|
||||
for fwg in self.plugin.get_firewall_groups(context):
|
||||
fwg_with_rules = self.plugin._make_firewall_group_dict_with_rules(
|
||||
context, fwg['id'])
|
||||
if fwg['status'] == nl_constants.PENDING_DELETE:
|
||||
fwg_with_rules['add-port-ids'] = []
|
||||
fwg_with_rules['del-port-ids'] = (
|
||||
self.plugin._get_ports_in_firewall_group(context,
|
||||
fwg['id']))
|
||||
else:
|
||||
fwg_with_rules['add-port-ids'] = (
|
||||
self.plugin._get_ports_in_firewall_group(context,
|
||||
fwg['id']))
|
||||
fwg_with_rules['del-port-ids'] = []
|
||||
fwg_list.append(fwg_with_rules)
|
||||
return fwg_list
|
||||
|
||||
def get_projects_with_firewall_groups(self, context, **kwargs):
|
||||
"""Get all projects that have firewall_groups."""
|
||||
LOG.debug("get_projects_with_firewall_groups() called")
|
||||
ctx = neutron_context.get_admin_context()
|
||||
fwg_list = self.plugin.get_firewall_groups(ctx)
|
||||
fwg_project_list = list(set(fwg['tenant_id'] for fwg in fwg_list))
|
||||
return fwg_project_list
|
||||
|
||||
|
||||
class FirewallPluginV2(
|
||||
firewall_db_v2.Firewall_db_mixin_v2):
|
||||
"""Implementation of the Neutron Firewall Service Plugin.
|
||||
|
||||
This class manages the workflow of FWaaS request/response.
|
||||
Most DB related works are implemented in class
|
||||
firewall_db_v2.Firewall_db_mixin_v2.
|
||||
"""
|
||||
supported_extension_aliases = ["fwaas_v2"]
|
||||
path_prefix = fw_ext.FIREWALL_PREFIX
|
||||
|
||||
def __init__(self):
|
||||
"""Do the initialization for the firewall service plugin here."""
|
||||
self.service_type_manager = st_db.ServiceTypeManager.get_instance()
|
||||
add_provider_configuration(
|
||||
self.service_type_manager, plugin_const.FIREWALL)
|
||||
self.start_rpc_listeners()
|
||||
|
||||
self.agent_rpc = FirewallAgentApi(
|
||||
fwaas_constants.FW_AGENT,
|
||||
cfg.CONF.host
|
||||
)
|
||||
|
||||
@property
|
||||
def _core_plugin(self):
|
||||
return directory.get_plugin()
|
||||
|
||||
def start_rpc_listeners(self):
|
||||
self.endpoints = [FirewallCallbacks(self)]
|
||||
|
||||
self.conn = n_rpc.create_connection()
|
||||
self.conn.create_consumer(
|
||||
fwaas_constants.FIREWALL_PLUGIN, self.endpoints, fanout=False)
|
||||
return self.conn.consume_in_threads()
|
||||
|
||||
def _rpc_update_firewall_group(self, context, fwg_id):
|
||||
status_update = {"firewall_group": {"status":
|
||||
nl_constants.PENDING_UPDATE}}
|
||||
super(FirewallPluginV2, self).update_firewall_group(
|
||||
context, fwg_id, status_update)
|
||||
fwg_with_rules = self._make_firewall_group_dict_with_rules(context,
|
||||
fwg_id)
|
||||
# this is triggered on an update to fwg rule or policy, no
|
||||
# change in associated ports.
|
||||
fwg_with_rules['add-port-ids'] = self._get_ports_in_firewall_group(
|
||||
context, fwg_id)
|
||||
fwg_with_rules['del-port-ids'] = []
|
||||
self.agent_rpc.update_firewall_group(context, fwg_with_rules)
|
||||
|
||||
def _rpc_update_firewall_policy(self, context, firewall_policy_id):
|
||||
firewall_policy = self.get_firewall_policy(context, firewall_policy_id)
|
||||
if firewall_policy:
|
||||
ing_fwg_ids, eg_fwg_ids = self._get_fwgs_with_policy(context,
|
||||
firewall_policy_id)
|
||||
for fwg_id in list(set(ing_fwg_ids + eg_fwg_ids)):
|
||||
self._rpc_update_firewall_group(context, fwg_id)
|
||||
|
||||
def _ensure_update_firewall_group(self, context, fwg_id):
|
||||
fwg = self.get_firewall_group(context, fwg_id)
|
||||
if fwg['status'] in [nl_constants.PENDING_CREATE,
|
||||
nl_constants.PENDING_UPDATE,
|
||||
nl_constants.PENDING_DELETE]:
|
||||
raise f_exc.FirewallGroupInPendingState(firewall_id=fwg_id,
|
||||
pending_state=fwg['status'])
|
||||
|
||||
def _ensure_update_firewall_policy(self, context, firewall_policy_id):
|
||||
firewall_policy = self.get_firewall_policy(context, firewall_policy_id)
|
||||
if firewall_policy:
|
||||
ing_fwg_ids, eg_fwg_ids = self._get_fwgs_with_policy(context,
|
||||
firewall_policy_id)
|
||||
for fwg_id in list(set(ing_fwg_ids + eg_fwg_ids)):
|
||||
self._ensure_update_firewall_group(context, fwg_id)
|
||||
|
||||
def _ensure_update_firewall_rule(self, context, fwr_id):
|
||||
fwp_ids = self._get_policies_with_rule(context, fwr_id)
|
||||
for fwp_id in fwp_ids:
|
||||
self._ensure_update_firewall_policy(context, fwp_id)
|
||||
|
||||
def _validate_ports_for_firewall_group(self, context, tenant_id,
|
||||
fwg_ports):
|
||||
# TODO(sridar): elevated context and do we want to use public ?
|
||||
for port_id in fwg_ports:
|
||||
port_db = self._core_plugin._get_port(context, port_id)
|
||||
if port_db['device_owner'] != "network:router_interface":
|
||||
raise f_exc.FirewallGroupPortInvalid(port_id=port_id)
|
||||
if port_db['tenant_id'] != tenant_id:
|
||||
raise f_exc.FirewallGroupPortInvalidProject(
|
||||
port_id=port_id, tenant_id=port_db['tenant_id'])
|
||||
return
|
||||
|
||||
def _check_no_need_pending(self, context, fwg_id, fwg_body):
|
||||
fwg_db = self._get_firewall_group(context, fwg_id)
|
||||
fwp_req_in = fwg_body.get('ingress_firewall_policy_id', None)
|
||||
fwp_req_eg = fwg_body.get('egress_firewall_policy_id', None)
|
||||
|
||||
if ((not fwg_db.ingress_firewall_policy_id and
|
||||
fwp_req_in is fwg_db.ingress_firewall_policy_id) and
|
||||
(not fwg_db.egress_firewall_policy_id and
|
||||
fwp_req_eg is fwg_db.ingress_firewall_policy_id)):
|
||||
return True
|
||||
return False
|
||||
|
||||
def create_firewall_group(self, context, firewall_group):
|
||||
LOG.debug("create_firewall_group() called")
|
||||
fwgrp = firewall_group['firewall_group']
|
||||
fwg_ports = fwgrp['ports']
|
||||
if not fwg_ports:
|
||||
# no messaging to agent needed, and fw needs to go
|
||||
# to INACTIVE(no associated ports) state.
|
||||
status = nl_constants.INACTIVE
|
||||
fwg = super(FirewallPluginV2, self).create_firewall_group(
|
||||
context, firewall_group, status)
|
||||
fwg['ports'] = []
|
||||
return fwg
|
||||
else:
|
||||
# Validate ports
|
||||
self._validate_ports_for_firewall_group(context,
|
||||
firewall_group['firewall_group']['tenant_id'],
|
||||
fwg_ports)
|
||||
self._validate_if_firewall_group_on_ports(context, fwg_ports)
|
||||
|
||||
if (not fwgrp['ingress_firewall_policy_id'] and
|
||||
not fwgrp['egress_firewall_policy_id']):
|
||||
# No policy configured
|
||||
status = nl_constants.INACTIVE
|
||||
fwg = super(FirewallPluginV2, self).create_firewall_group(
|
||||
context, firewall_group, status)
|
||||
return fwg
|
||||
|
||||
fwg = super(FirewallPluginV2, self).create_firewall_group(
|
||||
context, firewall_group)
|
||||
fwg['ports'] = fwg_ports
|
||||
|
||||
fwg_with_rules = (
|
||||
self._make_firewall_group_dict_with_rules(context, fwg['id']))
|
||||
|
||||
fwg_with_rules['add-port-ids'] = fwg_ports
|
||||
fwg_with_rules['del-ports-ids'] = []
|
||||
|
||||
self.agent_rpc.create_firewall_group(context, fwg_with_rules)
|
||||
|
||||
return fwg
|
||||
|
||||
def update_firewall_group(self, context, id, firewall_group):
|
||||
LOG.debug("update_firewall_group() called on firewall_group %s", id)
|
||||
|
||||
self._ensure_update_firewall_group(context, id)
|
||||
|
||||
# TODO(sridar): need closure on status when no policy associated.
|
||||
fwg_current_ports = fwg_new_ports = self._get_ports_in_firewall_group(
|
||||
context, id)
|
||||
if 'ports' in firewall_group['firewall_group']:
|
||||
fwg_ports = firewall_group['firewall_group']['ports']
|
||||
if fwg_ports == []:
|
||||
# This indicates that user is indicating no ports.
|
||||
fwg_new_ports = []
|
||||
else:
|
||||
self._validate_ports_for_firewall_group(
|
||||
context, context.tenant_id, fwg_ports)
|
||||
self._validate_if_firewall_group_on_ports(
|
||||
context, fwg_ports, id)
|
||||
fwg_new_ports = fwg_ports
|
||||
|
||||
if ((not fwg_new_ports and not fwg_current_ports) or
|
||||
self._check_no_need_pending(context,
|
||||
id, firewall_group['firewall_group'])):
|
||||
# no messaging to agent needed, and we need to continue
|
||||
# in INACTIVE state
|
||||
firewall_group['firewall_group']['status'] = nl_constants.INACTIVE
|
||||
fwg = super(FirewallPluginV2, self).update_firewall_group(
|
||||
context, id, firewall_group)
|
||||
if fwg_new_ports:
|
||||
fwg['ports'] = fwg_new_ports
|
||||
elif not fwg_new_ports and fwg_current_ports:
|
||||
fwg['ports'] = fwg_current_ports
|
||||
else:
|
||||
fwg['ports'] = []
|
||||
return fwg
|
||||
else:
|
||||
firewall_group['firewall_group']['status'] = (nl_constants.
|
||||
PENDING_UPDATE)
|
||||
fwg = super(FirewallPluginV2, self).update_firewall_group(
|
||||
context, id, firewall_group)
|
||||
fwg['ports'] = fwg_new_ports
|
||||
|
||||
fwg_with_rules = (
|
||||
self._make_firewall_group_dict_with_rules(context, fwg['id']))
|
||||
|
||||
# determine ports to add fw to and del from
|
||||
fwg_with_rules['add-port-ids'] = fwg_new_ports
|
||||
fwg_with_rules['del-port-ids'] = list(
|
||||
set(fwg_current_ports).difference(set(fwg_new_ports)))
|
||||
|
||||
# last-port drives agent to ack with status to set state to INACTIVE
|
||||
fwg_with_rules['last-port'] = not fwg_new_ports
|
||||
|
||||
LOG.debug("update_firewall_group %s: Add Ports: %s, Del Ports: %s",
|
||||
fwg['id'],
|
||||
fwg_with_rules['add-port-ids'],
|
||||
fwg_with_rules['del-port-ids'])
|
||||
|
||||
self.agent_rpc.update_firewall_group(context, fwg_with_rules)
|
||||
|
||||
return fwg
|
||||
|
||||
def delete_db_firewall_group_object(self, context, id):
|
||||
super(FirewallPluginV2, self).delete_firewall_group(context, id)
|
||||
|
||||
def delete_firewall_group(self, context, id):
|
||||
LOG.debug("delete_firewall_group() called on firewall_group %s", id)
|
||||
fw_with_rules = (
|
||||
self._make_firewall_group_dict_with_rules(context, id))
|
||||
fw_with_rules['del-port-ids'] = self._get_ports_in_firewall_group(
|
||||
context, id)
|
||||
fw_with_rules['add-port-ids'] = []
|
||||
if not fw_with_rules['del-port-ids']:
|
||||
# no ports, no need to talk to the agent
|
||||
self.delete_db_firewall_group_object(context, id)
|
||||
else:
|
||||
status = {"firewall_group":
|
||||
{"status": nl_constants.PENDING_DELETE}}
|
||||
super(FirewallPluginV2, self).update_firewall_group(
|
||||
context, id, status)
|
||||
# Reflect state change in fw_with_rules
|
||||
fw_with_rules['status'] = status['firewall_group']['status']
|
||||
self.agent_rpc.delete_firewall_group(context, fw_with_rules)
|
||||
|
||||
def update_firewall_policy(self, context, id, firewall_policy):
|
||||
LOG.debug("update_firewall_policy() called")
|
||||
self._ensure_update_firewall_policy(context, id)
|
||||
fwp = super(FirewallPluginV2,
|
||||
self).update_firewall_policy(context, id, firewall_policy)
|
||||
self._rpc_update_firewall_policy(context, id)
|
||||
return fwp
|
||||
|
||||
def update_firewall_rule(self, context, id, firewall_rule):
|
||||
LOG.debug("update_firewall_rule() called")
|
||||
self._ensure_update_firewall_rule(context, id)
|
||||
fwr = super(FirewallPluginV2,
|
||||
self).update_firewall_rule(context, id, firewall_rule)
|
||||
fwp_ids = self._get_policies_with_rule(context, id)
|
||||
for fwp_id in fwp_ids:
|
||||
self._rpc_update_firewall_policy(context, fwp_id)
|
||||
return fwr
|
|
@ -1,44 +0,0 @@
|
|||
# Copyright 2014 OpenStack Foundation.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
import os
|
||||
|
||||
from neutron.common import test_lib
|
||||
from neutron.tests import base as n_base
|
||||
from neutron.tests.unit.db import test_db_base_plugin_v2 as test_db_plugin
|
||||
|
||||
|
||||
class BaseTestCase(n_base.BaseTestCase):
|
||||
pass
|
||||
|
||||
|
||||
class NeutronDbPluginV2TestCase(test_db_plugin.NeutronDbPluginV2TestCase):
|
||||
|
||||
def setup_config(self):
|
||||
## Copied from neutron's test_db_base_plugin_v2 because they
|
||||
## don't allow to specify args
|
||||
|
||||
# Create the default configurations
|
||||
args = ['--config-file', n_base.etcdir('neutron.conf')]
|
||||
# If test_config specifies some config-file, use it, as well
|
||||
for config_file in test_lib.test_config.get('config_files', []):
|
||||
args.extend(['--config-file', config_file])
|
||||
|
||||
## our own stuff
|
||||
dirpath = os.path.join(os.path.dirname(__file__),
|
||||
'./../../etc/neutron/policy.d')
|
||||
args.extend(['--config-dir', dirpath])
|
||||
self.config_parse(args=args)
|
|
@ -1,3 +0,0 @@
|
|||
The files in this directory are intended for use by the
|
||||
infra jobs that run the various functional test
|
||||
suite in the gate for the neutron-fwaas repo.
|
|
@ -1,20 +0,0 @@
|
|||
# neutron-rootwrap command filters to support functional testing. It
|
||||
# is NOT intended to be used outside of a test environment.
|
||||
#
|
||||
# This file should be owned by (and only-writeable by) the root user
|
||||
|
||||
[Filters]
|
||||
# '$BASE_PATH' is intended to be replaced with the expected tox path
|
||||
# (e.g. /opt/stack/new/neutron/.tox/dsvm-functional) by the neutron
|
||||
# functional jenkins job. This ensures that tests can kill the
|
||||
# processes that they launch with their containing tox environment's
|
||||
# python.
|
||||
kill_tox_python: KillFilter, root, $BASE_PATH/bin/python, -9
|
||||
|
||||
# enable ping from namespace
|
||||
ping_filter: CommandFilter, ping, root
|
||||
|
||||
# enable curl from namespace
|
||||
curl_filter: CommandFilter, curl, root
|
||||
tee_filter: CommandFilter, tee, root
|
||||
tee_kill: KillFilter, root, tee, -9
|
|
@ -1,7 +0,0 @@
|
|||
# neutron-rootwrap command filters to support functional testing. It
|
||||
# is NOT intended to be used outside of a test environment.
|
||||
#
|
||||
# This file should be owned by (and only-writeable by) the root user
|
||||
|
||||
[Filters]
|
||||
#none currently
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue