neutron-fwaas-12.0.2/0000775000175000017500000000000013555600005014406 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/PKG-INFO0000664000175000017500000000350313555600005015504 0ustar zuulzuul00000000000000Metadata-Version: 1.1 Name: neutron-fwaas Version: 12.0.2 Summary: OpenStack Networking FWaaS Home-page: https://docs.openstack.org/neutron-fwaas/latest/ Author: OpenStack Author-email: openstack-dev@lists.openstack.org License: UNKNOWN Description: ======================== Team and repository tags ======================== .. image:: https://governance.openstack.org/tc/badges/neutron-fwaas.svg :target: https://governance.openstack.org/tc/reference/tags/index.html .. Change things from this point on 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: . Please refer to Neutron documentation for more information: `Neutron README.rst `_ Platform: UNKNOWN Classifier: Environment :: OpenStack Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.5 neutron-fwaas-12.0.2/README.rst0000664000175000017500000000154413555577713016124 0ustar zuulzuul00000000000000======================== Team and repository tags ======================== .. image:: https://governance.openstack.org/tc/badges/neutron-fwaas.svg :target: https://governance.openstack.org/tc/reference/tags/index.html .. Change things from this point on 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: . Please refer to Neutron documentation for more information: `Neutron README.rst `_ neutron-fwaas-12.0.2/.coveragerc0000664000175000017500000000014713555577713016554 0ustar zuulzuul00000000000000[run] branch = True source = neutron_fwaas omit = neutron_fwaas/tests/* [report] ignore_errors = True neutron-fwaas-12.0.2/tools/0000775000175000017500000000000013555600005015546 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/tools/generate_config_file_samples.sh0000775000175000017500000000144013555577713023771 0ustar zuulzuul00000000000000#!/bin/sh # # 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. set -e GEN_CMD=oslo-config-generator if ! type "$GEN_CMD" > /dev/null; then echo "ERROR: $GEN_CMD not installed on the system." exit 1 fi for file in `ls etc/oslo-config-generator/*`; do $GEN_CMD --config-file=$file done set -x neutron-fwaas-12.0.2/tools/clean.sh0000775000175000017500000000030413555577713017207 0ustar zuulzuul00000000000000#!/usr/bin/env bash rm -rf ./*.deb ./*.tar.gz ./*.dsc ./*.changes rm -rf */*.deb rm -rf ./plugins/**/build/ ./plugins/**/dist rm -rf ./plugins/**/lib/neutron_*_plugin.egg-info ./plugins/neutron-* neutron-fwaas-12.0.2/tools/deploy_rootwrap.sh0000775000175000017500000000404413555577713021363 0ustar zuulzuul00000000000000#!/usr/bin/env 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. set -eu if [ "$#" -ne 3 ]; then >&2 echo "Usage: $0 /path/to/neutron_fwaas /path/to/target/etc /path/to/target/bin Deploy Neutron FWaaS's rootwrap configuration. Warning: Any existing rootwrap files at the specified etc path will be removed by this script. Optional: set OS_SUDO_TESTING=1 to deploy the filters required by Neutron's functional testing suite." exit 1 fi OS_SUDO_TESTING=${OS_SUDO_TESTING:-0} neutron_path=${OS_NEUTRON_PATH} fwaas_path=$1 target_etc_path=$2 target_bin_path=$3 src_conf_path=${neutron_path}/etc src_conf=${src_conf_path}/rootwrap.conf src_rootwrap_path=${src_conf_path}/neutron/rootwrap.d fwaas_src_conf_path=${fwaas_path}/etc fwaas_src_rootwrap_path=${fwaas_src_conf_path}/neutron/rootwrap.d dst_conf_path=${target_etc_path}/neutron dst_conf=${dst_conf_path}/rootwrap.conf dst_rootwrap_path=${dst_conf_path}/rootwrap.d if [[ -d "$dst_rootwrap_path" ]]; then rm -rf ${dst_rootwrap_path} fi mkdir -p -m 755 ${dst_rootwrap_path} cp -p ${src_rootwrap_path}/* ${fwaas_src_rootwrap_path}/* ${dst_rootwrap_path}/ cp -p ${src_conf} ${dst_conf} sed -i "s:^filters_path=.*$:filters_path=${dst_rootwrap_path}:" ${dst_conf} sed -i "s:^\(exec_dirs=.*\)$:\1,${target_bin_path}:" ${dst_conf} if [[ "$OS_SUDO_TESTING" = "1" ]]; then sed -i 's/use_syslog=False/use_syslog=True/g' ${dst_conf} sed -i 's/syslog_log_level=ERROR/syslog_log_level=DEBUG/g' ${dst_conf} cp -p ${fwaas_path}/neutron_fwaas/tests/contrib/functional-testing.filters \ ${dst_rootwrap_path}/ fi neutron-fwaas-12.0.2/tools/ostestr_compat_shim.sh0000775000175000017500000000025113555577713022214 0ustar zuulzuul00000000000000#!/bin/sh # preserve old behavior of using an arg as a regex when '--' is not present case $@ in (*--*) ostestr $@;; ('') ostestr;; (*) ostestr --regex "$@" esac neutron-fwaas-12.0.2/tools/check_unit_test_structure.sh0000775000175000017500000000311513555577713023423 0ustar zuulzuul00000000000000#!/usr/bin/env bash # This script identifies the unit test modules that do not correspond # directly with a module in the code tree. See TESTING.rst for the # intended structure. neutron_path=$(cd "$(dirname "$0")/.." && pwd) base_test_path=neutron_fwaas/tests/unit test_path=$neutron_path/$base_test_path test_files=$(find ${test_path} -iname 'test_*.py') ignore_regexes=( "^plugins.*$", "^misc.*$" ) error_count=0 ignore_count=0 total_count=0 for test_file in ${test_files[@]}; do relative_path=${test_file#$test_path/} expected_path=$(dirname $neutron_path/neutron_fwaas/$relative_path) test_filename=$(basename "$test_file") expected_filename=${test_filename#test_} # Module filename (e.g. foo/bar.py -> foo/test_bar.py) filename=$expected_path/$expected_filename # Package dir (e.g. foo/ -> test_foo.py) package_dir=${filename%.py} if [ ! -f "$filename" ] && [ ! -d "$package_dir" ]; then for ignore_regex in ${ignore_regexes[@]}; do if [[ "$relative_path" =~ $ignore_regex ]]; then ((ignore_count++)) continue 2 fi done echo "Unexpected test file: $base_test_path/$relative_path" ((error_count++)) fi ((total_count++)) done if [ "$ignore_count" -ne 0 ]; then echo "$ignore_count unmatched test modules were ignored" fi if [ "$error_count" -eq 0 ]; then echo 'Success! All test modules match targets in the code tree.' exit 0 else echo "Failure! $error_count of $total_count test modules do not match targets in the code tree." exit 1 fi neutron-fwaas-12.0.2/tools/tox_install.sh0000775000175000017500000000457413555577713020502 0ustar zuulzuul00000000000000#!/usr/bin/env bash # Many of neutron's repos suffer from the problem of depending on neutron, # but it not existing on pypi. # This wrapper for tox's package installer will use the existing package # if it exists, else use zuul-cloner if that program exists, else grab it # from neutron master via a hard-coded URL. That last case should only # happen with devs running unit tests locally. # From the tox.ini config page: # install_command=ARGV # default: # pip install {opts} {packages} ZUUL_CLONER=/usr/zuul-env/bin/zuul-cloner BRANCH_NAME=stable/queens neutron_installed=$(echo "import neutron" | python 2>/dev/null ; echo $?) NEUTRON_DIR=$HOME/neutron set -e set -x install_cmd="pip install -c$1" shift PARAMS="$*" # The devstack based functional tests have neutron checked out in # $NEUTRON_DIR on the test systems - with the change to test in it. # Use this directory if it exists, so that this script installs the # neutron version to test here. # Note that the functional tests use sudo to run tox and thus # variables used for zuul-cloner to check out the correct version are # lost. if [ -d "$NEUTRON_DIR" ]; then echo "FOUND Neutron code at $NEUTRON_DIR - using" $install_cmd -U -e $NEUTRON_DIR elif [ $neutron_installed -eq 0 ]; then echo "ALREADY INSTALLED" > /tmp/tox_install.txt location=$(python -c "import neutron; print(neutron.__file__)") echo "ALREADY INSTALLED at $location" echo "Neutron already installed; using existing package" elif [ -x "$ZUUL_CLONER" ]; then echo "ZUUL CLONER" > /tmp/tox_install.txt # Make this relative to current working directory so that # git clean can remove it. We cannot remove the directory directly # since it is referenced after $install_cmd -e. mkdir -p .tmp NEUTRON_DIR=$(/bin/mktemp -d -p $(pwd)/.tmp) pushd $NEUTRON_DIR $ZUUL_CLONER --cache-dir \ /opt/git \ --branch $BRANCH_NAME \ https://git.openstack.org \ openstack/neutron cd openstack/neutron $install_cmd -e . popd else echo "PIP HARDCODE" > /tmp/tox_install.txt if [ -z "$NEUTRON_GIT_REPO" ]; then NEUTRON_GIT_REPO="https://git.openstack.org/openstack/neutron" fi SRC_DIR="$VIRTUAL_ENV/src/neutron" git clone --depth 1 --branch $BRANCH_NAME $NEUTRON_GIT_REPO $SRC_DIR $install_cmd -U -e $SRC_DIR fi if [ -n "${PARAMS}" ]; then $install_cmd -U ${PARAMS} fi exit $? neutron-fwaas-12.0.2/tools/configure_for_fwaas_func_testing.sh0000775000175000017500000000040313555577713024705 0ustar zuulzuul00000000000000set -e IS_GATE=${IS_GATE:-False} USE_CONSTRAINT_ENV=${USE_CONSTRAINT_ENV:-False} PROJECT_NAME=${PROJECT_NAME:-neutron-fwaas} REPO_BASE=${GATE_DEST:-$(cd $(dirname "$BASH_SOURCE")/../.. && pwd)} source $REPO_BASE/neutron/tools/configure_for_func_testing.sh neutron-fwaas-12.0.2/test-requirements.txt0000664000175000017500000000157713555577713020704 0ustar zuulzuul00000000000000# The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0 coverage!=4.4,>=4.0 # Apache-2.0 fixtures>=3.0.0 # Apache-2.0/BSD flake8-import-order==0.12 # LGPLv3 mock>=2.0.0 # BSD python-subunit>=1.0.0 # Apache-2.0/BSD requests-mock>=1.1.0 # Apache-2.0 sphinx!=1.6.6,>=1.6.2 # BSD openstackdocstheme>=1.18.1 # Apache-2.0 oslo.concurrency>=3.25.0 # Apache-2.0 os-testr>=1.0.0 # Apache-2.0 testrepository>=0.0.18 # Apache-2.0/BSD testresources>=2.0.0 # Apache-2.0/BSD testtools>=2.2.0 # MIT testscenarios>=0.4 # Apache-2.0/BSD WebOb>=1.7.1 # MIT WebTest>=2.0.27 # MIT oslotest>=3.2.0 # Apache-2.0 reno>=2.5.0 # Apache-2.0 PyMySQL>=0.7.6 # MIT License psycopg2>=2.6.2 # LGPL/ZPL doc8>=0.6.0 # Apache-2.0 neutron-fwaas-12.0.2/doc/0000775000175000017500000000000013555600005015153 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/doc/source/0000775000175000017500000000000013555600005016453 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/doc/source/contributor/0000775000175000017500000000000013555600005021025 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/doc/source/contributor/contributing.rst0000664000175000017500000000216513555577713024315 0ustar zuulzuul00000000000000============================= 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 `_ 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. neutron-fwaas-12.0.2/doc/source/contributor/devstack.rst0000664000175000017500000000005213555577713023403 0ustar zuulzuul00000000000000.. include:: ../../../devstack/README.rst neutron-fwaas-12.0.2/doc/source/contributor/modules.rst0000664000175000017500000000014513555577713023252 0ustar zuulzuul00000000000000================ Module Reference ================ .. toctree:: :maxdepth: 1 :glob: api/* neutron-fwaas-12.0.2/doc/source/contributor/index.rst0000664000175000017500000000037713555577713022720 0ustar zuulzuul00000000000000================= 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 neutron-fwaas-12.0.2/doc/source/contributor/fwaas_v2.rst0000664000175000017500000000042713555577713023315 0ustar zuulzuul00000000000000FireWall as a Service V2 ======================== The `FireWall as a Service API V2 `_ specification lists the changes that together compose FWaaS V2. These changes are not fully implemented. neutron-fwaas-12.0.2/doc/source/configuration/0000775000175000017500000000000013555600005021322 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/doc/source/configuration/samples/0000775000175000017500000000000013555600005022766 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/doc/source/configuration/samples/neutron_fwaas.rst0000664000175000017500000000042713555577713026421 0ustar zuulzuul00000000000000========================= Sample neutron_fwaas.conf ========================= This sample configuration can also be viewed in `the raw format <../../_static/config_samples/neutron_fwaas.conf.sample>`_. .. literalinclude:: ../../_static/config_samples/neutron_fwaas.conf.sample neutron-fwaas-12.0.2/doc/source/configuration/samples/fwaas_driver.rst0000664000175000017500000000041713555577713026221 0ustar zuulzuul00000000000000======================= Sample fwaas_driver.ini ======================= This sample configuration can also be viewed in `the raw format <../../_static/config_samples/fwaas_driver.conf.sample>`_. .. literalinclude:: ../../_static/config_samples/fwaas_driver.conf.sample neutron-fwaas-12.0.2/doc/source/configuration/index.rst0000664000175000017500000000121213555577713023202 0ustar zuulzuul00000000000000.. _configuring: ============================= Neutron Configuration Options ============================= This section provides a list of all possible options for each configuration file. Configuration Reference ----------------------- Neutron uses the following configuration files for its various services. .. toctree:: :glob: :maxdepth: 1 * Sample Configuration Files -------------------------- The following are sample configuration files for all Neutron services and utilities. These are generated from code and reflect the current state of code in the Neutron repository. .. toctree:: :glob: :maxdepth: 1 samples/* neutron-fwaas-12.0.2/doc/source/configuration/neutron_fwaas.rst0000664000175000017500000000021213555577713024745 0ustar zuulzuul00000000000000================== neutron_fwaas.conf ================== .. show-options:: :config-file: etc/oslo-config-generator/neutron_fwaas.conf neutron-fwaas-12.0.2/doc/source/configuration/fwaas_driver.rst0000664000175000017500000000020213555577713024545 0ustar zuulzuul00000000000000================ fwaas_driver.ini ================ .. show-options:: :config-file: etc/oslo-config-generator/fwaas_driver.ini neutron-fwaas-12.0.2/doc/source/conf.py0000664000175000017500000002077313555577713020006 0ustar zuulzuul00000000000000# -*- 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', 'oslo_config.sphinxext', 'oslo_config.sphinxconfiggen', 'openstackdocstheme',] todo_include_todos = True # Add any paths that contain templates here, relative to this directory. templates_path = [] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8' # The master toctree document. master_doc = 'index' # General information about the project. project = u'Neutron FWaaS' copyright = u'2011-present, OpenStack Foundation.' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # Version info from neutron_fwaas.version import version_info as neutron_fwaas_version release = neutron_fwaas_version.release_string() # The short X.Y version. version = neutron_fwaas_version.version_string() # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. # unused_docs = [] # List of directories, relative to source directory, that shouldn't be searched # for source files. exclude_trees = [] # The reST default role (for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. show_authors = True # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. modindex_common_prefix = ['neutron_fwaas.'] # -- Options for man page output -------------------------------------------- # Grouping the document tree for man pages. # List of tuples 'sourcefile', 'target', u'title', u'Authors name', 'manual' #man_pages = [ # ('man/neutron-server', 'neutron-server', u'Neutron Server', # [u'OpenStack'], 1) #] # -- Options for HTML output ------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. # html_theme_path = ["."] # html_theme = '_theme' 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 # " v 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 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' # -- Options for oslo_config.sphinxconfiggen --------------------------------- _config_generator_config_files = [ 'fwaas_driver.ini', 'neutron_fwaas.conf', ] def _get_config_generator_config_definition(config_file): config_file_path = '../../etc/oslo-config-generator/%s' % conf # oslo_config.sphinxconfiggen appends '.conf.sample' to the filename, # strip file extentension (.conf or .ini). output_file_path = '_static/config_samples/%s' % conf.rsplit('.', 1)[0] return (config_file_path, output_file_path) config_generator_config_file = [ _get_config_generator_config_definition(conf) for conf in _config_generator_config_files ] neutron-fwaas-12.0.2/doc/source/_static/0000775000175000017500000000000013555600005020101 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/doc/source/_static/.placeholder0000664000175000017500000000000013555577713022375 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/doc/source/index.rst0000664000175000017500000000050213555577713020334 0ustar zuulzuul00000000000000=========================== neutron-fwaas documentation =========================== General Information and Other Project References: .. toctree:: :glob: :maxdepth: 2 install/index configuration/index contributor/index .. rubric:: Indices and tables * :ref:`genindex` * :ref:`modindex` * :ref:`search` neutron-fwaas-12.0.2/doc/source/install/0000775000175000017500000000000013555600005020121 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/doc/source/install/index.rst0000664000175000017500000000260713555577713022012 0ustar zuulzuul00000000000000.. 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 `_ or the `Firewall-as-a-Service (FWaaS) v1 scenario `_. neutron-fwaas-12.0.2/neutron_fwaas.egg-info/0000775000175000017500000000000013555600005020753 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas.egg-info/PKG-INFO0000664000175000017500000000350313555600005022051 0ustar zuulzuul00000000000000Metadata-Version: 1.1 Name: neutron-fwaas Version: 12.0.2 Summary: OpenStack Networking FWaaS Home-page: https://docs.openstack.org/neutron-fwaas/latest/ Author: OpenStack Author-email: openstack-dev@lists.openstack.org License: UNKNOWN Description: ======================== Team and repository tags ======================== .. image:: https://governance.openstack.org/tc/badges/neutron-fwaas.svg :target: https://governance.openstack.org/tc/reference/tags/index.html .. Change things from this point on 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: . Please refer to Neutron documentation for more information: `Neutron README.rst `_ Platform: UNKNOWN Classifier: Environment :: OpenStack Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.5 neutron-fwaas-12.0.2/neutron_fwaas.egg-info/SOURCES.txt0000664000175000017500000002753513555600005022653 0ustar zuulzuul00000000000000.coveragerc .mailmap .pylintrc .testr.conf .zuul.yaml AUTHORS CONTRIBUTING.rst ChangeLog HACKING.rst LICENSE README.rst TESTING.rst babel.cfg requirements.txt setup.cfg setup.py test-requirements.txt tox.ini devstack/README.rst devstack/plugin.sh devstack/settings devstack/lib/l2_agent devstack/lib/l3_agent doc/source/conf.py doc/source/index.rst doc/source/_static/.placeholder doc/source/configuration/fwaas_driver.rst doc/source/configuration/index.rst doc/source/configuration/neutron_fwaas.rst doc/source/configuration/samples/fwaas_driver.rst doc/source/configuration/samples/neutron_fwaas.rst doc/source/contributor/contributing.rst doc/source/contributor/devstack.rst doc/source/contributor/fwaas_v2.rst doc/source/contributor/index.rst doc/source/contributor/modules.rst doc/source/install/index.rst etc/README.txt etc/neutron/policy.d/neutron-fwaas.json etc/neutron/rootwrap.d/fwaas-privsep.filters etc/oslo-config-generator/fwaas_driver.ini etc/oslo-config-generator/neutron_fwaas.conf neutron_fwaas/__init__.py neutron_fwaas/_i18n.py neutron_fwaas/opts.py neutron_fwaas/version.py neutron_fwaas.egg-info/PKG-INFO neutron_fwaas.egg-info/SOURCES.txt neutron_fwaas.egg-info/dependency_links.txt neutron_fwaas.egg-info/entry_points.txt neutron_fwaas.egg-info/not-zip-safe neutron_fwaas.egg-info/pbr.json neutron_fwaas.egg-info/requires.txt neutron_fwaas.egg-info/top_level.txt neutron_fwaas/common/__init__.py neutron_fwaas/common/exceptions.py neutron_fwaas/common/fwaas_constants.py neutron_fwaas/common/resources.py neutron_fwaas/db/__init__.py neutron_fwaas/db/firewall/__init__.py neutron_fwaas/db/firewall/firewall_db.py neutron_fwaas/db/firewall/firewall_router_insertion_db.py neutron_fwaas/db/firewall/v2/__init__.py neutron_fwaas/db/firewall/v2/firewall_db_v2.py neutron_fwaas/db/migration/__init__.py neutron_fwaas/db/migration/alembic_migrations/README neutron_fwaas/db/migration/alembic_migrations/env.py neutron_fwaas/db/migration/alembic_migrations/script.py.mako neutron_fwaas/db/migration/alembic_migrations/versions/4202e3047e47_add_index_tenant_id.py neutron_fwaas/db/migration/alembic_migrations/versions/540142f314f4_fwaas_router_insertion.py neutron_fwaas/db/migration/alembic_migrations/versions/796c68dffbb_cisco_csr_fwaas.py neutron_fwaas/db/migration/alembic_migrations/versions/CONTRACT_HEAD neutron_fwaas/db/migration/alembic_migrations/versions/EXPAND_HEAD neutron_fwaas/db/migration/alembic_migrations/versions/kilo_release.py neutron_fwaas/db/migration/alembic_migrations/versions/start_neutron_fwaas.py neutron_fwaas/db/migration/alembic_migrations/versions/liberty/contract/67c8e8d61d5_initial.py neutron_fwaas/db/migration/alembic_migrations/versions/liberty/expand/4b47ea298795_add_reject_rule.py neutron_fwaas/db/migration/alembic_migrations/versions/liberty/expand/c40fbb377ad_initial.py neutron_fwaas/db/migration/alembic_migrations/versions/mitaka/contract/458aa42b14b_fw_table_alter.py neutron_fwaas/db/migration/alembic_migrations/versions/newton/contract/f83a0b2964d0_rename_tenant_to_project.py neutron_fwaas/db/migration/alembic_migrations/versions/newton/expand/d6a12e637e28_neutron_fwaas_v2_0.py neutron_fwaas/db/migration/alembic_migrations/versions/pike/contract/fd38cd995cc0_shared_attribute_for_firewall_resources.py neutron_fwaas/db/migration/alembic_migrations/versions/queens/expand/876782258a43_create_default_firewall_groups_table.py neutron_fwaas/db/migration/alembic_migrations/versions/queens/expand/f24e0d5e5bff_uniq_firewallgroupportassociation0port.py neutron_fwaas/db/models/__init__.py neutron_fwaas/db/models/head.py neutron_fwaas/extensions/__init__.py neutron_fwaas/extensions/firewall.py neutron_fwaas/extensions/firewall_v2.py neutron_fwaas/extensions/firewallrouterinsertion.py neutron_fwaas/locale/en_GB/LC_MESSAGES/neutron_fwaas.po neutron_fwaas/privileged/__init__.py neutron_fwaas/privileged/netlink_constants.py neutron_fwaas/privileged/netlink_lib.py neutron_fwaas/privileged/utils.py neutron_fwaas/privileged/tests/__init__.py neutron_fwaas/privileged/tests/functional/__init__.py neutron_fwaas/privileged/tests/functional/dummy.py neutron_fwaas/privileged/tests/functional/utils.py neutron_fwaas/services/__init__.py neutron_fwaas/services/firewall/__init__.py neutron_fwaas/services/firewall/fwaas_plugin.py neutron_fwaas/services/firewall/fwaas_plugin_v2.py neutron_fwaas/services/firewall/agents/__init__.py neutron_fwaas/services/firewall/agents/firewall_agent_api.py neutron_fwaas/services/firewall/agents/firewall_service.py neutron_fwaas/services/firewall/agents/l2/__init__.py neutron_fwaas/services/firewall/agents/l2/fwaas_v2.py neutron_fwaas/services/firewall/agents/l3reference/__init__.py neutron_fwaas/services/firewall/agents/l3reference/firewall_l3_agent.py neutron_fwaas/services/firewall/agents/l3reference/firewall_l3_agent_v2.py neutron_fwaas/services/firewall/drivers/__init__.py neutron_fwaas/services/firewall/drivers/conntrack_base.py neutron_fwaas/services/firewall/drivers/fwaas_base.py neutron_fwaas/services/firewall/drivers/fwaas_base_v2.py neutron_fwaas/services/firewall/drivers/linux/__init__.py neutron_fwaas/services/firewall/drivers/linux/iptables_fwaas.py neutron_fwaas/services/firewall/drivers/linux/iptables_fwaas_v2.py neutron_fwaas/services/firewall/drivers/linux/legacy_conntrack.py neutron_fwaas/services/firewall/drivers/linux/netlink_conntrack.py neutron_fwaas/services/firewall/drivers/linux/l2/__init__.py neutron_fwaas/services/firewall/drivers/linux/l2/driver_base.py neutron_fwaas/services/firewall/drivers/linux/l2/noop/__init__.py neutron_fwaas/services/firewall/drivers/linux/l2/noop/noop_driver.py neutron_fwaas/services/firewall/drivers/linux/l2/openvswitch_firewall/__init__.py neutron_fwaas/services/firewall/drivers/linux/l2/openvswitch_firewall/constants.py neutron_fwaas/services/firewall/drivers/linux/l2/openvswitch_firewall/exceptions.py neutron_fwaas/services/firewall/drivers/linux/l2/openvswitch_firewall/firewall.py neutron_fwaas/services/firewall/drivers/linux/l2/openvswitch_firewall/rules.py neutron_fwaas/tests/__init__.py neutron_fwaas/tests/base.py neutron_fwaas/tests/contrib/README neutron_fwaas/tests/contrib/filters.template neutron_fwaas/tests/contrib/functional-testing.filters neutron_fwaas/tests/contrib/gate_hook.sh neutron_fwaas/tests/contrib/gate_hook_tempest.sh neutron_fwaas/tests/contrib/post_test_hook.sh neutron_fwaas/tests/contrib/hooks/api_extensions-base neutron_fwaas/tests/contrib/hooks/api_extensions-legacy neutron_fwaas/tests/contrib/hooks/api_extensions-v1 neutron_fwaas/tests/contrib/hooks/api_extensions-v2 neutron_fwaas/tests/etc/neutron/policy.d/neutron-fwaas.json neutron_fwaas/tests/functional/__init__.py neutron_fwaas/tests/functional/db/__init__.py neutron_fwaas/tests/functional/db/test_migrations.py neutron_fwaas/tests/functional/privileged/__init__.py neutron_fwaas/tests/functional/privileged/test_dummy.py neutron_fwaas/tests/functional/privileged/test_netlink_lib.py neutron_fwaas/tests/functional/privileged/test_utils.py neutron_fwaas/tests/tempest_plugin/__init__.py neutron_fwaas/tests/tempest_plugin/plugin.py neutron_fwaas/tests/tempest_plugin/services/__init__.py neutron_fwaas/tests/tempest_plugin/services/client.py neutron_fwaas/tests/tempest_plugin/services/v2_client.py neutron_fwaas/tests/tempest_plugin/tests/__init__.py neutron_fwaas/tests/tempest_plugin/tests/fwaas_client.py neutron_fwaas/tests/tempest_plugin/tests/fwaas_v2_client.py neutron_fwaas/tests/tempest_plugin/tests/api/__init__.py neutron_fwaas/tests/tempest_plugin/tests/api/base.py neutron_fwaas/tests/tempest_plugin/tests/api/test_fwaas_extensions.py neutron_fwaas/tests/tempest_plugin/tests/api/test_fwaasv2_extensions.py neutron_fwaas/tests/tempest_plugin/tests/api/v2_base.py neutron_fwaas/tests/tempest_plugin/tests/scenario/__init__.py neutron_fwaas/tests/tempest_plugin/tests/scenario/base.py neutron_fwaas/tests/tempest_plugin/tests/scenario/manager.py neutron_fwaas/tests/tempest_plugin/tests/scenario/test_fwaas.py neutron_fwaas/tests/tempest_plugin/tests/scenario/test_fwaas_v2.py neutron_fwaas/tests/unit/__init__.py neutron_fwaas/tests/unit/db/__init__.py neutron_fwaas/tests/unit/db/firewall/__init__.py neutron_fwaas/tests/unit/db/firewall/test_firewall_db.py neutron_fwaas/tests/unit/db/firewall/v2/__init__.py neutron_fwaas/tests/unit/db/firewall/v2/test_firewall_db_v2.py neutron_fwaas/tests/unit/privileged/__init__.py neutron_fwaas/tests/unit/privileged/test_netlink_lib.py neutron_fwaas/tests/unit/privileged/test_utils.py neutron_fwaas/tests/unit/services/__init__.py neutron_fwaas/tests/unit/services/firewall/__init__.py neutron_fwaas/tests/unit/services/firewall/test_fwaas_plugin.py neutron_fwaas/tests/unit/services/firewall/test_fwaas_plugin_v2.py neutron_fwaas/tests/unit/services/firewall/agents/__init__.py neutron_fwaas/tests/unit/services/firewall/agents/test_firewall_agent_api.py neutron_fwaas/tests/unit/services/firewall/agents/test_firewall_service.py neutron_fwaas/tests/unit/services/firewall/agents/l2/__init__.py neutron_fwaas/tests/unit/services/firewall/agents/l2/fake_data.py neutron_fwaas/tests/unit/services/firewall/agents/l2/test_fwaas_v2.py neutron_fwaas/tests/unit/services/firewall/agents/l3reference/__init__.py neutron_fwaas/tests/unit/services/firewall/agents/l3reference/test_firewall_l3_agent.py neutron_fwaas/tests/unit/services/firewall/agents/l3reference/test_firewall_l3_agent_v2.py neutron_fwaas/tests/unit/services/firewall/drivers/__init__.py neutron_fwaas/tests/unit/services/firewall/drivers/linux/__init__.py neutron_fwaas/tests/unit/services/firewall/drivers/linux/test_iptables_fwaas.py neutron_fwaas/tests/unit/services/firewall/drivers/linux/test_iptables_fwaas_v2.py neutron_fwaas/tests/unit/services/firewall/drivers/linux/test_legacy_conntrack.py neutron_fwaas/tests/unit/services/firewall/drivers/linux/test_netlink_conntrack.py neutron_fwaas/tests/unit/services/firewall/drivers/linux/l2/__init__.py neutron_fwaas/tests/unit/services/firewall/drivers/linux/l2/noop/__init__.py neutron_fwaas/tests/unit/services/firewall/drivers/linux/l2/noop/test_noop_driver.py neutron_fwaas/tests/unit/services/firewall/drivers/linux/l2/openvswitch_firewall/__init__.py neutron_fwaas/tests/unit/services/firewall/drivers/linux/l2/openvswitch_firewall/test_firewall.py neutron_fwaas/tests/unit/services/firewall/drivers/linux/l2/openvswitch_firewall/test_rules.py neutron_fwaas/tests/unit/services/firewall/plugins/__init__.py releasenotes/notes/.placeholder releasenotes/notes/adding-new-tables-for-future-consumption-ffd537c1f82e2e01.yaml releasenotes/notes/auto-association-default-firewall-group-7e9faf1afca1df85.yaml releasenotes/notes/bug-1702242-c917c832ac2fa4e1.yaml releasenotes/notes/bug-1746404-493a66faac333403.yaml releasenotes/notes/cisco-fwaas-driver-move-8f46325d13c93543.yaml releasenotes/notes/coexistence-between-sg-and-fwg-1f77a755539a9463.yaml releasenotes/notes/config-file-generation-265c5256668a26bf.yaml releasenotes/notes/enable-quotas-a3d0a21743bb1985.yaml releasenotes/notes/fwaas-config-9c780ccfb0e7887f.yaml releasenotes/notes/fwaas_v2-374471c215af0ca0.yaml releasenotes/notes/mcafee-fwaas-driver-removal-8915271e5d4288cf.yaml releasenotes/notes/ovs-firewall-driver-c347ea0a560b7e38.yaml releasenotes/notes/validation_if_port_is_supported-639d0df705eb67f9.yaml releasenotes/notes/varmour-fwaas-driver-removal-f7aa304a4544134a.yaml releasenotes/notes/vyatta-fwaas-driver-removal-e38e6ecde5105084.yaml releasenotes/source/conf.py releasenotes/source/index.rst releasenotes/source/liberty.rst releasenotes/source/mitaka.rst releasenotes/source/newton.rst releasenotes/source/ocata.rst releasenotes/source/pike.rst releasenotes/source/unreleased.rst releasenotes/source/_static/.placeholder releasenotes/source/_templates/.placeholder releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po releasenotes/source/locale/fr/LC_MESSAGES/releasenotes.po tools/check_unit_test_structure.sh tools/clean.sh tools/configure_for_fwaas_func_testing.sh tools/deploy_rootwrap.sh tools/generate_config_file_samples.sh tools/ostestr_compat_shim.sh tools/tox_install.shneutron-fwaas-12.0.2/neutron_fwaas.egg-info/top_level.txt0000664000175000017500000000001613555600005023502 0ustar zuulzuul00000000000000neutron_fwaas neutron-fwaas-12.0.2/neutron_fwaas.egg-info/pbr.json0000664000175000017500000000006013555600005022425 0ustar zuulzuul00000000000000{"git_version": "4c05f1f66", "is_release": true}neutron-fwaas-12.0.2/neutron_fwaas.egg-info/not-zip-safe0000664000175000017500000000000113555600005023201 0ustar zuulzuul00000000000000 neutron-fwaas-12.0.2/neutron_fwaas.egg-info/entry_points.txt0000664000175000017500000000336313555600005024256 0ustar zuulzuul00000000000000[firewall_drivers] iptables = neutron_fwaas.services.firewall.drivers.linux.iptables_fwaas:IptablesFwaasDriver iptables_v2 = neutron_fwaas.services.firewall.drivers.linux.iptables_fwaas_v2:IptablesFwaasDriver neutron.services.firewall.drivers.linux.iptables_fwaas.IptablesFwaasDriver = neutron_fwaas.services.firewall.drivers.linux.iptables_fwaas:IptablesFwaasDriver [neutron.agent.l2.extensions] fwaas_v2 = neutron_fwaas.services.firewall.agents.l2.fwaas_v2:FWaaSV2AgentExtension [neutron.agent.l2.firewall_drivers] noop = neutron_fwaas.services.firewall.drivers.linux.l2.noop.noop_driver:NoopFirewallL2Driver ovs = neutron_fwaas.services.firewall.drivers.linux.l2.openvswitch_firewall.firewall:OVSFirewallDriver [neutron.agent.l3.extensions] fwaas = neutron_fwaas.services.firewall.agents.l3reference.firewall_l3_agent:L3WithFWaaS fwaas_v2 = neutron_fwaas.services.firewall.agents.l3reference.firewall_l3_agent_v2:L3WithFWaaS [neutron.db.alembic_migrations] neutron-fwaas = neutron_fwaas.db.migration:alembic_migrations [neutron.service_plugins] firewall = neutron_fwaas.services.firewall.fwaas_plugin:FirewallPlugin firewall_v2 = neutron_fwaas.services.firewall.fwaas_plugin_v2:FirewallPluginV2 neutron.services.firewall.fwaas_plugin.FirewallPlugin = neutron_fwaas.services.firewall.fwaas_plugin:FirewallPlugin [neutron_fwaas.services.firewall.drivers.linux] conntrack = neutron_fwaas.services.firewall.drivers.linux.legacy_conntrack:ConntrackLegacy netlink_conntrack = neutron_fwaas.services.firewall.drivers.linux.netlink_conntrack:ConntrackNetlink [oslo.config.opts] firewall.agent = neutron_fwaas.opts:list_agent_opts neutron.fwaas = neutron_fwaas.opts:list_opts [tempest.test_plugins] neutron-fwaas = neutron_fwaas.tests.tempest_plugin.plugin:NeutronFWaaSPlugin neutron-fwaas-12.0.2/neutron_fwaas.egg-info/requires.txt0000664000175000017500000000055713555600005023362 0ustar zuulzuul00000000000000pbr!=2.1.0,>=2.0.0 eventlet!=0.18.3,!=0.20.1,<0.21.0,>=0.18.2 netaddr>=0.7.18 SQLAlchemy!=1.1.5,!=1.1.6,!=1.1.7,!=1.1.8,>=1.0.10 alembic>=0.8.10 six>=1.10.0 neutron-lib>=1.13.0 oslo.config>=5.1.0 oslo.db>=4.27.0 oslo.log>=3.36.0 oslo.messaging>=5.29.0 oslo.service!=1.28.1,>=1.24.0 oslo.utils>=3.33.0 oslo.privsep>=1.23.0 [:(sys_platform!='win32')] pyroute2>=0.4.21 neutron-fwaas-12.0.2/neutron_fwaas.egg-info/dependency_links.txt0000664000175000017500000000000113555600005025021 0ustar zuulzuul00000000000000 neutron-fwaas-12.0.2/setup.cfg0000664000175000017500000000625413555600005016236 0ustar zuulzuul00000000000000[metadata] name = neutron-fwaas summary = OpenStack Networking FWaaS description-file = README.rst author = OpenStack author-email = openstack-dev@lists.openstack.org home-page = https://docs.openstack.org/neutron-fwaas/latest/ classifier = Environment :: OpenStack Intended Audience :: Information Technology Intended Audience :: System Administrators License :: OSI Approved :: Apache Software License Operating System :: POSIX :: Linux Programming Language :: Python Programming Language :: Python :: 2 Programming Language :: Python :: 2.7 Programming Language :: Python :: 3 Programming Language :: Python :: 3.5 [files] packages = neutron_fwaas data_files = etc/neutron/rootwrap.d = etc/neutron/rootwrap.d/fwaas-privsep.filters [global] setup-hooks = pbr.hooks.setup_hook [entry_points] firewall_drivers = neutron.services.firewall.drivers.linux.iptables_fwaas.IptablesFwaasDriver = neutron_fwaas.services.firewall.drivers.linux.iptables_fwaas:IptablesFwaasDriver iptables = neutron_fwaas.services.firewall.drivers.linux.iptables_fwaas:IptablesFwaasDriver iptables_v2 = neutron_fwaas.services.firewall.drivers.linux.iptables_fwaas_v2:IptablesFwaasDriver neutron.service_plugins = firewall = neutron_fwaas.services.firewall.fwaas_plugin:FirewallPlugin firewall_v2 = neutron_fwaas.services.firewall.fwaas_plugin_v2:FirewallPluginV2 neutron.services.firewall.fwaas_plugin.FirewallPlugin = neutron_fwaas.services.firewall.fwaas_plugin:FirewallPlugin neutron.db.alembic_migrations = neutron-fwaas = neutron_fwaas.db.migration:alembic_migrations tempest.test_plugins = neutron-fwaas = neutron_fwaas.tests.tempest_plugin.plugin:NeutronFWaaSPlugin oslo.config.opts = neutron.fwaas = neutron_fwaas.opts:list_opts firewall.agent = neutron_fwaas.opts:list_agent_opts neutron.agent.l2.extensions = fwaas_v2 = neutron_fwaas.services.firewall.agents.l2.fwaas_v2:FWaaSV2AgentExtension neutron.agent.l2.firewall_drivers = noop = neutron_fwaas.services.firewall.drivers.linux.l2.noop.noop_driver:NoopFirewallL2Driver ovs = neutron_fwaas.services.firewall.drivers.linux.l2.openvswitch_firewall.firewall:OVSFirewallDriver neutron.agent.l3.extensions = fwaas = neutron_fwaas.services.firewall.agents.l3reference.firewall_l3_agent:L3WithFWaaS fwaas_v2 = neutron_fwaas.services.firewall.agents.l3reference.firewall_l3_agent_v2:L3WithFWaaS neutron_fwaas.services.firewall.drivers.linux = conntrack = neutron_fwaas.services.firewall.drivers.linux.legacy_conntrack:ConntrackLegacy netlink_conntrack = neutron_fwaas.services.firewall.drivers.linux.netlink_conntrack:ConntrackNetlink [build_sphinx] all_files = 1 build-dir = doc/build source-dir = doc/source warning-is-error = 1 [extract_messages] keywords = _ gettext ngettext l_ lazy_gettext mapping_file = babel.cfg output_file = neutron_fwaas/locale/neutron_fwaas.pot [compile_catalog] directory = neutron_fwaas/locale domain = neutron_fwaas [update_catalog] domain = neutron_fwaas output_dir = neutron_fwaas/locale input_file = neutron_fwaas/locale/neutron_fwaas.pot [wheel] universal = 1 [pbr] autodoc_tree_index_modules = True autodoc_tree_excludes = setup.py neutron_fwaas/tests api_doc_dir = contributor/api [egg_info] tag_build = tag_date = 0 neutron-fwaas-12.0.2/requirements.txt0000664000175000017500000000172213555577713017717 0ustar zuulzuul00000000000000# The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. pbr!=2.1.0,>=2.0.0 # Apache-2.0 eventlet!=0.18.3,!=0.20.1,<0.21.0,>=0.18.2 # MIT netaddr>=0.7.18 # BSD SQLAlchemy!=1.1.5,!=1.1.6,!=1.1.7,!=1.1.8,>=1.0.10 # MIT alembic>=0.8.10 # MIT six>=1.10.0 # MIT neutron-lib>=1.13.0 # Apache-2.0 oslo.config>=5.1.0 # Apache-2.0 oslo.db>=4.27.0 # Apache-2.0 oslo.log>=3.36.0 # Apache-2.0 oslo.messaging>=5.29.0 # Apache-2.0 oslo.service!=1.28.1,>=1.24.0 # Apache-2.0 oslo.utils>=3.33.0 # Apache-2.0 oslo.privsep>=1.23.0 # Apache-2.0 pyroute2>=0.4.21;sys_platform!='win32' # Apache-2.0 (+ dual licensed GPL2) # This project does depend on neutron as a library, but the # openstack tooling does not play nicely with projects that # are not publicly available in pypi. # -e git+https://git.openstack.org/openstack/neutron#egg=neutron neutron-fwaas-12.0.2/TESTING.rst0000664000175000017500000000075613555577713016310 0ustar zuulzuul00000000000000Testing 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 `_ For instructions on how to use FWaaS with devstack, look at: `Neutron-FWaaS DevStack `_ neutron-fwaas-12.0.2/.zuul.yaml0000664000175000017500000000316413555577713016376 0ustar zuulzuul00000000000000- project: templates: - check-requirements - periodic-stable-jobs-neutron - openstack-python-jobs-neutron - openstack-python35-jobs-neutron - release-notes-jobs - publish-openstack-sphinx-docs check: jobs: - neutron-fwaas-networking-midonet-cross-py35: voting: false - legacy-neutron-fwaas-v2-dsvm-tempest - legacy-neutron-fwaas-v1-dsvm-tempest - legacy-neutron-fwaas-dsvm-functional - legacy-grenade-dsvm-neutron-fwaas-multinode: voting: false irrelevant-files: - ^(test-|)requirements.txt$ - ^setup.cfg$ - legacy-neutron-fwaas-v1-dsvm-tempest-multinode: voting: false - legacy-neutron-fwaas-v2-dsvm-tempest-multinode: voting: false gate: jobs: - legacy-neutron-fwaas-v2-dsvm-tempest - legacy-neutron-fwaas-v1-dsvm-tempest - legacy-neutron-fwaas-dsvm-functional experimental: jobs: - legacy-neutron-fwaas-dsvm-fullstack post: jobs: - openstack-tox-cover - job: name: neutron-fwaas-networking-midonet-cross-py35 parent: openstack-tox-py35 description: | Run Python 35 tests on networking-midonet repository. vars: zuul_work_dir: src/opendev.org/openstack/networking-midonet required-projects: - openstack/requirements - openstack/networking-midonet - openstack/neutron - openstack/neutron-vpnaas - openstack/neutron-lbaas - openstack/neutron-dynamic-routing - openstack/networking-l2gw - x/tap-as-a-service neutron-fwaas-12.0.2/HACKING.rst0000664000175000017500000000036013555577713016226 0ustar zuulzuul00000000000000Neutron FWaaS Style Commandments ================================ Please see the Neutron HACKING.rst file for style commandments for neutron-fwaas: `Neutron HACKING.rst `_ neutron-fwaas-12.0.2/devstack/0000775000175000017500000000000013555600005016212 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/devstack/README.rst0000664000175000017500000000344213555577713017727 0ustar zuulzuul00000000000000========================= neutron-fwaas in DevStack ========================= This is setup as a DevStack plugin. For more information on DevStack plugins, see the `DevStack Plugins documentation `_. 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 neutron-fwaas-12.0.2/devstack/lib/0000775000175000017500000000000013555600005016760 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/devstack/lib/l3_agent0000664000175000017500000000105613555577713020424 0ustar zuulzuul00000000000000# 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" } neutron-fwaas-12.0.2/devstack/lib/l2_agent0000664000175000017500000000106113555577713020417 0ustar zuulzuul00000000000000# 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" } neutron-fwaas-12.0.2/devstack/plugin.sh0000775000175000017500000001066013555577713020075 0ustar zuulzuul00000000000000#!/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 iniset_multiline $Q_L3_CONF_FILE fwaas driver $FWAAS_DRIVER_V1 } 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 iniset_multiline $Q_L3_CONF_FILE fwaas driver $FWAAS_DRIVER_V2 iniset $NEUTRON_CORE_PLUGIN_CONF fwaas firewall_l2_driver $FW_L2_DRIVER iniset $NEUTRON_CORE_PLUGIN_CONF agent extensions fwaas_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 } # 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 neutron-fwaas-12.0.2/devstack/settings0000664000175000017500000000062613555577713020024 0ustar zuulzuul00000000000000FWAAS_DRIVER_V1=${FWAAS_DRIVER_V1:-iptables} FWAAS_DRIVER_V2=${FWAAS_DRIVER_V2:-iptables_v2} FW_L2_DRIVER=${FW_L2_DRIVER:-noop} FWAAS_PLUGIN_V1=${FWAAS_PLUGIN:-firewall} FWAAS_PLUGIN_V2=${FWAAS_PLUGIN:-firewall_v2} NEUTRON_FWAAS_DIR=$DEST/neutron-fwaas NEUTRON_FWAAS_CONF_FILE=neutron_fwaas.conf NEUTRON_FWAAS_CONF=$NEUTRON_CONF_DIR/$NEUTRON_FWAAS_CONF_FILE neutron_server_config_add $NEUTRON_FWAAS_CONF neutron-fwaas-12.0.2/LICENSE0000664000175000017500000002363713555577713015451 0ustar zuulzuul00000000000000 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. neutron-fwaas-12.0.2/tox.ini0000664000175000017500000001021613555577713015744 0ustar zuulzuul00000000000000[tox] envlist = py35,py27,pep8,pylint minversion = 1.6 skipsdist = True [testenv] setenv = VIRTUAL_ENV={envdir} PYTHONWARNINGS=default::DeprecationWarning usedevelop = True install_command = {toxinidir}/tools/tox_install.sh {env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=stable/queens} {opts} {packages} deps = -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt whitelist_externals = sh find commands = find . -type f -name "*.py[c|o]" -delete find . -type d -name "__pycache__" -delete {toxinidir}/tools/ostestr_compat_shim.sh {posargs} # there is also secret magic in ostestr which lets you run in a fail only # mode. To do this define the TRACE_FAILONLY environmental variable. [testenv:functional] setenv = OS_TEST_PATH=./neutron_fwaas/tests/functional commands = python setup.py testr --slowest --testr-args='{posargs}' [testenv:api] sitepackages=True setenv = OS_TEST_PATH=./neutron_fwaas/tests/tempest_plugin/tests/api/ OS_TESTR_CONCURRENCY=1 TEMPEST_CONFIG_DIR={env:TEMPEST_CONFIG_DIR:/opt/stack/tempest/etc} commands = python setup.py testr --slowest --testr-args='{posargs}' [testenv:scenario] sitepackages=True setenv = OS_TEST_PATH=./neutron_fwaas/tests/tempest_plugin/tests/scenario/ OS_TESTR_CONCURRENCY=1 TEMPEST_CONFIG_DIR={env:TEMPEST_CONFIG_DIR:/opt/stack/tempest/etc} commands = python setup.py testr --slowest --testr-args='{posargs}' [testenv:dsvm-functional] setenv = OS_TEST_PATH=./neutron_fwaas/tests/functional OS_SUDO_TESTING=1 OS_ROOTWRAP_CMD=sudo {envdir}/bin/neutron-rootwrap {envdir}/etc/neutron/rootwrap.conf OS_ROOTWRAP_DAEMON_CMD=sudo {envdir}/bin/neutron-rootwrap-daemon {envdir}/etc/neutron/rootwrap.conf OS_FAIL_ON_MISSING_DEPS=1 OS_NEUTRON_PATH={env:OS_NEUTRON_PATH:/opt/stack/new/neutron} whitelist_externals = sh cp sudo commands = {toxinidir}/tools/deploy_rootwrap.sh {toxinidir} {envdir}/etc {envdir}/bin python setup.py testr --slowest --testr-args='{posargs}' [testenv:releasenotes] commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html [testenv:pep8] commands = flake8 doc8 {posargs} {toxinidir}/tools/check_unit_test_structure.sh neutron-db-manage --subproject neutron-fwaas --database-connection sqlite:// check_migration {[testenv:genconfig]commands} whitelist_externals = sh [testenv:cover] commands = python setup.py test --coverage --coverage-package-name=neutron_fwaas --testr-args='{posargs}' coverage report [testenv:venv] commands = {posargs} [testenv:docs] commands = python setup.py build_sphinx [doc8] ignore = D000 ignore-path = .venv,.git,.tox,.tmp,*neutron_fwaas/locale*,*lib/python*,neutron_fwaas.egg*,doc/build,releasenotes/*,doc/source/contributor/api [flake8] # E125 continuation line does not distinguish itself from next logical line # E126 continuation line over-indented for hanging indent # E128 continuation line under-indented for visual indent # E129 visually indented line with same indent as next logical line # E265 block comment should start with '# ' # H404 multi line docstring should start with a summary # H405 multi line docstring summary not separated with an empty line # TODO(dougwig) -- uncomment this to test for remaining linkages # N530 direct neutron imports not allowed # TODO(ihrachys) -- reenable N537 when new neutron-lib release is available # H106: Do not put vim configuration in source files # H203: Use assertIs(Not)None to check for None # H904: Delay string interpolations at logging calls # N521: jsonutils.loads must be used instead of json.loads ignore = E125,E126,E128,E129,E265,H404,H405,N530,N521 enable-extensions=H106,H203,H904 show-source = true exclude = .venv,.git,.tox,dist,doc,*lib/python*,.tmp,*egg,build,tools,.ropeproject,rally-scenarios import-order-style = pep8 [testenv:pylint] deps = {[testenv]deps} pylint commands = pylint --rcfile=.pylintrc --output-format=colorized {posargs:neutron_fwaas} [hacking] import_exceptions = neutron_fwaas._i18n local-check-factory = neutron_lib.hacking.checks.factory [testenv:genconfig] commands = {toxinidir}/tools/generate_config_file_samples.sh neutron-fwaas-12.0.2/babel.cfg0000664000175000017500000000002113555577713016150 0ustar zuulzuul00000000000000[python: **.py] neutron-fwaas-12.0.2/setup.py0000664000175000017500000000200613555577713016141 0ustar zuulzuul00000000000000# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. # # 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. # THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT import setuptools # In python < 2.7.4, a lazy loading of package `pbr` will break # setuptools if some other modules registered functions in `atexit`. # solution from: http://bugs.python.org/issue15881#msg170215 try: import multiprocessing # noqa except ImportError: pass setuptools.setup( setup_requires=['pbr>=2.0.0'], pbr=True) neutron-fwaas-12.0.2/AUTHORS0000664000175000017500000002517213555600005015465 0ustar zuulzuul00000000000000Aaron Rosen Abhishek Raut Adam Harwell Adit Sarfaty Akash Gangil Akihiro MOTOKI Akihiro Motoki Akihiro Motoki Aleks Chirko Alessandro Pilotti Alessio Ababilov Alessio Ababilov Alfredo Moralejo Amir Sadoughi Andre Pech Andrea Frittoli Andreas Jaeger Andreas Jaeger Angus Lees Ann Kamyshnikova Anusha Ramineni Aradhana Singh Armando Migliaccio Artur Korzeniewski Arvind Somy Arvind Somya Assaf Muller Bertrand Lallau Bertrand Lallau Bhuvan Arumugam Bob Kukura Bob Melander Boden R Brad Hall Brandon Logan Brandon Logan Brant Knudson Brian Haley Brian Waldon Cao Xuan Hoang Carl Baldwin Cedric Brandily Chandan Dutta Chowdhury Chang Bo Guo ChangBo Guo(gcb) Christian Berendt Chuck Short Clark Boylan Clint Byrum Cuong Nguyen Cyril Roelandt Dan Prince Dan Wendlandt Davanum Srinivas Davanum Srinivas Dave Lapsley Deepak N Dirk Mueller Dongcan Ye Doug Hellmann Doug Hellmann Doug Wiegley Duan Jiong Edgar Magana Elena Ezhova Emilien Macchi Eugene Nikanorov Flavio Percoco Gary Kotton Gary Kotton German Eichberger German Eichberger Gordon Chung Guilherme Salgado Ha Van Tu Hareesh Puthalath Harkirat Singh He Jie Xu Hemanth Ravi Henry Gessau Henry Gessau Henry Gessau HenryVIII Hirofumi Ichihara Hiroyuki Ito Hunt Xu Ian Wienand Ignacio Scopetta Ihar Hrachyshka Inessa Vasilevskaya Ionuț Arțăriși Irena Berezovsky Isaku Yamahata Isaku Yamahata Itsuro Oda JJ Asghar Jacek Swiderski Jakub Libosvar James Arendt James E. Blair James E. Blair Jason Kölker Jay Pipes Jeremy Stanley Jiajun Liu Joe Gordon Joe Heck John Dunning Jordan Tardif Juliano Martinez Julien Danjou Justin Lund KIYOHIRO ADACHI Ken'ichi Ohmichi Keshava Bharadwaj Kevin Benton Kevin L. Mitchell Koteswara Rao Kelam Koteswara Rao Kelam Kris Lindgren Kun Huang Kyle Mestery Kyle Mestery Li Ma LiuNanke Luke Gorrie Luong Anh Tuan Major Hayden Margaret Frances Mark McClain Mark McClain Mark McLoughlin Martin Hickey Maru Newby Maru Newby Mate Lakat Mathieu Rohon Matt Riedemann Matthew Treinish Miguel Angel Ajo Mohammad Banikazemi Monty Taylor Morgan Fainberg Motohiro OTSUKA Nachi Ueno Nachi Ueno Nader Lahouti Nam Nguyen Hoai Nate Johnston Nguyen Phuong An Nguyen Van Trung Nick Numan Siddique Oleg Bondarev Ondřej Nový OpenStack Release Bot Paddu Krishnan Paul Michali Paul Michali Pavlo Shchelokovskyy Praneet Bachheti Preeti Mirji Rajaram Mallya Rajesh Mohan Rajesh Mohan Ralf Haferkamp Reedip Reedip Reedip Reedip Banerjee Rich Curran Roman Podoliaka Romil Gupta Rui Zang Russell Bryant Ryan Tidwell Ryota MIBU Salvatore Orlando Salvatore Orlando Samer Deeb Santhosh Santhosh Kumar Sarath Chandra Mekala Sascha Peilicke Sascha Peilicke Sascha Peilicke Sean Dague Sean Dague Sean M. Collins Sean M. Collins Sergey Lukjanov Sergey Skripnick Shiv Haris Shivakumar M Simon Murray Sirushti Murugesan Somik Behera Somik Behera SongmingYan Sridar Kandaswamy Sukhdev Sumit Naiksatam Sumit Naiksatam Sushil Kumar Swaminathan Vasudevan Sylvain Afchain Terry Wilson Thierry Carrez Thomas Bechtold Tim Miller Tim Swanson Tony Breeds Tony Xu Toshiaki Higuchi Trinath Somanchi TrinathSomanchi Tyler Smith Van Hung Pham Vikash082 Vishwanath Jayaraman Vu Cong Tuan Weidong Shao Wu Wenxiang Xuhan Peng YAMAMOTO Takashi YAMAMOTO Takashi Yaguang Tang Yalei Wang Yann Morice Yanping Qu Yi Zhao Ying Liu Yong Sheng Gong Yong Sheng Gong Yoshihiro Kaneko Yushiro FURUKAWA Zang MingJie ZhaoBo Zhenguo Niu Zhenmei ZhiQiang Fan ZhiQiang Fan Zhongyue Luo alexpilotti armando-migliaccio armando-migliaccio badveli_vishnuus berlin da52700 drzix fujioka yuuichi fumihiko kakuma garyduan ghanshyam gongysh gongysh goocher gordon chung huanghuijin justin Lund lawrancejing liu-sheng liuqing llg8212 loooosy madhusudhan-kandadai mark mcclain mathieu-rohon reedip ricolin ritesh.arya rohitagarwalla ronak root rossella shihanzhang snaiksat sridhargaddam sukhdev takanorimiyagishi trinaths vinkesh banka wujun zhangdebo1987 zhangguoqing zhangyanxian zhhuabj zhufl zoukeke neutron-fwaas-12.0.2/.mailmap0000664000175000017500000000111613555577713016051 0ustar zuulzuul00000000000000# Format is: # # lawrancejing Jiajun Liu Zhongyue Luo Kun Huang Zhenguo Niu Isaku Yamahata Isaku Yamahata Morgan Fainberg neutron-fwaas-12.0.2/CONTRIBUTING.rst0000664000175000017500000000027213555577713017073 0ustar zuulzuul00000000000000Please see the Neutron CONTRIBUTING.rst file for how to contribute to neutron-fwaas: `Neutron CONTRIBUTING.rst `_ neutron-fwaas-12.0.2/ChangeLog0000664000175000017500000015230513555600004016165 0ustar zuulzuul00000000000000CHANGES ======= 12.0.2 ------ * Fix AttributeError with third-party L3 service plugins * FWaaS-DVR: FWaaS rules not updated in DVR routers on compute host * OpenDev Migration Patch * Replace openstack.org git:// URLs with https:// 12.0.1 ------ * Should forward only first accepted packet to table 91 and 92 * import zuul job settings from project-config * Pin neutron version to stable/queens in unit test * Filter out conntrack entries with unknown protocol * Fix pep8 new warnings and pip install error * Fix sanity check for default firewall group name * tests: don't rely on configuration files outside tests directory * DVR-FWaaS: Fix DVR FWaaS rules for fipnamespace * Update UPPER\_CONSTRAINTS\_FILE for stable/queens * Update .gitreview for stable/queens 12.0.0 ------ * Validating if a port is supported by FWaaS L2 driver * Remove disable option for default FWG and allow only on VM ports * Updated from global requirements 12.0.0.0b3 ---------- * Fixing OVS driver coexistence check * Enable to associate ports with default fwg for non-admin users * Support IPv6 for netlink\_conntrack * Updated from global requirements * Add Zuul v3 native midonet cross test * Adding new tables for future consumption * Updated from global requirements * Apply default firewall group for new VM ports * Updated from global requirements * use EGRESS\_DIRECTION and INGRESS\_DIRECTION from neutron-lib * Co-existing between fwg and sg * Enable to use conntrack driver in fwaas\_v2 * Add doc8 to pep8 check for neutron-fwaas project * Replace jsonutils by common json package * Imported Translations from Zanata 12.0.0.0b2 ---------- * Trivial-fix: Remove unused method * Add reno for "OVS based l2 Firewall driver for FWaaS v2" * Adding unique constraint for port\_id * Updated from global requirements * OVS based l2 Firewall driver for FWaaS v2 * FWaaS v2 extension for L2 agent * Imported Translations from Zanata * Remove setting of version/release from releasenotes * Updated from global requirements * Updated from global requirements * Updated from global requirements * Refactor \_get\_routers\_in\_project method 12.0.0.0b1 ---------- * Adopt a new abstract method ha\_state\_change * Fix UTs gate failed * Introduce default firewall groups * Updated from global requirements * Introduce firewall l2 driver base * Updated from global requirements * Fix mismatch in error messages * Use shim tool for ostestr * Updated from global requirements * Updated from global requirements * Remove unused constant for topics * Extend FWaaS V2 ICMP reachability test scenarios * Remove vestigate HUDSON\_PUBLISH\_DOCS reference * devstack: Use entrypoint name for service\_plugin * Use neutron-lib FirewallInternalDriverError * Fix router update on L3 agent restart * Remove log translations * Updated from global requirements * Drop MANIFEST.in - it's not needed by pbr * Updated from global requirements * Fix filtering parameter to get firewall\_group * Update policy on rule addition/removal * [DevStack] Configure iptables\_v2 firewall driver for FWaaS V2 * Updated from global requirements * FW rule applied incorrectly if port specified is a range * Imported Translations from Zanata * Add firewall\_policy\_id in FWaaS v2 * Get default client config from config module * FirewallGroupPortInvalidProject can be raised now * Don't return 404 when deleting a non-existant FWG * consume load\_class\_by\_alias\_or\_classname from neutron-lib * fix releasenotes build * Update reno for stable/pike * Trivial-fix: Omit 'tests/\*' in coverage result * Update links in README 11.0.0.0rc1 ----------- * Updated from global requirements 11.0.0.0b3 ---------- * Change import orders with hacking guide * Add additional test case for Action * Use API Definitions from neutron-lib * Replace deprecated test.attr with decorators.attr * Enable some off-by-default checks * Replace uuid.uuid4() with uuidutils.generate\_uuid() * Update the documentation link for doc migration * Handle rehome of firewall exceptions * Add auto-generated config reference * Updated from global requirements * Fix genconfig in neutron-fwaas * Move remaining policy rules specific to neutron-fwaas * rearrange content to fit the new standard layout * Updated from global requirements * Use flake8-import-order plugin * Switch from oslosphinx to openstackdocstheme * Enable warning-is-error in doc build * use service type constants from neutron\_lib plugins * Updated from global requirements * Replace the usage of 'manager' with 'os\_primary' * Refactor find iptables rules * Updated from global requirements * Add original copyright * use plugin constants from neutron-lib * Updated from global requirements * Trivial-fix: Remove non ascii char 11.0.0.0b2 ---------- * Fixed comments in tox.ini [flake8] * Optimize the link address * Revert "Use neutron-lib definition of neutron-fwaas API" * Modify an order between iptables and conntrack when update firewall * Migrate Neutron's attribute to Neutron-Lib * Use neutron-lib definition of neutron-fwaas API * Fix html\_last\_updated\_fmt for Python3 * Updated from global requirements * Replace assertRaisesRegexp with assertRaisesRegex * Enable to set neutron's local repository for testing * Updated from global requirements * Added neutron\_fwaas.conf file for Firewall config * tox\_install: speed up neutron repo clone * Updated from global requirements * devstack: Adapt to lib/neutron * Updated from global requirements * Improve iptables handling * Updated from global requirements * Remove get\_namespace from API extensions * Updated from global requirements * Add supported conntrack\_driver option to devstack plugin * consume neutron-lib callbacks * Remove unused 'host' parameter from Unit tests * ConntrackNetlink driver to delete conntrack entries * Disable new N537 hacking check from next neutron-lib * Updated from global requirements * Migrate Public attribute to shared in Policy.json * Netlink library to delete conntrack entries 11.0.0.0b1 ---------- * Remove subunit-trace fork * Use \_i18n in unittests * Adding cleanup of router in unit tests * Updated from global requirements * Added 'shared' attribute for firewall resources * Enable to configure conntrack driver * Remove pyroute2.netns.setns patch * Add blank line under the subject * Updated from global requirements * Optimize the link address for fetching git code * Remove unused get\_firewalls\_for\_tenant\_without\_rules * Remove "plugins" directory * Rehome L3 exceptions to neutron-lib * Switch to use stable data\_utils * Updated from global requirements * Revert "Revert "Fix RPC scale issue using cast instead of fanout v1"" * Convert gate\_hook to devstack-tools * Revert "Fix RPC scale issue using cast instead of fanout v1" * Fix RPC scale issue using cast instead of fanout v1 * Use neutron-lib's context module instead of neutron * Updated from global requirements * consume ServicePluginBase from neutron-lib * Fix scale issue in case of services\_sync\_needed v1 * Optimize get\_firewalls\_for\_tenant implementation * Use lazy formatting when logging * [Fix gate]Update test requirement * tempest: Switch to local copy of tempset/scenario/manager.py * Remove duplicate SQL request on firewall update * Updated from global requirements * Updated from global requirements * Define in\_namespace contextmanager * Enable to run rootwrap/privsep related functional tests * Fix some unit test comments * gate\_hook: Stop including localrc * Cleanup setUp method in Unit tests * Remove "constants" duplicated import * Revert "Netlink solution to improve FWaaS performance" * Optimize fwaas tempest api tests * Remove support for py34 * Cleanup useful code in unit tests * Updated from global requirements * Switch to use test\_utils.call\_until\_true * Fix typo in .pylintrc file * Do not complain in firewall\_group\_deleted if the FW is already deleted * Add functional test to run dsvm-functional * Do not complain in firewall\_deleted if the FW is already deleted * Correct LOG message in fwaas\_plugin\_v2 * Update reno for stable/ocata 10.0.0.0rc1 ----------- * Use addCleanup instead of tearDown * Switch to decorators.idempotent\_id * TrivialFix: Added link for modindex * Optimize \_make\_firewall\_group\_dict\_with\_rules 10.0.0.0b3 ---------- * Update policy.json for FWaaS v2 * Updated from global requirements * Netlink solution to improve FWaaS performance * Fix validation in converting 'protocol' * Fix tempest gate issue on FirewallNotFound exception * Optimize \_make\_firewall\_dict\_with\_rules db queries * Updated from global requirements * Fix 'ports' attribute for firewall\_group * TrivialFix : Fix Port Error Message * Fix AssertionError raised from \_set\_rules\_for\_policy * Use BRANCH constants from neutron\_lib * Fix duplicated python version in classifier * Track neutron-lib migration of agent extensions * Support neutron-lib hacking check H904 * Updated from global requirements * [WIP]Remove no used attribute in FirewallPolicyRuleAssociation * Fix update fwr with ipv6 address * Consistent update with router\_info * Fix a typo * Remove httplib2 useless requirement * Updated from global requirements * Enable coverage report in console output * Check error message returned by the system * Fix PENDING\_UPDATE state when update exist no policy fw\_group with ports * Privsep configuration for neutron-fwaas * Updated from global requirements * Not allow to update icmp fw\_rule with port * Use neutron-lib and fwaas constants instead of neutron 10.0.0.0b2 ---------- * Updated from global requirements * Tempest Scenario tests for FWaaS V2 * Docs: fix Devstack config documentation rendering * Changed the home-page link * Adding validation protocol parameters when updating firewall rules * Updated from global requirements * neutron-lib cleanup in neutron-fwaas * Fix removing rule\_association on updating a policy * Add os-testr to test requirements * Use ExtensionDescriptor from neutron-lib * Use DB field sizes instead of \_MAX\_LEN constants * Remove PLURALS * Show team and repo badges on README * Reenable FWaaS v2 tempest tests * Add a variant of gate hook script * Revert "Set NETWORK\_API\_EXTENSIONS in fwaas devstack plugin" * FWaaS v2 Tempest API tests * Set NETWORK\_API\_EXTENSIONS in fwaas devstack plugin * Use TimeoutException from tempest.lib * Updated from global requirements * Switch to using plugins directory in lieu of neutron manager 10.0.0.0b1 ---------- * Updated from global requirements * Replaces uuid.uuid4 with uuidutils.generate\_uuid() * Add smoke tests to neutron-fwaas * Remove unused code * Add developer documentation to FWaaS * Updated from global requirements * Fix fwaas\_v2 driver internal error when restart l3 agent * Remove last vestiges of oslo-incubator * Updated from global requirements * Remove unnecessary neutron agent config reference * Remove import of neutron.extensions * Updated from global requirements * Add Python 3.5 classifier and venv * Catch the integrityError when update firewall group with non exist port * Updated from global requirements * Migrate references from neutron to neutron-lib * Fix UnboundLocalError during update firewall group * Fix typo in comment * Refactor of \_convert\_fwaas\_to\_iptables\_rule and dependencies * Updated from global requirements * Deal with the '-m protocol' flag in iptables FwAAS v1 and v2 * Enable release notes translation * Fix periodic-neutron-fwaas-pyXX-with-neutron-lib-master jobs * Updated from global requirements * Updated from global requirements * Remove reference to third-party drivers in readme * Fix import path for l3\_config * Updated from global requirements * Add devstack plugin support for fwaas v2 * Fix an argument for an exception message * gate\_hook: Fix INSTALL\_MYSQL\_ONLY != True case * Fix KeyError when fw rule associated with a policy is updated * Updated from global requirements * Check for \_interfaces in updated\_router * Add neutron-fwaas in-tree documentation * Update reno for stable/newton 9.0.0.0rc1 ---------- * Tag the alembic migration revisions for Newton * Fix neutron-fwaas tests after project\_id addition * Updated from global requirements * Skip test test\_update\_firewall\_shared\_fails\_for\_non\_admin * Add reno note for FWaaS v2 * Updated from global requirements 9.0.0.0b3 --------- * Add tests ensuring models and migrations are in sync * FWaaS v2 utilize L3 Agent Extension framework * Migrate FWaaS policy.json to FWaaS repo * Use neutron-lib model\_base instead of neutron models * Add FWaaS v1 and v2 entrypoints to setup.cfg * Add special handling for functional tests * Remove vendor driver: vyatta from community repo * Use temporary directory for neutron install * Remove Cisco driver from neutron-fwaas repo * Updated from global requirements * FWaaS v2 Database rule insert/remove operations support * Remove vendor driver: vArmour from community repo * FWaaS v2 L3 Agent Extension * FWaaS V2 Plugin * Fix enum usage in db migration for postgresql * Fix db vs migration mismatches * Fix db migration chain * Fix model/migration sync issues with FWaaS * Updated from global requirements * Constrain remaining tox targets * FWaaS v2 Database * TrivialFix: Add validation for tenant\_id * FwaaS v2 REST API * Fix column\_name in migration from project\_id change * Fix db migration after project\_id changes * Remove temporary local HasProject * Enable DeprecationWarning in test environments * Updating imports as l3 agent config options * Updated from global requirements * devstack: Don't bother to have our own l3 agent config file * Update imports (common.config -> conf.common) * Rename DB columns: tenant -> project * add "reject" action to firewall rule doesn't work for postgresql * Updated from global requirements 9.0.0.0b2 --------- * Delete mcafee FwaaS driver * Updated from global requirements * DevStack plugin for fwaas * Add python 3 classifiers * Add entrypoints for iptables and varmor drivers * Fix deprecation warnings * Fix subunit trace help * Fix a few test cases in test\_cisco\_fwaas\_plugin * Follow the recent tempest change * Remove unused POT file * Updated from global requirements * Remove check\_i18n files * Use call\_and\_ignore\_notfound\_exc directly * Updated from global requirements 9.0.0.0b1 --------- * Fix neutron\_lib deprecations * [Trivial] Remove unnecessary executable privilege * Remove unnecessary executable permissions * Updated from global requirements * Fix broken tempest tests * Skip broken tests * Switch to using hacking checks from neutron-lib * Fix "Not applying Firewall rules immediately" problem 8.1.2 ----- * Updated from global requirements * Updated from global requirements * Don't use zuul-cloner for venv env, for periodic jobs * Fix doc build if git is absent * Updated from global requirements 8.0.0 ----- * Constraint requirements using mitaka upper-constraints.txt file * FWaaS Disable nonstandard-exception due to neutron\_lib shims * Update reno for stable/mitaka 8.0.0.0rc2 ---------- * Update .gitreview for stable/mitaka 8.0.0.0rc1 ---------- * Translations: add in the locale directory * Use routers client instead of networks client * Use tempest.lib base module for network * Updated from global requirements * Make all tox targets constrained * Tag the alembic migration revisions for Mitaka 8.0.0.0b3 --------- * FWaaS: make use of neutron\_lib constants * Use tempest.lib instead of tempest-lib * Track alembic heads * Remove unused pngmath Sphinx extension * Fixed typo in subunit-trace.py * Updated from global requirements * Select the right DB to store user and data in each backend * Modify tox.ini to run dsvm-functional tests * Updated from global requirements * FWaaS: make use of neutron\_lib exceptions * delete meaningless braces in log * Updated from global requirements * Cleanup i18n module usages * tempest: Confirm the opposite direction * tempest: Fix a few test cases * FWaaS quota registration * Add testresources to neutron-fwaas test requirements * Fix neutron-fwaas cover tests * Send Notifications for Firewall policy updates * Set testenv env for api and scenario testing * tempest: Skips router insertion tests if public\_router\_id is configured * tempest: Update for NetworkClient * Add an explicit BRANCH\_NAME to tox\_install.sh * Update translation setup * tempest: Add a comment * Updated from global requirements * tempest: Remove an extra comma, which breaks the following assertion * Add unittest of convert\_action\_to\_case\_insensitive * Updated from global requirements 8.0.0.0b2 --------- * Clean up removed hacking rule from [flake8] ignore lists * tempest: Add test cases for router insertion * Replace deprecated library function os.popen() with subprocess * Add unit test for convert\_protocol * Updated from global requirements * Change LOG.warn to LOG.warning * LOG.warn -> LOG.warning * Trival: Remove unused logging import * tempest: Update after the recent tempest change * Updated from global requirements * Avoid duplicating tenant check when creating resources * Fix a typo from UPPER\_CONTRAINTS\_FILE to UPPER\_CONSTRAINTS\_FILE * Setup for translation * Deprecated tox -downloadcache option removed * Remove Neutron FWaaS static example configuration files * Updated from global requirements * Added constraints tox targets * Automatically generate neutron FWaaS configuration files * Adapt to the recent tempest * Delete python bytecode before every test run * Updated from global requirements * Remove dependency on neutron for topics * Add reno for release notes management * Fix db error when running python34 Unit tests * Remove version from setup.cfg * Validation of router\_ids insertion with column selection 8.0.0.0b1 --------- * Fix TempestPlugin to fix gate failure * Freescale FWaaS Plugin code final decomposition * Updated from global requirements * Add some more tempest scenarios * Updated from global requirements * Set ZUUL\_BRANCH using BRANCH if not available * Move api tests from Neutron repository * Revert "Register FWaaS resources to quota engine" * Remove deprecated use\_namespaces option * Prepare neutron "use\_namespaces" option removal * Update list of modules supporting py34 tests * FWaaS fix comment typos * Updated from global requirements * Updated from global requirements * Add tempest scenario tests * Updated from global requirements * Removed new=True argument from create\_connection * Include alembic versions directory to the package * Register FWaaS resources to quota engine * Updated from global requirements * Validate src\_ip\_adress, dest\_ip\_address and ip\_version * Updated from global requirements * Use assert(Not)In instead of assertTrue/False * use assertIs(Not)None instead of assert(Not)Equal * use assertFalse instead of assertEqual(False, \*\*\*) * Fix argument order for assertEqual to (expected, observed) * Case-Sensitivity for name column in fw table 7.0.0 ----- * Tag the alembic migration revisions for Liberty * Tag the alembic migration revisions for Liberty * Consume FWaaS plugin queue in RPC workers * Kill HEADS file * Stop doing any magic cloning of neutron during CI * Use stable/liberty branch for neutron dep 7.0.0.0rc1 ---------- * Update defaultbranch in .gitreview to stable/liberty * Open Mitaka development * Change ignore-errors to ignore\_errors * Updated from global requirements * Fix check\_migration error * Added +x permission to gate hook scripts * py34 support for fwaas 7.0.0.0b3 --------- * Updated from global requirements * Updated from global requirements * Removing unused dependency: discover * Kill contention between update and delete * FWaaS: Fix 500 INTERNAL error on too long name or description * Adopt the migration chaining and branch names from neutron * Switch to using os-testr's copy of subunit2html * Cisco CSR FWaaS driver should use 'l4-options' in CSR RESTAPI call * Updated from global requirements * Add REJECT rule on Neutron FWaaS * Make tests/unit/extensions discoverable * Updated from global requirements * Reduce contention between update and delete * Add oslotest dependency to test-requirements * Updated from global requirements * Validate tenant\_id between firewall and firewall-policy * Do not make delete\_db\_firewall conditional * Updated from global requirements * switch to multi branch migration tree for liberty * py34: Enable initial python34 testing for FWaaS * Killed existing downgrade rules in migration scripts 7.0.0.0b2 --------- * script.py.mako: update to latest standards * py34: Fix usage of gettext.install * Register alembic\_migrations at install time * Updated from global requirements * Remove quantum untracked files from .gitignore * Fix wrong argument name in \_rpc\_update\_firewall * Use oslo\_log.helpers.log\_method\_call * Updated from global requirements * Fix UT failures during firewall mock plugin setup * COMMON\_PREFIXES cleanup - patch 3/5 * Updated from global requirements * Switch to oslo.service 7.0.0.0b1 --------- * Updated from global requirements * Switch to oslo\_utils.uuidutils * Fixes firewall going to error state on an update * Trim some unused test packages * Update version for Liberty * Updated from global requirements * Add handle for protocol value "any" in CSR1kv driver 7.0.0a0 ------- * Updated from global requirements * Revert "Insert tenant\_id validation for fw and fw-policy" * Updated from global requirements * Python 3: use six.iteritems instead of dict.items * Enable random hash seeds * Do not assume jsonutils.dumps ordering * Updated from global requirements * Add validation of port\_range for firewall-rule * Remove contextlib.nested from tests * vendor code should refer to Brocade * Updated from global requirements * Make iptables firewall work with L3 plugin without DVR support * Updated from global requirements * Insert tenant\_id validation for fw and fw-policy 2015.1.0 -------- * Insert validation in creating/updating firewall * update .gitreview for stable/kilo * Updated Protocol named constants * Add Kilo release milestone * Add Kilo release milestone * Pin neutron to stable/kilo * Restore Brocade Vyatta firewall functionality * FWaaS: Remove check for bash usage * Restore Brocade Vyatta firewall functionality * Switch from neutron.common.log.log to oslo\_log.helpers.log\_method\_call * logic error in updating audited of firewall policy 2015.1.0rc1 ----------- * FWaaS: Reorganize unit test tree * Open Liberty development * Updated Protocol named constants * FWaaS: Refactor callback mechanism * Use BaseSudoTestCase instead of BaseLinuxTestCase * Remove reference to testlib\_plugin * Updated from global requirements 2015.1.0b3 ---------- * Adding mandatory parameters for Router Info in vArmour fwaas agent * tests: stop overwriting neutron BaseTestCase configuration files * Refactoring related to 'FWaaS insertion Model on Routers' * Adds FWaaS driver for Cisco CSR1kv * Adds FWaaS service plugin for Cisco CSR1kv * FWaaS Insertion Model on Routers * Decouple L3 and Firewall during DVR router migration * Implementation of Mcafee NGFW Driver * Provide hooks for FWaaS repo functional gate * Updated from global requirements * Fix for breaking UTs when creating RouterInfo * Add the missing parameter agent\_mode to the FWaaSDriverBase class methods * Add the prefix networking\_brocade to import paths * Pick up missing neutron to neuton\_fwaas change * Removed lockutils-wrapper from tox targets * Migrate to oslo.log * Change L3 agent AdvancedService class to be non-singleton * Updated from global requirements * Enable removing references to self.services\_sync in l3 agent * Stop storing and passing root\_helper * Fix pylint error due to novaclient v1\_1/v2 rename * Add index on tenant\_id * FWaaS: Enable coverage testing * Remove unused \_destroy\_router\_namespaces() * Implementation of Brocade Vyatta Firewall driver * Pass root\_helper to ip\_lib by keyword argument to prep for removal * Fixed tests to use neutron\_fwaas extensions and neutrons * oslo: migrate to namespace-less import paths 2015.1.0b2 ---------- * Code cleanup for Freescale FWaaS Plugin * Updated from global requirements * Move extensions to service repo * Fix neutron-fwaas unit test failures due to multiple issues * Move '\_make\_firewall\_dict\_with\_rules' to firewall\_db.py * Freescale FWaaS Plugin * Updated from global requirements * Updated from global requirements * Updated from global requirements * Migrate to oslo.concurrency * Adjust fwaas unit tests to work with quotas enabled * Updated from global requirements * Update hacking to 0.10 * Fix the neutron-fwaas unit test failures * Updated from global requirements * FWaaS: L3 Agent restructure - observer hierarchy * Cleaned up requirements.txt * Bump from global requirements * Fix gitignore of egg files properly * Do not list neutron in requirements.txt * Re-enable UT for neutron-fwaas for services split * Backward compatibility for fwaas * Update documentation files for FWaaS 2015.1.0b1 ---------- * Kill oslo-incubator files * Move classes out of l3\_agent.py * Delete accidentally committed egg files * Fix python path to neutron\_fwaas module * Init of separate alembic chain * Fix python path in ini file * After the services split, get neutron-fwaas Jenkins jobs passing * Fix git review target for repo * Split fwaas services code into neutron-fwaas * Workflow documentation is now in infra-manual * tox.ini: Prevent casual addition of bash dependency * Updated from global requirements * Get rid of py26 references: OrderedDict, httplib, xml testing * Updated the README.rst * pretty\_tox.sh: Portablity improvement * test\_dhcp\_agent: Fix no-op tests * Enable undefined-loop-variable pylint check * Fix incorrect exception order in \_execute\_request * Migrate to oslo.i18n * Migrate to oslo.middleware * Migrate to oslo.utils * Remove Python 2.6 classifier * Remove ryu plugin * Updated from global requirements * Fix AttributeError in RPC code for DVR * Show progress output while running unit tests * Enforce log hints in neutron.services.firewall * enable H401 hacking check * enable H237 check * Updated from global requirements * Drop RpcProxy usage from FWaaS code * Drop several uses of RpcCallback * Enable default SNAT from networks connected to a router indirectly * Updated from global requirements * Update i18n translation for neutron.db log msg's * Update i18n translation for neutron.agents log msg's * enable F812 check for flake8 * enable F811 check for flake8 * Support pudb as a different post mortem debugger * switch to oslo.serialization * Add rootwrap filters for ofagent * Remove useless return * Remove openvswitch core plugin entry point * Updated from global requirements * Purge use of "PRED and A or B" poor-mans-ternary * Remove duplicate ensure\_remove\_chain method in iptables\_manager * Remove use\_namespaces from RouterInfo Property * Updated from global requirements * Remove XML support * enable F402 check for flake8 * enable E713 in pep8 tests * Hyper-V: Remove useless use of "else" clause on for loop * Enable no-name-in-module pylint check * Updated from global requirements * Remove duplicate import of constants module * Switch run-time import to using importutils.import\_module * Enable assignment-from-no-return pylint check * tox.ini: Avoid using bash where unnecessary * Empty files should not contain copyright or license * Remove single occurrence of lost-exception warning * Updated fileutils and its dependencies * remove E251 exemption from pep8 check * mock.assert\_called\_once() is not a valid method * Add pylint tox environment and disable all existing warnings * Updated from global requirements * Ignore top-level hidden dirs/files by default * Avoid constructing a RouterInfo object to get namespace name * Drop sslutils and versionutils modules * Removed kombu from requirements * Updated from global requirements * Updated from global requirements * Remove sslutils from openstack.common * remove linuxbridge plugin * Open Kilo development * Implement ModelsMigrationsSync test from oslo.db * Disallow unsharing used firewall policy * Fix entrypoint of OneConvergencePlugin plugin * Stop admin using other tenants unshared rules * Set dsvm-functional job to use system packages * Separate Configuration from Freescale SDN ML2 mechanism Driver * Remove @author(s) from copyright statements * Add HA support to the l3 agent * Updated from global requirements * Adds ipset support for Security Groups * Remove useless check in \_rpc\_update\_firewall * Don't allow user to set firewall rule with port and no protocol * Changes to support FWaaS in a DVR based environment * Add requests\_mock to test-requirements.txt * Removed kombu from requirements * Supply missing cisco\_cfg\_agent.ini file * Remove unused arg to config.setup\_logging() * Updated from global requirements * shared policy shouldn't have unshared rules * Work toward Python 3.4 support and testing * Revert "Cisco DFA ML2 Mechanism Driver" * Remove SELECT FOR UPDATE use in delete\_firewall * Big Switch: Separate L3 functions into L3 service * Remove reference to cisco\_cfg\_agent.ini from setup.cfg again * Adds router service plugin for CSR1kv * Support for extensions in ML2 * Cisco DFA ML2 Mechanism Driver * Adding mechanism driver in ML2 plugin for Nuage Networks * Fix state\_path in tests * Remove ovs dependency in embrane plugin * Use lockutils module for tox functional env * Remove SELECT FOR UPDATE use in update\_firewall * Prefer "val !=/== ref" over "val (not) in [ref]" in conditions * Remove status initialization from plugin's create\_firewall * Set firewall state to CREATED when dealing with DVR * Updated from global requirements * Add specific docs build option to tox * Fix bigswitch setup.cfg lines * Remove auto-generation of db schema from models at startup * Skip FWaaS config mismatch check if RPC method is unsupported * Updated from global requirements * Use jsonutils instead of stdlib json * Remove INACTIVE status from FWaaS * Preserve link local IP allocations for DVR fip ns across restart * Opencontrail plug-in implementation for core resources * Return 403 instead of 404 on attr policy failures * Proper validation for inserting firewall rule * Remove redundant topic from rpc calls * Add a tox test environment for random hashseed testing * Updated from global requirements * Move from Python logging to Openstack logging * Remove reference to cisco\_cfg\_agent.ini from setup.cfg * Exit Firewall Agent if config is invalid * Removed configobj from test requirements * Updated from global requirements * Functional tests work fine with random PYTHONHASHSEED * Set python hash seed to 0 in tox.ini * Configuration agent for Cisco devices * Updated from global requirements * ML2 mechanism driver for SR-IOV capable NIC based switching, Part 2 * Audited attribute for policy update not changing * This patch changes the name of directory from mech\_arista to arista * ML2 mechanism driver for SR-IOV capable NIC based switching, Part 1 * Allow to import \_LC, \_LE, \_LI and \_LW functions directly * Make readme reference git.openstack.org not github * Bump hacking to version 0.9.2 * Use auth\_token from keystonemiddleware * Change all occurences of no\_delete to do\_delete * Extract CommonDBMixin to a separate file * Remove reference to setuptools\_git * Add a gate-specific tox env for functional tests * Add CONTRIBUTING.rst * Updated from global requirements * Updated from global requirements * Updated from global requirements * Fix example for running individual tests * Switch to using of oslo.db * remove unsupported middleware * Add config for performance gate job * Synced log module and its dependencies from olso-incubator * don't ignore rules that are already enforced * Moved rpc\_compat.py code back into rpc.py * Updated from global requirements * Updated from global requirements * ofagent: move main module from ryu repository * Remove the useless vim modelines * Removed 'rpc' and 'notifier' incubator modules * Removed create\_rpc\_dispatcher methods * Use openstack.common.lockutils module for locks in tox functional tests * Renamed consume\_in\_thread -> consume\_in\_threads * Port to oslo.messaging * Updated from global requirements * Ignore emacs checkpoint files * Check port value when creating firewall rule with icmp protocol * Configure agents using neutron.common.config.init (formerly .parse) * Added missing core\_plugins symbolic names * Introduced rpc\_compat.create\_connection() * Introduce RpcCallback class * remove pep8 E122 exemption and correct style * remove E112 hacking exemption and fix errors * Updated from global requirements * Fix race condition with firewall deletion * Monkey patch threading module as early as possible * Added RpcProxy class * Freescale SDN Mechanism Driver for ML2 Plugin * Remove run-time version checking for openvswitch features * Log firewall status on delete in case of status inconsistency * Added missing plugin .ini files to setup.cfg * FWaaS plugin doesn't need to handle firewall rule del ops * Updated from global requirements * Disallow regular user to update firewall's shared attribute * Synced jsonutils from oslo-incubator * Cisco APIC ML2 mechanism driver, part 2 * NSX: get rid of the last Nicira/NVP bits * Do not defer IPTables apply in firewall path * Fix non-existent 'assert' calls to mocks * Add missing translation support * Add mailmap entry * Updated from global requirements * Remove explicit dependency on amqplib * Remove duplicate module-rgx line in .pylintrc * Fix H302 violations * Fix H302 violations in unit tests * Updated from global requirements * Updated from global requirements * Exclude .ropeproject from flake8 checks * Remove mock.patch.stop from tests that inherit from BaseTestCase * Enable flake8 E711 and E712 checking * Updated from global requirements * Sync service and systemd modules from oslo-incubator * Move bash whitelisting to pep8 testenv * Fix Jenkins translation jobs * Set ns\_name in RouterInfo as attribute * ignore build directory for pep8 * Return 409 for second firewall creation * Enable hacking H301 check * Updated from global requirements * Remove last parts of Quantum compatibility shim * UT: do not hide an original error in test resource ctxtmgr * Open Juno development * Start using oslosphinx theme for docs * Updated from global requirements * add HEAD sentinel file that contains migration revision * Ensure to count firewalls in target tenant * Mock agent RPC for FWaaS tests to delete DB objs * Fix KeyError except on router\_info in FW Agent * Bugfix and refactoring for ovs\_lib flow methods * Removes calls to mock.patch.stopall in unit tests * Updated from global requirements * Updated from global requirements * Updated from global requirements * One Convergence Neutron Plugin l3 ext support * One Convergence Neutron Plugin Implementation * BigSwitch: Add SSL Certificate Validation * Updated from global requirements * Add OpenDaylight ML2 MechanismDriver * Implementaion of Mechanism driver for Brocade VDX cluster of switches * Implement Mellanox ML2 MechanismDriver * Implement OpenFlow Agent mechanism driver * Finish off rebranding of the Nicira NVP plugin * BigSwitch: Add agent to support neutron sec groups * Adds the new IBM SDN-VE plugin * Updated from global requirements * Remove unused variable * Change firewall to DOWN when admin state down * Update License Headers to replace Nicira with VMware * Developer documentation * options: consolidate options definitions * Rename Neutron core/service plugins for VMware NSX * Updated from global requirements * Sync minimum requirements * Copy cache package from oslo-incubator * ipt\_mgr.ipv6 written in the wrong ipt\_mgr.ipv4 * Validate rule uuids provided for update\_policy * Remove dependent module py3kcompat * Add migration support from agent to NSX dhcp/metadata services * Remove psutil dependency * LBaaS: move agent based driver files into a separate dir * mailmap: update .mailmap * Return request-id in API response * Prepare for multiple cisco ML2 mech drivers * Support building wheels (PEP-427) * Use oslo.rootwrap library instead of local copy * Enables BigSwitch/Restproxy ML2 VLAN driver * Add an explicit tox job for functional tests * Base ML2 bulk support on the loaded drivers * Enable hacking H233 rule * Update RPC code from oslo * Configure plugins by name * Update lockutils and fixture in openstack.common * Rename nicira configuration elements to match new naming structure * Remove unused imports * Rename check\_nvp\_config utility tool * Corrects broken format strings in check\_i18n.py * Remove FWaaS Noop driver as default and move to unit tests dir * Updates tox.ini to use new features * Updated from global requirements * Sync global requirements to pin sphinx to sphinx>=1.1.2,<1.2 * Remove start index 0 in range() * Add fwaas\_driver.ini to setup.cfg * Add vpnaas and debug filters to setup.cfg * Updates .gitignore * Update Zhenguo Niu's mailmap * Fwaas can't run in operating system without namespace feature * Replace stubout with fixtures * Ensure get\_pid\_to\_kill works with rootwrap script * Call \_destroy\_metadata\_proxy from \_destroy\_router\_namespaces * Apply six for metaclass * Updated from global requirements * Cleanup HACKING.rst * Fix import log\_handler error with publish\_errors set * Updated from global requirements * Utilizes assertIsNone and assertIsNotNone * Updated from global requirements * Fix incorrect indentations found by Pep 1.4.6+ * avoid changing the rule's own action * Cleanup and make HACKING.rst DRYer * Add support for managing async processes * Remove obsolete redhat-eventlet.patch * Open Icehouse development * Updated from global requirements * Fix to enable delete of firewall in PENDING\_CREATE state * Require oslo.config 1.2.0 final * Fix FWaaS plugin to allow one firewall per tenant * Use built-in print() instead of print statement * FWaaS - fix reordering of rules in policy * Support for NVP advanced FwaaS service * FWaaS - fix policy association of firewall rule * Add l2 population base classes * Fix message i18n error * Install metering\_agent.ini and vpn\_agent.ini * fix conversion type missing * vArmour gateway agent and FWaaS driver * Enclose command args in with\_venv.sh * ML2 Mechanism Driver for Cisco Nexus * Reference driver implementation (IPsec) for VPNaaS * Implement ML2 port binding * Adding more unit tests for the FWaaS agent * Arista ML2 Mechanism driver * ML2 Mechanism Driver for Tail-f Network Control System (NCS) * Default to not capturing log output in tests * Add Neutron l3 metering agent * Update mailmap * Fix wrong example in HACKING.rst * Bumps hacking to 0.7.0 * remove binaries under bin * Fixes Windows setup dependency bug * Restore Babel to requirements.txt * fix up inadevertant octal to make hacking pass * Firewall as a Service (FWaaS) Iptables Driver * Remove DHCP lease logic * Firewall as a Service (FWaaS) Agent * Remove last vestiges of nose * Updated from global requirements * Ignore pbr\*.egg directory * Fix H102, H103 Apache 2.0 license hacking check error * Remove openstack.common.exception usage * Adds Babel dependency missing from 555d27c * Fix the alphabetical order in requirement files * Followup fixes to FWaaS API patch * Remove comments from requirements.txt (workaround pbr bug) * Remove last of assertEquals * Firewall as a Service (FWaaS) APIs and DB Model * remove netifaces dependency of ryu-agent * Add gre tunneling support for the ML2 plugin * Add VXLAN tunneling support for the ML2 plugin * xenapi - rename quantum to neutron * Fix issue with pip installing oslo.config-1.2.0 * Initial Modular L2 Mechanism Driver implementation * Add cover/ to .gitignore * fix some missing change from quantum to neutron * git remove old non-working packaging files * Rename Quantum to Neutron * Rename quantum to neutron in .gitreview * Sync install\_venv\_common from oslo * Update to use OSLO db * Require greenlet 0.3.2 (or later) * Remove single-version-externally-managed in setup.cfg * Fix single-version-externally-mananged typo in setup.cfg * Allow use of lowercase section names in conf files * Require pbr 0.5.16 or newer * Update to the latest stevedore * Rename agent\_loadbalancer directory to loadbalancer * Remove unit tests that are no longer run * Update with latest OSLO code * Remove explicit distribute depend * Fix and enable H90x tests * Remove generic Exception when using assertRaises * Add \*.swo/swp to .gitignore * python3: Introduce py33 to tox.ini * Rename README to README.rst * Rename requires files to standard names * Initial Modular L2 plugin implementation * Revert dependency on oslo.config 1.2.0 * Perform a sync with oslo-incubator * Require oslo.config 1.2.0a2 * update mailmap * Revert "Fix ./run\_tests.sh --pep8" * Move to pbr * Docstrings formatted according to pep257 * relax amqplib and kombu version requirements * Fix ./run\_tests.sh --pep8 * blueprint mellanox-quantum-plugin * Update flake8 pinned versions * Let the cover venv run individual tests * Copy the RHEL6 eventlet workaround from Oslo * Remove locals() from strings substitutions * Enable automatic validation of many HACKING rules * Shorten the path of the nicira nvp plugin * Allow pdb debugging in manually-invoked tests * Reformat openstack-common.conf * Switch to flake8 from pep8 * Parallelize quantum unit testing: * blueprint cisco-single-config * Add lbaas\_agent files to setup.py * Add VIRTUAL\_ENV key to enviroment passed to patch\_tox\_env * Pin SQLAlchemy to 0.7.x * Sync latest Oslo components for updated copyright * drop rfc.sh * Replace "OpenStack LLC" with "OpenStack Foundation" * First havana commit * remove references to netstack in setup.py * Switch to final 1.1.0 oslo.config release * Update to Quantum Client 2.2.0 * Update tox.ini to support RHEL 6.x * Switch to oslo.config * Add common test base class to hold common things * Pin pep8 to 1.3.3 * Add initial testr support * LBaaS Agent Reference Implementation * Bump python-quantumclient version to 2.1.2 * Add scheduling feature basing on agent management extension * Remove compat cfg wrapper * Unpin PasteDeploy dependency version * Use testtools instead of unittest or unittest2 * Add midonet to setup.py * Sync latest install\_venv\_common.py with olso * Add check-nvp-config utility * Add unit test for ryu-agent * Use oslo-config-2013.1b3 * Adds Brocade Plugin implementation * Synchronize code from oslo * PLUMgrid quantum plugin * Update .coveragerc * Allow tools/install\_venv\_common.py to be run from within the source directory * Updated to latest oslo-version code * Use install\_venv\_common.py from oslo * Cisco plugin cleanup * Use babel to generate translation file * Update WebOb version to >=1.2 * Update latest OSLO * Adding multi switch support to the Cisco Nexus plugin * Adds support for deploying Quantum on Windows * Latest OSLO updates * Port to argparse based cfg * Add migration support to Quantum * Undo change to require WebOb 1.2.3, instead, require only >=1.0.8 * .gitignore cleanup * Upgrade WebOb to 1.2.3 * Logging module cleanup * Add OVS cleanup utility * Add tox artifacts to .gitignore * Add restproxy.ini to config\_path in setup.py * Add script for checking i18n message * l3 agent rpc * Add metadata\_agent.ini to config\_path in setup.py * Remove \_\_init\_\_.py from bin/ and tools/ * add metadata proxy support for Quantum Networks * Use auth\_token middleware in keystoneclient * Add QUANTUM\_ prefix for env used by quantum-debug * Make tox.ini run pep8 checks on bin * Explicitly include versioninfo in tarball * Import lockutils and fileutils from openstack-common * Updated openstack-common setup and version code * Ensure that the anyjson version is correct * Add eventlet\_backdoor and threadgroup from openstack-common * Add loopingcall from openstack-common * Added service from openstack-common * Drop lxml dependency * Add uuidutils module * Import order clean-up * pin sqlalchemy to 0.7 * Correct Intended Audience * Add OpenStack trove classifier for PyPI * Improve unit test times * l3\_nat\_agent was renamed to l3\_agent and this was missed * Support for several HA RabbitMQ servers * add missing files from setup.py * Create .mailmap file * Lower webob dep from v1.2.0 to v1.0.8 * Implements agent for Quantum Networking testing * Create utility to clean-up netns * Update rootwrap; track changes in nova/cinder * Execute unit tests for Cisco plugin with Quantum tests * Add lease expiration script support for dnsmasq * Add nosehtmloutput as a test dependency * quantum l3 + floating IP support * Updates pip requirements * NEC OpenFlow plugin support * remove old gflags config code * RPC support for OVS Plugin and Agent * Initial implemention of MetaPlugin * RPC support for Linux Bridge Plugin and Agent * Exempt openstack-common from pep8 check * fix bug lp:1025526,update iniparser.py to accept empty value * Introduce files from openstack common * fix bug lp:1019230,update rpc from openstack-common * implement dhcp agent for quantum * Use setuptools git plugin for file inclusion * Remove paste configuration details to a seperate file. blueprint use-common-cfg * Implements the blueprint use-common-cfg for the quantum service. More specifically uses global CONF for the quantum.conf file * Add authZ through incorporation of policy checks * Bug #1013967 - Quantum is breaking on tests with pep 1.3 * Use openstack.common.exception * API v2: mprove validation of post/put, rename few attributes * Add API v2 support * Fix up test running to match jenkins expectation * Add build\_sphinx options * Quantum should use openstack.common.jsonutils * Remove hardcoded version for pep8 from tools/test-requires * Quantum should use openstack.common.importutils * PEP8 fixes * Bug #1002605 * Parse linuxbridge plugins using openstack.common.cfg * Add HACKING.rst to tarball generation bug 1001220 * Include AUTHORS in release package * Change Resource.\_\_call\_\_() to not leak internal errors * Removed simplejson from pip-requires * Remove dependency on python-quantumclient * Add sphinx to the test build deps * Add HACKING.rst coding style doc * bug 963152: add a few missing files to sdist tarball * Fix path to python-quantumclient * Split out pip requires and aligned tox file * Fix missing files in sdist package [bug 954906] * Downgraded required version of WebOb to 1.0.8 * more files missing in sdist tarball * make sure pip-requires is included in setup.py sdist * remove pep8 and strict lxml version from setup.py * plugin: introduce ryu plugin * bug 934459: pip no longer supports -E * blueprint quantum-ovs-tunnel-agent * Initial commit: nvp plugin * Cleanup the source distribution * blueprint quantum-linux-bridge-plugin * Remove quantum CLI console script * Bug 925372: remove deprecated webob attributes (and also specify stable webob version in pip-requires) * Make tox config work * Pin versions to standard versions * Split out quantum.client and quantum.common * Quantum was missing depend on lxml * moving batch config out of quantum-server repo * Getting ready for the client split * Removed erroneous print from setup.py * Base version.py on glance * Fix lp bug 897882 * Install a good version of pip in the venv * Rename .quantum-venv to .venv * Remove plugin pip-requires * Bug #890028 * Fix for bug 900316 * Second round of packaging changes * Changes to make pip-based tests work with jenkins * Fix for bug 888811 * Fix for Bug #888820 - pip-requires file support for plugins * blueprint quantum-packaging * Add .gitreview config file for gerrit * Add code-coverage support to run\_tests.sh (lp860160) 2011.3 ------ * Add rfc.sh to help with gerrit workflow * merge tyler's unit tests for cisco plugin changes lp845140 * merge salv's no-cheetah CLI branch lp 842190 * merge sumit's branch for lp837752 * Merging latest from lp:quantum * Merging lo:~salvatore-orlando/quantum/quantum-api-auth * Updating CLI for not using Cheetah anymore. Now using a mechanism based on Python built-in templates * Merging Sumit's changes including fixes for multinic support, and CLI module for working with extensions * Merging from Cisco branch * Merging from lp:quantum * merge cisco consolidated plugin changes * Merging lp:~salvatore-orlando/quantum/bug834449 * merge trunk * Merging from lp:quantum * merge salvatore's new cli code * Addressing comments from Dan * Merging from quantum * merge cisco extensions branch * Merging from Sumit's branch, changes to VIF-driver and Scheduler; extension action names have been changed in response to Salvatore's review comments in the extensions branch review * Syncing with Cisco extensions branch * Merging from Sumit's branch, import ordering related changes * Merging the Cisco branch * Finishing cli work Fixing bug with XML deserialization * Merging lp:~salvatore-orlando/quantum/quantum-api-alignment * merge latest quantum branch and resolve conflicts * Merging lp:~asomya/quantum/lp833163 Fix for Bug #833163: Pep8 violations in recent packaging changes that were merged into trunk (Critical) * PEP8 fixes for setup.py * Merging lp:~cisco-openstack/quantum/802dot1qbh-vifdriver-scheduler * Merging lp:~cisco-openstack/quantum/l2network-plugin-persistence * Merging lp:quantum * merging with lp:quantum * Making Keystone version configurable * Merging branch: lp:~danwent/quantum/test-refactor * Syncing with lp:quantum * Merging fixes and changes batch-config script. Thanks lp:danwent ! * Merging lp:~asomya/quantum/lp824145 Fix for Bug#824145 : Adding a setup script for quantum * merge trunk pep8 fixes adapting CLI to API v1.0 Fixing wsgi to avoid failure with extensions * merge trunk * Pulling in changes from lp:quantum * Merging Cisco's contribution to Quantum. Thanks to various folks at Cisco Systems, Quantum will have plugins to integrate with Cisco UCS blade servers using 802.1Qbh, Cisco Nexus family of switches and the ability for Quantum plugin to have multiple switches/devices within a single Quantum plugin * Merging from Sumit's branch pylint fixes and incorporating review comments * Mergin from cisco brach * Merging from lp:quantum * Introducting cheetah Updating list\_nets in CLI Writing unit tests for list\_nets Stubbing out with FakeConnection now * Merging quantum extenions framework into trunk. Thanks rajaram vinkesh, deepak & santhosh for the great work! * lp Bug#824145 : Adding a setup script for quantum * skeleton for cli unit tests * merge trunk * Merged quantum trunk * - Adding setup script * force batch\_config.py to use json, as XML has issues (see bug: 798262) * update batch\_config.py to use new client lib, hooray for deleting code * Merging changes addressing Bug # 802772. Thanks lp:danwent ! * Merging bugfix for Bug 822890 - Added License file for Quantum code distribution * L2 Network Plugin Framework merge * Adding Apache Version 2.0 license file. This is the official license agreement under which Quantum code is available to the Open Source community * merge * merge heckj's pip-requires fixes * updates to pip-requires for CI * Merged quantum trunk * Merging changes from lp:quantum * Completing API spec alignment Unit tests aligned with changes in the API spec * Merging the brand new Quantum-client-library feature * Merging lp:quantum updates * persistence of l2network & ucs plugins using mysql - db\_conn.ini - configuration details of making a connection to the database - db\_test\_plugin.py - contains abstraction methods for storing database values in a dict and unit test cases for DB testing - l2network\_db.py - db methods for l2network models - l2network\_models.py - class definitions for the l2 network tables - ucs\_db.py - db methods for ucs models - ucs\_models.py - class definition for the ucs tables dynamic loading of the 2nd layer plugin db's based on passed arguments Create, Delete, Get, Getall, Update database methods at - Quantum, L2Network and Ucs Unit test cases for create, delete, getall and update operations for L2Network and Ucs plugins pep8 checks done branch based off revision 34 plugin-framework * Merged from trunk * merged the latest changes from plugin-framework branch - revision 39 conforming to the new cisco plugin directory structure and moving all db related modules into cisco/db folder updated db\_test\_plugin.py - added import of cisco constants module - added LOG.getLogger for logging component name - updated import module paths for l2network\_models/db and ucs\_models/db to use the new directory structure - updated (rearranged) imports section to obey openstack alphabetical placement convention updated db\_conn.ini - updated database name from cisco\_naas to quantum\_l2network unit test cases ran successfully and pep8 checks done again * merge branch for to fix bug817826 * Merging the latest changes from lp:quantum * fix bug 817826 and similar error in batch\_config.py * merge Salvatore's api branch with fixes for tests. Tweaking branch to remove unwanted bin/quantum.py as part of merge * Santhosh/Rajaram|latest merge from quantum and made extensions use options to load plugin * Apply fix for bug #797419 merging lp:~salvatore-orlando/quantum/bug797419 * Merging branch lp:~netstack/quantum/quantum-unit-tests * Merged from quantum trunk * Adapated plugin infrastructure to allow API to pass options to plugins Now using in-memory sqlite db for tests on FakePlugin teardown() now 'resets' the in-memory db Adding unit tests for APIs * Adding Routes>=1.12.3 to tools/pip-requires * Merging dan wendlandt's bugfixes for Bug #800466 and improvements that enable Quantum to seamlessly run on KVM! * more pep8 goodness * refactor batch\_config, allow multiple attaches with the empty string * merge and pep8 cleanup * Merging latest changes from parent repo - lp:network-service , Parent repo had approved merge proposal for merging lp:~santhom/network-service/quantum\_testing\_framework , which has now been merged into lp:network-service * Merging pep8 and functional test related changes lp:~santhom/network-service/quantum\_testing\_framework branch * add example to usage string for batch\_config.py * Bug fixes and clean-up, including supporting libvirt * Santhosh/Vinkesh | Added the testing framework. Moved the smoketest to tests/functional * Pushing initial started code based on Glance project and infrstructure work done by the melange team * Merging in latest changes from lp:quantum neutron-fwaas-12.0.2/neutron_fwaas/0000775000175000017500000000000013555600005017261 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/services/0000775000175000017500000000000013555600005021104 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/services/firewall/0000775000175000017500000000000013555600005022711 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/services/firewall/agents/0000775000175000017500000000000013555600005024172 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/services/firewall/agents/firewall_service.py0000664000175000017500000000314313555577713030115 0ustar zuulzuul00000000000000# 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) neutron-fwaas-12.0.2/neutron_fwaas/services/firewall/agents/l3reference/0000775000175000017500000000000013555600005026367 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/services/firewall/agents/l3reference/firewall_l3_agent.py0000664000175000017500000004361713555577713032360 0ustar zuulzuul00000000000000# 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 neutron_lib.agent import l3_extension from neutron_lib import constants as nl_constants from neutron_lib import context from neutron_lib.exceptions import firewall_v1 as fw_ext from oslo_config import cfg from oslo_log import helpers as log_helpers from oslo_log import log as logging from neutron_fwaas.common import fwaas_constants from neutron_fwaas.common import resources as f_resources from neutron_fwaas.services.firewall.agents import firewall_agent_api as api from neutron_fwaas.services.firewall.agents import firewall_service 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("FWaaS RPC call failed; L3 agent_api failure") return self.agent_api.get_routers_in_project(project_id) 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("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("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( "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("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("Firewall Driver Error for create_firewall " "for firewall: %s", 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("FWaaS RPC failure in create_firewall " "for firewall: %s", 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( "Firewall Driver Error for " "update_firewall for firewall: %s", 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( "Firewall Driver Error for " "update_firewall for firewall: %s", 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("FWaaS RPC failure in update_firewall " "for firewall: %s", 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("Firewall Driver Error for delete_firewall " "for firewall: %s", 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("FWaaS RPC failure in delete_firewall " "for firewall: %s", firewall['id']) self.services_sync_needed = True def ha_state_change(self, context, data): pass 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) neutron-fwaas-12.0.2/neutron_fwaas/services/firewall/agents/l3reference/firewall_l3_agent_v2.py0000664000175000017500000005575113555577713032771 0ustar zuulzuul00000000000000# 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.agent import l3_extension from neutron_lib import constants as nl_constants from neutron_lib import context from neutron_lib.exceptions import firewall_v2 as fw_ext from oslo_config import cfg from oslo_log import helpers as log_helpers from oslo_log import log as logging from neutron_fwaas.common import fwaas_constants from neutron_fwaas.common import resources as f_resources from neutron_fwaas.services.firewall.agents import firewall_agent_api as api from neutron_fwaas.services.firewall.agents import firewall_service 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): local_ns_list = ip_lib.list_network_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): """Get ports in namespace by their IDs. Returns port objects in the local namespace, along with their router_info. :param port_ids: IDs of router ports (set, list or tuple) """ 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, ports, firewall_group): """Call driver to sync 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. :param ctx: RPC context :param ports: IDs of ports associated with a firewall group (set, list or tuple) :param firewall_group: Dictionary describing the firewall group object """ port_list = self._get_in_ns_ports(ports) 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 = ("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 = ("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) if nl_constants.INTERFACE_KEY not in updated_router: return # Apply a firewall group, as requested, to ports on the new router. all_router_ports = set( p['id'] for p in updated_router[nl_constants.INTERFACE_KEY] ) processed_ports = set() for firewall_group in fwg_list: if not self._has_port_insertion_fields(firewall_group): continue ports_to_process = (set(firewall_group['add-port-ids'] + firewall_group['del-port-ids']) & all_router_ports) # A port can have at most one firewall group. port_ids_to_exclude = ports_to_process & processed_ports if port_ids_to_exclude: LOG.warning("Port(s) %s is associated with " "more than one firewall group(s).", port_ids_to_exclude) ports_to_process -= port_ids_to_exclude self._invoke_driver_for_sync_from_plugin( ctx, ports_to_process, firewall_group) processed_ports |= ports_to_process 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("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( "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("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 = ("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 = ("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. del_fwg_ports = self._get_firewall_group_ports( context, firewall_group, to_delete=True, require_new_plugin=True) add_fwg_ports = self._get_firewall_group_ports(context, firewall_group) port_ids = (firewall_group.get('del-port-ids') + firewall_group.get('add-port-ids')) if port_ids and not (del_fwg_ports or add_fwg_ports): LOG.debug("All ports are not router port." "No need to update firewall driver.") return # Remove firewall group from ports if requested. if del_fwg_ports: fw_ports = [p for ri_port in del_fwg_ports for p in ri_port[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, del_fwg_ports, firewall_group) except fw_ext.FirewallInternalDriverError: msg = ("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): if add_fwg_ports: fw_ports = [p for ri_port in add_fwg_ports for p in ri_port[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, add_fwg_ports, firewall_group) except fw_ext.FirewallInternalDriverError: msg = ("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("FWaaS RPC failure in update_firewall_group " "for firewall group: %s", 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("FWaaS driver error in delete_firewall_group " "for firewall group: %s", 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("FWaaS RPC failure in delete_firewall_group " "for firewall group: %s", firewall_group['id']) self.services_sync_needed = True def ha_state_change(self, context, data): pass 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) neutron-fwaas-12.0.2/neutron_fwaas/services/firewall/agents/l3reference/__init__.py0000664000175000017500000000000013555577713030511 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/services/firewall/agents/__init__.py0000664000175000017500000000000013555577713026314 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/services/firewall/agents/l2/0000775000175000017500000000000013555600005024507 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/services/firewall/agents/l2/fwaas_v2.py0000664000175000017500000004537113555577713026626 0ustar zuulzuul00000000000000# Copyright 2017-2018 FUJITSU LIMITED # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_concurrency import lockutils from oslo_config import cfg from oslo_log import log as logging import six from neutron.agent import securitygroups_rpc from neutron.common import rpc as n_rpc from neutron import manager from neutron.plugins.ml2.drivers.openvswitch.agent import vlanmanager from neutron_lib.agent import l2_extension from neutron_lib import constants as nl_const from neutron_lib.exceptions import firewall_v2 as f_exc from neutron_lib.utils import net as nl_net from neutron_fwaas._i18n import _ from neutron_fwaas.common import fwaas_constants as consts from neutron_fwaas.services.firewall.agents import firewall_agent_api as api LOG = logging.getLogger(__name__) FWAAS_L2_DRIVER = 'neutron.agent.l2.firewall_drivers' SG_OVS_DRIVER = 'openvswitch' class FWaaSL2PluginApi(api.FWaaSPluginApiMixin): """L2 agent side of FWaaS agent-to-plugin RPC API""" def get_firewall_group_for_port(self, context, port_id): """Get firewall group is associated with a port""" LOG.debug("Get firewall group is associated with port %s", port_id) cctxt = self.client.prepare() return cctxt.call(context, 'get_firewall_group_for_port', port_id=port_id) def set_firewall_group_status(self, context, fwg_id, status, host): """Set the status of a group operation.""" LOG.debug("Fetch firewall group changing status") cctxt = self.client.prepare() return cctxt.call(context, 'set_firewall_group_status', fwg_id=fwg_id, status=status, host=host) def firewall_group_deleted(self, context, fwg_id, host): """Notifies the plugin that a firewall group has been deleted.""" LOG.debug("Notify to the plugin that firewall group has been deleted") cctxt = self.client.prepare() return cctxt.call(context, 'firewall_group_deleted', fwg_id=fwg_id, host=host) class FWaaSV2AgentExtension(l2_extension.L2AgentExtension): def initialize(self, connection, driver_type): """Perform Agent Extension initialization""" self.conf = cfg.CONF int_br = self.agent_api.request_int_br() self.vlan_manager = vlanmanager.LocalVlanManager() fw_l2_driver_cls = self._load_l2_driver_class(driver_type) sg_enabled = securitygroups_rpc.is_firewall_enabled() sg_firewall_driver = self.conf.SECURITYGROUP.firewall_driver sg_with_ovs = sg_enabled and (sg_firewall_driver == SG_OVS_DRIVER) self.driver = manager.NeutronManager.load_class_for_provider( FWAAS_L2_DRIVER, fw_l2_driver_cls)(int_br, sg_with_ovs) self.plugin_rpc = FWaaSL2PluginApi( consts.FIREWALL_PLUGIN, self.conf.host) self.start_rpc_listeners() self.fwg_map = PortFirewallGroupMap() def consume_api(self, agent_api): self.agent_api = agent_api def start_rpc_listeners(self): self.conn = n_rpc.create_connection() endpoints = [self] self.conn.create_consumer(consts.FW_AGENT, endpoints, fanout=False) return self.conn.consume_in_threads() def _load_l2_driver_class(self, driver_type): driver = self.conf.fwaas.firewall_l2_driver or 'noop' if driver == api.FW_L2_NOOP_DRIVER: return driver if driver != driver_type: raise Exception( _("Firewall l2 driver: %s is not compatible"), driver_type) return driver def _is_port_layer2(self, port): """This function checks if a port belongs to a L2 case. Currently both DHCP and router ports are eliminated. """ return port and port.get('device_owner', '').startswith( nl_const.DEVICE_OWNER_COMPUTE_PREFIX) def _get_firewall_group_ports(self, fwg, host, to_delete=False): port_list = [] port_ids = fwg['del-port-ids'] if to_delete else fwg['add-port-ids'] LOG.debug("_get_fwg fwg=%(fwg)s ports=%(port)s to_delete=%(delete)s", {'fwg': fwg, 'port': port_ids, 'delete': to_delete}) for fw_port in port_ids: port_detail = fwg['port_details'].get(fw_port) if (self._is_port_layer2(port_detail) and port_detail.get('host') == host): port_list.append(port_detail) return port_list @staticmethod def _has_ports(fwg, event): """Verifying fwg has ports or not This function verify applying firewall group on ports :param fwg: a fwg object :param event: create/update firewall group or create/update/delete port :return: True if applying firewall group is fine. Otherwise is False """ if event == consts.UPDATE_FWG and 'last-port' in fwg: return not fwg['last-port'] else: return bool(fwg['ports']) @staticmethod def _has_policy(fwg): """Verifying fwg has policy or not""" return bool(fwg['ingress_firewall_policy_id'] or fwg['egress_firewall_policy_id']) def _compute_status(self, fwg, result, event=consts.CREATE_FWG): """Compute a status of specified firewall group for update Validates 'ACTIVE', 'DOWN', 'INACTIVE', 'ERROR' and None as follows: - "ERROR" : if result is not True - "ACTIVE" : admin_state_up is True and exists ports - "INACTIVE" : admin_state_up is True and with no ports - "DOWN" : admin_state_up is False - None : In case of 'delete_firewall_group' """ if not result: return nl_const.ERROR if not fwg['admin_state_up']: return nl_const.DOWN if event == consts.DELETE_FWG: # This firewall_group will be deleted. No need to update status. return if (self._has_ports(fwg, event) and self._has_policy(fwg)): return nl_const.ACTIVE return nl_const.INACTIVE def _get_network_id(self, fwg_port): port_id = fwg_port.get('port_id', fwg_port.get('id')) port_details = fwg_port.get('port_details') if port_details: target = port_details.get(port_id) if target: return target.get('network_id') return return fwg_port.get('network_id') def _add_local_vlan_to_ports(self, fwg_ports): """Add local VLAN to ports if found This function tries to add local VLAN related to ports. """ ports_with_lvlan = [] for fwg_port in fwg_ports: try: network_id = self._get_network_id(fwg_port) l_vlan = self.vlan_manager.get(network_id).vlan fwg_port['lvlan'] = int(l_vlan) except vlanmanager.MappingNotFound: LOG.warning("No Local VLAN found in network %s", network_id) # NOTE(yushiro): We ignore this exception because we should send # all selected ports to driver layer. It depends on driver's # behavior whether it occurs an error with no local VLAN or not. ports_with_lvlan.append(fwg_port) return ports_with_lvlan def _apply_fwg_rules(self, fwg, ports, event=consts.UPDATE_FWG): """This function invokes the driver create/update routine. """ # Set firewall group status; will be overwritten if call to driver # fails. if event in [consts.CREATE_FWG, consts.UPDATE_FWG]: ports_for_driver = self._add_local_vlan_to_ports(ports) else: ports_for_driver = ports # apply firewall group to driver try: if event == consts.UPDATE_FWG: self.driver.update_firewall_group(ports_for_driver, fwg) elif event == consts.DELETE_FWG: self.driver.delete_firewall_group(ports_for_driver, fwg) elif event == consts.CREATE_FWG: self.driver.create_firewall_group(ports_for_driver, fwg) except f_exc.FirewallInternalDriverError: msg = _("FWaaS driver error in %(event)s_firewall_group " "for firewall group: %(fwg_id)s") LOG.exception(msg, {'event': event, 'fwg_id': fwg['id']}) return False return True def _send_fwg_status(self, context, fwg_id, status, host): """Send firewall group's status to plugin. :returns: True if no exception occurred otherwise False :rtype: boolean """ try: self.plugin_rpc.set_firewall_group_status( context, fwg_id, status, host) LOG.debug("Successfully sent status(%s) for firewall_group(%s)", status, fwg_id) except Exception: msg = _("Failed to send status for firewall_group(%s)") LOG.exception(msg, fwg_id) def _create_firewall_group(self, context, fwg, host, event=consts.CREATE_FWG): """Handles RPC from plugin to create a firewall group. """ add_ports = self._get_firewall_group_ports(fwg, host) if not add_ports: status = nl_const.INACTIVE else: ret = self._apply_fwg_rules(fwg, add_ports, event) # cleanup port_map for port in add_ports: self.fwg_map.remove_port(port) status = self._compute_status(fwg, ret, event) for port in add_ports: self.fwg_map.set_port_fwg(port, fwg) # Update status of firewall group which is associated with ports # after updating. self._send_fwg_status(context, fwg['id'], status, host) def _delete_firewall_group(self, context, fwg, host, event=consts.DELETE_FWG): """Handles RPC from plugin to delete a firewall group. """ del_ports = self._get_firewall_group_ports(fwg, host, to_delete=True) if not del_ports: return # cleanup all flows of del_ports ret = self._apply_fwg_rules(fwg, del_ports, event=consts.DELETE_FWG) del_port_ids = [] for port in del_ports: del_port_ids.append(port['id']) self.fwg_map.remove_port(port) if event == consts.DELETE_FWG: self.fwg_map.remove_fwg(fwg) self.plugin_rpc.firewall_group_deleted( context, fwg['id'], host=self.conf.host) else: status = self._compute_status(fwg, ret, event) self._send_fwg_status(context, fwg['id'], status, self.conf.host) @lockutils.synchronized('fwg') def create_firewall_group(self, context, firewall_group, host): """Handles create firewall group event""" # TODO(chandanc): Fix agent RPC endpoint to remove host arg host = cfg.CONF.host with self.driver.defer_apply(): try: self._create_firewall_group(context, firewall_group, host) except Exception as exc: LOG.exception( "Exception caught in create_firewall_group %s", exc) self._send_fwg_status(context, firewall_group['id'], status=nl_const.ERROR, host=host) @lockutils.synchronized('fwg') def delete_firewall_group(self, context, firewall_group, host): """Handles delete firewall group event""" # TODO(chandanc): Fix agent RPC endpoint to remove host arg host = cfg.CONF.host with self.driver.defer_apply(): try: self._delete_firewall_group(context, firewall_group, host) except Exception as exc: LOG.exception( "Exception caught in delete_firewall_group %s", exc) self._send_fwg_status(context, firewall_group['id'], status=nl_const.ERROR, host=host) @lockutils.synchronized('fwg') def update_firewall_group(self, context, firewall_group, host): """Handles update firewall group event""" # TODO(chandanc): Fix agent RPC endpoint to remove host arg host = cfg.CONF.host with self.driver.defer_apply(): try: self._delete_firewall_group( context, firewall_group, host, event=consts.UPDATE_FWG) self._create_firewall_group( context, firewall_group, host, event=consts.UPDATE_FWG) except Exception as exc: LOG.exception( "Exception caught in update_firewall_group %s", exc) self._send_fwg_status(context, firewall_group['id'], status=nl_const.ERROR, host=host) @lockutils.synchronized('fwg-port') def handle_port(self, context, port): """Handle port update event""" # Check if port is trusted and called at once. if nl_net.is_port_trusted(port) and not self.fwg_map.get_port(port): self._add_rule_for_trusted_port(port) self.fwg_map.set_port(port) return if not self._is_port_layer2(port): return # check if port is already assigned to a fwg if self.fwg_map.get_port_fwg(port): return fwg = self.plugin_rpc.get_firewall_group_for_port( context, port.get('port_id')) if not fwg: LOG.info("Firewall group applied to port %s is " "not available on server.", port['port_id']) return ret = self._apply_fwg_rules(fwg, [port]) status = self._compute_status(fwg, ret, event=consts.HANDLE_PORT) self.fwg_map.set_port_fwg(port, fwg) self._send_fwg_status( context, fwg_id=fwg['id'], status=status, host=self.conf.host) def _add_rule_for_trusted_port(self, port): self._add_local_vlan_to_ports([port]) self.driver.process_trusted_ports([port]) def _delete_rule_for_trusted_port(self, port): self.driver.remove_trusted_ports([port['port_id']]) def delete_port(self, context, port): """This is being called when a port is deleted by the agent. """ # delete_port should be handled only unbound timing for a port. # If 'vif_port' is included in the port dict, this is called after # deleted the port and should be ignored. if 'vif_port' in port: return port = self.fwg_map.get_port(port) if port and nl_net.is_port_trusted(port): self._delete_rule_for_trusted_port(port) self.fwg_map.remove_port(port) return if not self._is_port_layer2(port): return fwg = self.fwg_map.get_port_fwg(port) if not fwg: LOG.info("Firewall group associated to port %(port_id)s is " "not available on server.", {'port_id': port['port_id']}) return ret = self._apply_fwg_rules(fwg, [port], event=consts.DELETE_FWG) port_id = self.fwg_map.port_id(port) if port_id in fwg['ports']: fwg['ports'].remove(port_id) # update the fwg dict to known_fwgs self.fwg_map.set_fwg(fwg) self.fwg_map.remove_port(port) status = self._compute_status(fwg, ret, event=consts.DELETE_PORT) self._send_fwg_status(context, fwg['id'], status, self.conf.host) class PortFirewallGroupMap(object): """Store relations between Port and Firewall Group and trusted port This map is used in deleting firewall_group because the firewall_group has been deleted at that time. Therefore, it is impossible to refer 'ports'. This map enables to refer 'ports' for specified firewall_group. Furthermore, it is necessary to check 'device_owner' for trusted port, this Map also stores trusted port data. """ def __init__(self): self.known_fwgs = {} self.port_fwg = {} self.port_detail = {} # TODO(yushiro): If agent is restarted, this map doesn't have any # information. Need to consider map initialization in __init__() def port_id(self, port): return (port if isinstance(port, six.string_types) else port.get('port_id', port.get('id'))) def get_fwg(self, fwg_id): return self.known_fwgs.get(fwg_id) def set_fwg(self, fwg): self.known_fwgs[fwg['id']] = fwg def get_port(self, port): return self.port_detail.get(self.port_id(port)) def get_port_fwg(self, port): fwg_id = self.port_fwg.get(self.port_id(port)) if fwg_id: return self.get_fwg(fwg_id) def set_port(self, port): """Add a new port into port_detail""" port_id = self.port_id(port) self.port_detail[port_id] = port def set_port_fwg(self, port, fwg): """Add a new port into fwg['ports']""" port_id = self.port_id(port) # Update fwg['ports'] data fwg['ports'] = list(set(fwg['ports'] + [port_id])) # Update fwg_id -> firewall_group data self.known_fwgs[fwg['id']] = fwg # Update port_id -> port data self.port_detail[port_id] = port # Update port_id -> firewall_group_id relation self.port_fwg[port_id] = fwg['id'] def remove_port(self, port): """Remove port from fwg['ports'] and port_fwg dictionary When removing 'port' from several cases, the port should be removed from this map. """ port_id = self.port_id(port) # Check if 'port_id' has registered in port_fwg dictionary. # Update firewall_group if port_id in self.port_fwg: fwg_id = self.port_fwg.get(port_id) if not fwg_id: # This case is trusted port. Try to delete port_detail dict try: del self.port_detail[port_id] except KeyError: pass return new_fwg = self.known_fwgs[fwg_id] new_fwg['ports'] = [p for p in new_fwg['ports'] if p != port_id] self.known_fwgs[fwg_id] = new_fwg del self.port_fwg[port_id] del self.port_detail[port_id] def remove_fwg(self, fwg): """Remove firewall_group from known_fwgs dictionary When removing firewall_group, it should be removed from this map """ if fwg['id'] in self.known_fwgs: del self.known_fwgs[fwg['id']] neutron-fwaas-12.0.2/neutron_fwaas/services/firewall/agents/l2/__init__.py0000664000175000017500000000000013555577713026631 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/services/firewall/agents/firewall_agent_api.py0000664000175000017500000000554113555577713030410 0ustar zuulzuul00000000000000# 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" FW_L2_NOOP_DRIVER = 'noop' 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_V2, help=_("Firewall agent class")), cfg.StrOpt( 'conntrack_driver', default='conntrack', help=_("Name of the FWaaS Conntrack Driver")), cfg.StrOpt( 'firewall_l2_driver', default=FW_L2_NOOP_DRIVER, help=_("Name of the firewall l2 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 neutron-fwaas-12.0.2/neutron_fwaas/services/firewall/drivers/0000775000175000017500000000000013555600005024367 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/services/firewall/drivers/linux/0000775000175000017500000000000013555600005025526 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/services/firewall/drivers/linux/iptables_fwaas.py0000664000175000017500000004441613555577713031120 0ustar zuulzuul00000000000000# 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 neutron.agent.linux import iptables_manager from neutron.common import utils from neutron_lib import constants from neutron_lib.exceptions import firewall_v1 as f_exc from oslo_log import log as logging from neutron_fwaas.common import fwaas_constants as f_const from neutron_fwaas.services.firewall.drivers import conntrack_base from neutron_fwaas.services.firewall.drivers import fwaas_base 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'} CHAIN_NAME_PREFIX = {constants.INGRESS_DIRECTION: 'i', constants.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 = {constants.INGRESS_DIRECTION: '-o', constants.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 self.conntrack = conntrack_base.load_and_init_conntrack_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("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.rtr_fip_connect: # 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("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("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( "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, constants.INGRESS_DIRECTION) ochain_name = self._get_chain_name( fwid, ver, constants.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, constants.INGRESS_DIRECTION) ochain_name = self._get_chain_name( fwid, ver, constants.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 [constants.INGRESS_DIRECTION, constants.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 [constants.INGRESS_DIRECTION, constants.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 (rule.get('source_port') is not None or rule.get('destination_port') is not 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 neutron-fwaas-12.0.2/neutron_fwaas/services/firewall/drivers/linux/netlink_conntrack.py0000664000175000017500000001274013555577713031635 0ustar zuulzuul00000000000000# 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 neutron_lib import constants 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 rule_protocol == constants.PROTO_NAME_IPV6_ICMP: rule_protocol = constants.PROTO_NAME_IPV6_ICMP_LEGACY 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 neutron-fwaas-12.0.2/neutron_fwaas/services/firewall/drivers/linux/iptables_fwaas_v2.py0000664000175000017500000004721013555577713031522 0ustar zuulzuul00000000000000# 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.common import utils from neutron_lib import constants from neutron_lib.exceptions import firewall_v2 as fw_ext from oslo_log import log as logging from neutron_fwaas.services.firewall.drivers import conntrack_base 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'} CHAIN_NAME_PREFIX = {constants.INGRESS_DIRECTION: 'i', constants.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 = {constants.INGRESS_DIRECTION: '-o', constants.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 self.conntrack = conntrack_base.load_and_init_conntrack_driver() 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("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.rtr_fip_connect: # 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("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("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( "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, constants.INGRESS_DIRECTION) ochain_name = self._get_chain_name( fwid, ver, constants.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, constants.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, constants.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 _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'] 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""" 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 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 [constants.INGRESS_DIRECTION, constants.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 [constants.INGRESS_DIRECTION, constants.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 (rule.get('source_port') is not None or rule.get('destination_port') is not 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 neutron-fwaas-12.0.2/neutron_fwaas/services/firewall/drivers/linux/__init__.py0000664000175000017500000000000013555577713027650 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/services/firewall/drivers/linux/legacy_conntrack.py0000664000175000017500000002206113555577713031432 0ustar zuulzuul00000000000000# 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 neutron.agent.linux import utils as linux_utils from neutron_lib import constants from oslo_log import log as logging from neutron_fwaas.services.firewall.drivers import conntrack_base LOG = logging.getLogger(__name__) IP_VERSIONS = [constants.IP_VERSION_4, constants.IP_VERSION_6] ATTR_POSITIONS = { 'icmp': (('type', 5), ('code', 6), ('src', 3), ('dst', 4), ('id', 7)), 'icmpv6': (('type', 5), ('code', 6), ('src', 3), ('dst', 4), ('id', 7)), 'tcp': (('sport', 6), ('dport', 7), ('src', 4), ('dst', 5)), 'udp': (('sport', 5), ('dport', 6), ('src', 3), ('dst', 4)) } 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): rule_filters = sorted(self._get_filter_from_rule(r) for r in rules) delete_entries = self._get_entries_to_delete( rule_filters, self.list_entries(namespace)) for delete_entry in delete_entries: cmd = self._get_conntrack_cmd_from_entry(delete_entry, namespace) self._execute_command(cmd) def _execute_command(self, cmd): try: output = 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) return output def list_entries(self, namespace): """List and parse all conntrack entries :param namespace: namespace to get conntrack entries :returns: sorted list of conntrack entries in Python tuple for 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 = [] prefixcmd = ['ip', 'netns', 'exec', namespace] if namespace else [] for ip_version in IP_VERSIONS: cmd = prefixcmd + ['conntrack', '-L', '-f', 'ipv' + str(ip_version)] raw_entries = self._execute_command(cmd).splitlines() for raw_entry in raw_entries: parsed_entry = self._parse_entry(raw_entry.split(), ip_version) if parsed_entry is not None: parsed_entries.append(parsed_entry) return sorted(parsed_entries) def _get_conntrack_cmd_from_entry(self, entry, namespace): prefixcmd = ['ip', 'netns', 'exec', namespace] if namespace else [] cmd = ['conntrack', '-D'] contrack_filter = ['-f', 'ipv' + str(entry[0]), '-p', entry[1]] if entry[1] in ['icmp', 'icmpv6']: contrack_filter.extend(['--icmp-type', entry[2], '--icmp-code', entry[3], '-s', entry[4], '-d', entry[5], '--icmp-id', entry[6]]) else: contrack_filter.extend(['--sport', entry[2], '--dport', entry[3], '-s', entry[4], '-d', entry[5]]) exec_cmd = prefixcmd + cmd + contrack_filter return exec_cmd def _parse_entry(self, entry, ip_version): """Parse entry from text to Python tuple :param entry: conntrack entry as a list of string :param ip_version: ip version 4 or 6 :returns: conntrack entry in Python tuple for 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[0] if protocol not in ATTR_POSITIONS: LOG.warning( 'Skipping conntrack entry %s with unsupported protocol', entry) return None parsed_entry = [ip_version, 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) 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 :returns: 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_filter( 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 :returns: 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_filter(rule_filter, entry): """Define that the entry should be deleted or not :param rule_filter: filter that is parsed from a firewall rule for example: (4, 'tcp', ['22', '33'], ['44', '55']) :param entry: parsed conntrack entry, for example: (4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2') :returns: -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_ip_version = rule_filter[0] if entry[0] < rule_ip_version: return ENTRY_IS_LOWER elif entry[0] > rule_ip_version: return ENTRY_IS_HIGHER rule_protocol = rule_filter[1] if rule_protocol == constants.PROTO_NAME_IPV6_ICMP: rule_protocol = constants.PROTO_NAME_IPV6_ICMP_LEGACY 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 neutron-fwaas-12.0.2/neutron_fwaas/services/firewall/drivers/linux/l2/0000775000175000017500000000000013555600005026043 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/services/firewall/drivers/linux/l2/openvswitch_firewall/0000775000175000017500000000000013555600005032301 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000015100000000000011212 Lustar 00000000000000neutron-fwaas-12.0.2/neutron_fwaas/services/firewall/drivers/linux/l2/openvswitch_firewall/exceptions.pyneutron-fwaas-12.0.2/neutron_fwaas/services/firewall/drivers/linux/l2/openvswitch_firewall/exception0000664000175000017500000000166013555577713034250 0ustar zuulzuul00000000000000# Copyright 2016, Red Hat, 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 exceptions from neutron_fwaas._i18n import _ class OVSFWaaSPortNotFound(exceptions.NeutronException): message = _("Port %(port_id)s is not managed by this agent.") class OVSFWaaSTagNotFound(exceptions.NeutronException): message = _("Cannot get vlan tag for port %(port_id)s.") neutron-fwaas-12.0.2/neutron_fwaas/services/firewall/drivers/linux/l2/openvswitch_firewall/rules.py0000664000175000017500000001667213555577713034044 0ustar zuulzuul00000000000000# Copyright 2015 Red Hat, 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 netaddr from neutron_lib import constants as n_consts from oslo_log import log as logging from neutron.common import utils from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants \ as ovs_consts from neutron_fwaas.services.firewall.drivers.linux.l2.openvswitch_firewall \ import constants as fwaas_ovs_consts LOG = logging.getLogger(__name__) # NOTE(ivasilevskaya) copy-paste from neutron ovsfw driver, differs in # constants CT_STATES = [ fwaas_ovs_consts.OF_STATE_ESTABLISHED_NOT_REPLY, fwaas_ovs_consts.OF_STATE_NEW_NOT_ESTABLISHED] # NOTE(ivasilevskaya) copy-paste from neutron ovsfw driver FLOW_FIELD_FOR_IPVER_AND_DIRECTION = { (n_consts.IP_VERSION_4, n_consts.EGRESS_DIRECTION): 'nw_dst', (n_consts.IP_VERSION_6, n_consts.EGRESS_DIRECTION): 'ipv6_dst', (n_consts.IP_VERSION_4, n_consts.INGRESS_DIRECTION): 'nw_src', (n_consts.IP_VERSION_6, n_consts.INGRESS_DIRECTION): 'ipv6_src', } # NOTE(ivasilevskaya) copy-paste from neutron ovsfw driver FORBIDDEN_PREFIXES = (n_consts.IPv4_ANY, n_consts.IPv6_ANY) # NOTE(ivasilevskaya) copy-paste from neutron ovsfw driver def is_valid_prefix(ip_prefix): # IPv6 have multiple ways how to describe ::/0 network, converting to # IPNetwork and back to string unifies it return (ip_prefix and str(netaddr.IPNetwork(ip_prefix)) not in FORBIDDEN_PREFIXES) # NOTE(ivasilevskaya) copy-paste from neutron ovsfw driver def create_flows_from_rule_and_port(rule, port): ethertype = rule['ethertype'] direction = rule['direction'] dst_ip_prefix = rule.get('dest_ip_prefix') src_ip_prefix = rule.get('source_ip_prefix') offset = int(rule.get('offset', 0)) flow_template = { 'priority': 70 + offset, 'dl_type': fwaas_ovs_consts.ethertype_to_dl_type_map[ethertype], 'reg_port': port.ofport, } if is_valid_prefix(dst_ip_prefix): flow_template[FLOW_FIELD_FOR_IPVER_AND_DIRECTION[( utils.get_ip_version(dst_ip_prefix), n_consts.EGRESS_DIRECTION)] ] = dst_ip_prefix if is_valid_prefix(src_ip_prefix): flow_template[FLOW_FIELD_FOR_IPVER_AND_DIRECTION[( utils.get_ip_version(src_ip_prefix), n_consts.INGRESS_DIRECTION)] ] = src_ip_prefix flows = create_protocol_flows(direction, flow_template, port, rule) return flows # NOTE(ivasilevskaya) copy-paste from neutron ovsfw driver, differs in # constants def populate_flow_common(direction, flow_template, port): """Initialize common flow fields.""" if direction == n_consts.INGRESS_DIRECTION: flow_template['table'] = fwaas_ovs_consts.FW_RULES_INGRESS_TABLE flow_template['actions'] = "output:{:d}".format(port.ofport) elif direction == n_consts.EGRESS_DIRECTION: flow_template['table'] = fwaas_ovs_consts.FW_RULES_EGRESS_TABLE # Traffic can be both ingress and egress, check that no ingress rules # should be applied flow_template['actions'] = 'resubmit(,{:d})'.format( fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE) return flow_template # NOTE(ivasilevskaya) copy-paste from neutron ovsfw driver def create_protocol_flows(direction, flow_template, port, rule): flow_template = populate_flow_common(direction, flow_template.copy(), port) protocol = rule.get('protocol') if protocol is not None: flow_template['nw_proto'] = protocol if protocol in [n_consts.PROTO_NUM_ICMP, n_consts.PROTO_NUM_IPV6_ICMP]: flows = create_icmp_flows(flow_template, rule) else: flows = create_port_range_flows(flow_template, rule) return flows or [flow_template] # NOTE(ivasilevskaya) copy-paste from neutron ovsfw driver, differs only in # constant def create_port_range_flows(flow_template, rule): protocol = fwaas_ovs_consts.REVERSE_IP_PROTOCOL_MAP_WITH_PORTS.get( rule.get('protocol')) if protocol is None: return [] flows = [] src_port_match = '{:s}_src'.format(protocol) src_port_min = rule.get('source_port_range_min') src_port_max = rule.get('source_port_range_max') dst_port_match = '{:s}_dst'.format(protocol) dst_port_min = rule.get('port_range_min') dst_port_max = rule.get('port_range_max') dst_port_range = [] if dst_port_min and dst_port_max: dst_port_range = utils.port_rule_masking(dst_port_min, dst_port_max) src_port_range = [] if src_port_min and src_port_max: src_port_range = utils.port_rule_masking(src_port_min, src_port_max) for port in src_port_range: flow = flow_template.copy() flow[src_port_match] = port if dst_port_range: for port in dst_port_range: dst_flow = flow.copy() dst_flow[dst_port_match] = port flows.append(dst_flow) else: flows.append(flow) else: for port in dst_port_range: flow = flow_template.copy() flow[dst_port_match] = port flows.append(flow) return flows # NOTE(ivasilevskaya) copy-paste from neutron ovsfw driver def create_icmp_flows(flow_template, rule): icmp_type = rule.get('port_range_min') if icmp_type is None: return flow = flow_template.copy() flow['icmp_type'] = icmp_type icmp_code = rule.get('port_range_max') if icmp_code is not None: flow['icmp_code'] = icmp_code return [flow] def resubmit_to_sg(flow): if flow['table'] == fwaas_ovs_consts.FW_RULES_EGRESS_TABLE: flow['actions'] = 'resubmit(,{:d})'.format( ovs_consts.RULES_EGRESS_TABLE) if flow['table'] == fwaas_ovs_consts.FW_RULES_INGRESS_TABLE: flow['actions'] = 'resubmit(,{:d})'.format( ovs_consts.RULES_INGRESS_TABLE) def create_accept_flows(flow, sg_enabled=False): flow['ct_state'] = CT_STATES[0] if sg_enabled: resubmit_to_sg(flow) result = [flow.copy()] flow['ct_state'] = CT_STATES[1] if sg_enabled: resubmit_to_sg(flow) elif flow['table'] == fwaas_ovs_consts.FW_RULES_INGRESS_TABLE: flow['actions'] = ( 'ct(commit,zone=NXM_NX_REG{:d}[0..15]),{:s},' 'resubmit(,{:d})'.format( fwaas_ovs_consts.REG_NET, flow['actions'], ovs_consts.ACCEPTED_INGRESS_TRAFFIC_TABLE) ) result.append(flow) return result def create_drop_flows(flow): if flow['table'] in [fwaas_ovs_consts.FW_RULES_INGRESS_TABLE, fwaas_ovs_consts.FW_RULES_EGRESS_TABLE]: flow['actions'] = 'resubmit(,%d)' % ovs_consts.DROPPED_TRAFFIC_TABLE flow['ct_state'] = fwaas_ovs_consts.OF_STATE_NEW_NOT_ESTABLISHED result = [flow.copy()] flow['ct_state'] = fwaas_ovs_consts.OF_STATE_ESTABLISHED_NOT_REPLY result.append(flow) return result ././@LongLink0000000000000000000000000000015000000000000011211 Lustar 00000000000000neutron-fwaas-12.0.2/neutron_fwaas/services/firewall/drivers/linux/l2/openvswitch_firewall/constants.pyneutron-fwaas-12.0.2/neutron_fwaas/services/firewall/drivers/linux/l2/openvswitch_firewall/constants0000664000175000017500000000356213555577713034271 0ustar zuulzuul00000000000000# Copyright 2015 # 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 from neutron.common import constants as n_const OF_STATE_NOT_TRACKED = "-trk" OF_STATE_TRACKED = "+trk" OF_STATE_NEW_NOT_ESTABLISHED = "+new-est" OF_STATE_NOT_ESTABLISHED = "-est" OF_STATE_ESTABLISHED = "+est" OF_STATE_ESTABLISHED_NOT_REPLY = "+est-rel-rpl" OF_STATE_ESTABLISHED_REPLY = "+est-rel+rpl" OF_STATE_RELATED = "-new-est+rel-inv" OF_STATE_INVALID = "+trk+inv" OF_STATE_NEW = "+new" OF_STATE_NOT_REPLY_NOT_NEW = "-new-rpl" CT_MARK_NORMAL = '0x0' CT_MARK_INVALID = '0x1' REG_PORT = 5 REG_NET = 6 FW_BASE_EGRESS_TABLE = 64 FW_RULES_EGRESS_TABLE = 65 FW_ACCEPT_OR_INGRESS_TABLE = 66 FW_BASE_INGRESS_TABLE = 68 FW_RULES_INGRESS_TABLE = 69 OVS_FIREWALL_TABLES = ( FW_BASE_EGRESS_TABLE, FW_RULES_EGRESS_TABLE, FW_ACCEPT_OR_INGRESS_TABLE, FW_BASE_INGRESS_TABLE, FW_RULES_INGRESS_TABLE, ) PROTOCOLS_WITH_PORTS = (constants.PROTO_NAME_SCTP, constants.PROTO_NAME_TCP, constants.PROTO_NAME_UDP) # Only map protocols that need special handling REVERSE_IP_PROTOCOL_MAP_WITH_PORTS = { constants.IP_PROTOCOL_MAP[proto]: proto for proto in PROTOCOLS_WITH_PORTS} ethertype_to_dl_type_map = { constants.IPv4: n_const.ETHERTYPE_IP, constants.IPv6: n_const.ETHERTYPE_IPV6, } ././@LongLink0000000000000000000000000000014700000000000011217 Lustar 00000000000000neutron-fwaas-12.0.2/neutron_fwaas/services/firewall/drivers/linux/l2/openvswitch_firewall/firewall.pyneutron-fwaas-12.0.2/neutron_fwaas/services/firewall/drivers/linux/l2/openvswitch_firewall/firewall.0000664000175000017500000012037113555577713034136 0ustar zuulzuul00000000000000# Copyright 2015 # 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 netaddr from neutron_lib import constants as lib_const from oslo_log import log as logging from oslo_utils import netutils from neutron.agent import firewall from neutron.common import constants from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants \ as ovs_consts from neutron_fwaas.services.firewall.drivers.linux.l2 import driver_base from neutron_fwaas.services.firewall.drivers.linux.l2.openvswitch_firewall \ import constants as fwaas_ovs_consts from neutron_fwaas.services.firewall.drivers.linux.l2.openvswitch_firewall \ import exceptions from neutron_fwaas.services.firewall.drivers.linux.l2.openvswitch_firewall \ import rules LOG = logging.getLogger(__name__) ACTION_ALLOW = 'allow' # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver. def _replace_register(flow_params, register_number, register_value): """Replace value from flows to given register number 'register_value' key in dictionary will be replaced by register number given by 'register_number' :param flow_params: Dictionary containing defined flows :param register_number: The number of register where value will be stored :param register_value: Key to be replaced by register number """ try: reg_port = flow_params[register_value] del flow_params[register_value] flow_params['reg{:d}'.format(register_number)] = reg_port except KeyError: pass # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver that # differs only in constants REG_PORT/REG_NET. def create_reg_numbers(flow_params): """Replace reg_(port|net) values with defined register numbers""" _replace_register(flow_params, fwaas_ovs_consts.REG_PORT, 'reg_port') _replace_register(flow_params, fwaas_ovs_consts.REG_NET, 'reg_net') class FirewallGroup(object): def __init__(self, id_): self.id = id_ self.ingress_rules = [] self.egress_rules = [] self.members = {} self.ports = set() def update_rules(self, ingress_rules, egress_rules): """Update firewall group with ingress/egress rules. If a rule has a protocol field, it is normalized to a number here in order to ease later processing. """ def _translate_protocol_to_number(rule): protocol = rule.get('protocol') if protocol is not None: if protocol.isdigit(): rule['protocol'] = int(protocol) elif (rule.get('ethertype') == lib_const.IPv6 and protocol == lib_const.PROTO_NAME_ICMP): rule['protocol'] = lib_const.PROTO_NUM_IPV6_ICMP else: rule['protocol'] = lib_const.IP_PROTOCOL_MAP.get( protocol, protocol) return rule self.ingress_rules = [_translate_protocol_to_number(ir) for ir in ingress_rules] self.egress_rules = [_translate_protocol_to_number(er) for er in egress_rules] def get_ethertype_filtered_addresses(self, ethertype, exclude_addresses=None): exclude_addresses = set(exclude_addresses if exclude_addresses else []) group_addresses = set(self.members.get(ethertype, [])) return list(group_addresses - exclude_addresses) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver that # differs only in firewall groups list field name class OFPort(object): def __init__(self, port_dict, ovs_port, vlan_tag): self.id = port_dict['device'] self.vlan_tag = vlan_tag self.mac = ovs_port.vif_mac self.lla_address = str(netutils.get_ipv6_addr_by_EUI64( lib_const.IPv6_LLA_PREFIX, self.mac)) self.ofport = ovs_port.ofport self.fw_group = None self.fixed_ips = port_dict.get('fixed_ips', []) self.neutron_port_dict = port_dict.copy() self.allowed_pairs_v4 = self._get_allowed_pairs(port_dict, version=4) self.allowed_pairs_v6 = self._get_allowed_pairs(port_dict, version=6) @staticmethod def _get_allowed_pairs(port_dict, version): aap_dict = port_dict.get('allowed_address_pairs', set()) return {(aap['mac_address'], aap['ip_address']) for aap in aap_dict if netaddr.IPNetwork(aap['ip_address']).version == version} @property def all_allowed_macs(self): macs = {item[0] for item in self.allowed_pairs_v4.union( self.allowed_pairs_v6)} macs.add(self.mac) return macs @property def ipv4_addresses(self): return [ip_addr for ip_addr in [fixed_ip['ip_address'] for fixed_ip in self.fixed_ips] if netaddr.IPAddress(ip_addr).version == 4] @property def ipv6_addresses(self): return [ip_addr for ip_addr in [fixed_ip['ip_address'] for fixed_ip in self.fixed_ips] if netaddr.IPAddress(ip_addr).version == 6] def update(self, port_dict): self.allowed_pairs_v4 = self._get_allowed_pairs(port_dict, version=4) self.allowed_pairs_v6 = self._get_allowed_pairs(port_dict, version=6) # Neighbour discovery uses LLA self.allowed_pairs_v6.add((self.mac, self.lla_address)) self.fixed_ips = port_dict.get('fixed_ips', []) self.neutron_port_dict = port_dict.copy() # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver that # differs in methods name [s/sg/fwg] and update_rules method. class FWGPortMap(object): def __init__(self): self.ports = {} self.fw_groups = {} # Maps port_id to ofport number self.unfiltered = {} def get_fwg(self, fwg_id): return self.fw_groups.get(fwg_id, None) def get_or_create_fwg(self, fwg_id): fw_group = self.get_fwg(fwg_id) if not fw_group: fw_group = FirewallGroup(fwg_id) self.fw_groups[fwg_id] = fw_group return fw_group def delete_fwg(self, fwg_id): del self.fw_groups[fwg_id] # XXX NOTE(ivasilevskaya) couldn't find any logical definition why # firewall_group should come as 3rd argument instead of adding fwg_id # to port_dict. Removed in favor of SG api def create_port(self, port, port_dict): self.ports[port.id] = port self.update_port(port, port_dict) # XXX NOTE(ivasilevskaya) couldn't find any logical definition why # firewall_group should come as 3rd argument instead of adding fwg_id # to port_dict. Removed in favor of SG api def update_port(self, port, port_dict): for fw_group in self.fw_groups.values(): fw_group.ports.discard(port) fw_group = self.get_or_create_fwg(port_dict['firewall_group']) port.fw_group = fw_group fw_group.ports.add(port) port.update(port_dict) def remove_port(self, port): if port.fw_group: port.fw_group.ports.discard(port) del self.ports[port.id] def update_rules(self, fwg_id, ingress_rules, egress_rules): fw_group = self.get_or_create_fwg(fwg_id) fw_group.update_rules(ingress_rules, egress_rules) def update_members(self, fwg_id, members): fw_group = self.get_or_create_fwg(fwg_id) fw_group.members = members # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver that # doesn't have a conjunction manager because no remote_group_id concept is # applicable to firewall groups class OVSFirewallDriver(driver_base.FirewallL2DriverBase): REQUIRED_PROTOCOLS = [ ovs_consts.OPENFLOW10, ovs_consts.OPENFLOW11, ovs_consts.OPENFLOW12, ovs_consts.OPENFLOW13, ovs_consts.OPENFLOW14, ] provides_arp_spoofing_protection = True # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver. # This driver won't have any conj_manager logic because there is no concept # of remote_group_id for firewall groups (that I know of at least) def __init__(self, integration_bridge, sg_with_ovs=False): """Initialize object :param integration_bridge: Bridge on which openflow rules will be applied """ self.int_br = self.initialize_bridge(integration_bridge) self.fwg_port_map = FWGPortMap() self.fwg_to_delete = set() self._deferred = False self.sg_with_ovs = sg_with_ovs self._drop_all_unmatched_flows() self._initialize_third_party_tables() # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver def _accept_flow(self, **flow): for f in rules.create_accept_flows(flow, self.sg_with_ovs): self._add_flow(**f) def _drop_flow(self, **flow): for f in rules.create_drop_flows(flow): self._add_flow(**f) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver def _add_flow(self, **kwargs): dl_type = kwargs.get('dl_type') create_reg_numbers(kwargs) if isinstance(dl_type, int): kwargs['dl_type'] = "0x{:04x}".format(dl_type) if self._deferred: self.int_br.add_flow(**kwargs) else: self.int_br.br.add_flow(**kwargs) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver def _delete_flows(self, **kwargs): create_reg_numbers(kwargs) if self._deferred: self.int_br.delete_flows(**kwargs) else: self.int_br.br.delete_flows(**kwargs) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver def _strict_delete_flow(self, **kwargs): """Delete given flow right away even if bridge is deferred. Delete command will use strict delete. """ create_reg_numbers(kwargs) self.int_br.br.delete_flows(strict=True, **kwargs) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver @staticmethod def initialize_bridge(int_br): int_br.add_protocols(*OVSFirewallDriver.REQUIRED_PROTOCOLS) return int_br.deferred(full_ordered=True) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver, # differs in constants def _drop_all_unmatched_flows(self): for table in fwaas_ovs_consts.OVS_FIREWALL_TABLES: if (table == fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE and self.sg_with_ovs): continue self.int_br.br.add_flow(table=table, priority=0, actions='drop') def _initialize_third_party_tables(self): self.int_br.br.add_flow( table=ovs_consts.ACCEPTED_EGRESS_TRAFFIC_TABLE, priority=1, actions='normal') for table in (ovs_consts.ACCEPTED_INGRESS_TRAFFIC_TABLE, ovs_consts.DROPPED_TRAFFIC_TABLE): self.int_br.br.add_flow( table=table, priority=0, actions='drop') # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver def get_ovs_port(self, port_id): ovs_port = self.int_br.br.get_vif_port_by_id(port_id) if not ovs_port: raise exceptions.OVSFWaaSPortNotFound(port_id=port_id) return ovs_port def _get_port_vlan_tag(self, port): vlan_tag = port.get('lvlan', None) if not vlan_tag: raise exceptions.OVSFWaaSTagNotFound(port_id=port['device']) return vlan_tag # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver def get_ofport(self, port): port_id = port['device'] return self.fwg_port_map.ports.get(port_id) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver, # self.sg_port_map -> self.fwg_port_map def get_or_create_ofport(self, port): """Get ofport specified by port['device'], checking and reflecting ofport changes. If ofport is nonexistent, create and return one. """ port_id = port['device'] ovs_port = self.get_ovs_port(port_id) try: of_port = self.fwg_port_map.ports[port_id] except KeyError: port_vlan_id = self._get_port_vlan_tag(port) of_port = OFPort(port, ovs_port, port_vlan_id) self.fwg_port_map.create_port(of_port, port) else: if of_port.ofport != ovs_port.ofport: self.fwg_port_map.remove_port(of_port) of_port = OFPort(port, ovs_port, of_port.vlan_tag) self.fwg_port_map.update_port(of_port, port) return of_port # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver def is_port_managed(self, port): return port['device'] in self.fwg_port_map.ports # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver def prepare_port_filter(self, port): # NOTE(annp): port no security should be handled by security group in # co-existence mode, otherwise(standalone mode) fwg will handle it. if not firewall.port_sec_enabled(port) and not self.sg_with_ovs: self._initialize_egress_no_port_security(port) return old_of_port = self.get_ofport(port) # Make sure delete old allow_address_pair MACs because # allow_address_pair MACs will be updated in # self.get_or_create_ofport(port) if old_of_port: LOG.error("Initializing port %s that was already " "initialized.", port['device']) self.delete_all_port_flows(old_of_port) of_port = self.get_or_create_ofport(port) self.initialize_port_flows(of_port) self.add_flows_from_rules(of_port) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver def update_port_filter(self, port): """Update rules for given port Current existing filtering rules are removed and new ones are generated based on current loaded firewall group rules and members. Note: port no security should be handled by security group in co-existence mode, otherwise fwg will handle it. """ if not firewall.port_sec_enabled(port) and not self.sg_with_ovs: self.remove_port_filter(port) self._initialize_egress_no_port_security(port) return elif not self.is_port_managed(port): if not self.sg_with_ovs: self._remove_egress_no_port_security(port['device']) self.prepare_port_filter(port) return old_of_port = self.get_ofport(port) of_port = self.get_or_create_ofport(port) # TODO(jlibosva): Handle firewall blink self.delete_all_port_flows(old_of_port) self.initialize_port_flows(of_port) self.add_flows_from_rules(of_port) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver, # sg_port_map -> fwg_port_map def remove_port_filter(self, port): """Remove port from firewall All flows related to this port are removed from ovs. Port is also removed from ports managed by this firewall. """ if self.is_port_managed(port): of_port = self.get_ofport(port) self.delete_all_port_flows(of_port) self.fwg_port_map.remove_port(of_port) self._schedule_fwg_deletion_maybe(of_port.fw_group.id) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver # with ingress\egress rules arguments instead of single rules def update_firewall_group_rules(self, fwg_id, ingress_rules, egress_rules): self.fwg_port_map.update_rules(fwg_id, ingress_rules, egress_rules) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver # with sg_port_map -> fwg_port_map def _schedule_fwg_deletion_maybe(self, fwg_id): """Schedule possible deletion of the given firewall group. This function must be called when the number of ports associated to fwg_id drops to zero, as it isn't possible to know FWG deletions from agents due to RPC API design. """ fwg_group = self.fwg_port_map.get_or_create_fwg(fwg_id) if not fwg_group.members or not fwg_group.ports: self.fwg_to_delete.add(fwg_id) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver # with sg_port_map -> fwg_port_map def _cleanup_stale_fwg(self): fwg_to_delete = self.fwg_to_delete self.fwg_to_delete = set() for fwg_id in fwg_to_delete: fw_group = self.fwg_port_map.get_fwg(fwg_id) if fw_group.members and fw_group.ports: # firewall group is still in use continue self.fwg_port_map.delete_fwg(fwg_id) def process_trusted_ports(self, ports): """Pass packets from these ports directly to ingress pipeline.""" if self.sg_with_ovs: return for port in ports: self._initialize_egress_no_port_security(port) def remove_trusted_ports(self, port_ids): if self.sg_with_ovs: return for port_id in port_ids: self._remove_egress_no_port_security(port_id) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver def filter_defer_apply_on(self): self._deferred = True # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver def filter_defer_apply_off(self): if self._deferred: self._cleanup_stale_fwg() self.int_br.apply_flows() self._deferred = False # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver # with sg_port_map -> fwg_port_map @property def ports(self): return {id_: port.neutron_port_dict for id_, port in self.fwg_port_map.ports.items()} # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver # which differs in constants (table numbers) def initialize_port_flows(self, port): """Set base flows for port :param port: OFPort instance """ # Identify egress flow self._add_flow( table=ovs_consts.TRANSIENT_TABLE, priority=105, in_port=port.ofport, actions='set_field:{:d}->reg{:d},' 'set_field:{:d}->reg{:d},' 'resubmit(,{:d})'.format( port.ofport, fwaas_ovs_consts.REG_PORT, port.vlan_tag, fwaas_ovs_consts.REG_NET, fwaas_ovs_consts.FW_BASE_EGRESS_TABLE) ) # Identify ingress flows after egress filtering for mac_addr in port.all_allowed_macs: self._add_flow( table=ovs_consts.TRANSIENT_TABLE, priority=95, dl_dst=mac_addr, dl_vlan='0x%x' % port.vlan_tag, actions='set_field:{:d}->reg{:d},' 'set_field:{:d}->reg{:d},' 'strip_vlan,resubmit(,{:d})'.format( port.ofport, fwaas_ovs_consts.REG_PORT, port.vlan_tag, fwaas_ovs_consts.REG_NET, fwaas_ovs_consts.FW_BASE_INGRESS_TABLE), ) self._initialize_egress(port) self._initialize_ingress(port) def _fwaas_process_colocated_ingress(self, port): for mac_addr in port.all_allowed_macs: self._add_flow( table=ovs_consts.ACCEPT_OR_INGRESS_TABLE, priority=105, dl_dst=mac_addr, reg_net=port.vlan_tag, actions='set_field:{:d}->reg{:d},resubmit(,{:d})'.format( port.ofport, fwaas_ovs_consts.REG_PORT, fwaas_ovs_consts.FW_BASE_INGRESS_TABLE), ) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver # which differs in constants (table numbers) def _initialize_egress_ipv6_icmp(self, port): for icmp_type in firewall.ICMPV6_ALLOWED_EGRESS_TYPES: self._add_flow( table=fwaas_ovs_consts.FW_BASE_EGRESS_TABLE, priority=95, in_port=port.ofport, reg_port=port.ofport, dl_type=constants.ETHERTYPE_IPV6, nw_proto=lib_const.PROTO_NUM_IPV6_ICMP, icmp_type=icmp_type, actions='normal') # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver # which differs in constants (table numbers) and exception classes def _initialize_egress_no_port_security(self, port): port_id = port['device'] try: ovs_port = self.get_ovs_port(port_id) vlan_tag = self._get_port_vlan_tag(port) except exceptions.OVSFWaaSTagNotFound: # It's a patch port, don't set anything return except exceptions.OVSFWaaSPortNotFound as not_found_e: LOG.error("Initializing unfiltered port %(port_id)s that does not " "exist in ovsdb: %(err)s.", {'port_id': port_id, 'err': not_found_e}) return self.fwg_port_map.unfiltered[port_id] = ovs_port.ofport self._add_flow( table=ovs_consts.TRANSIENT_TABLE, priority=100, in_port=ovs_port.ofport, actions='set_field:%d->reg%d,' 'set_field:%d->reg%d,' 'resubmit(,%d)' % ( ovs_port.ofport, fwaas_ovs_consts.REG_PORT, vlan_tag, fwaas_ovs_consts.REG_NET, fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE) ) self._add_flow( table=fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE, priority=80, reg_port=ovs_port.ofport, actions='normal', ) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver # which differs in constants (table numbers) def _remove_egress_no_port_security(self, port_id): try: ofport = self.fwg_port_map.unfiltered[port_id] except KeyError: LOG.debug("Port %s is not handled by the firewall.", port_id) return self._delete_flows( table=ovs_consts.TRANSIENT_TABLE, in_port=ofport ) self._delete_flows( table=fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE, reg_port=ofport ) del self.fwg_port_map.unfiltered[port_id] # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver # which differs in constants (table numbers) def _initialize_egress(self, port): """Identify egress traffic and send it to egress base""" self._initialize_egress_ipv6_icmp(port) # Apply mac/ip pairs for IPv4 allowed_pairs = port.allowed_pairs_v4.union( {(port.mac, ip_addr) for ip_addr in port.ipv4_addresses}) for mac_addr, ip_addr in allowed_pairs: self._add_flow( table=fwaas_ovs_consts.FW_BASE_EGRESS_TABLE, priority=95, in_port=port.ofport, reg_port=port.ofport, dl_src=mac_addr, dl_type=constants.ETHERTYPE_ARP, arp_spa=ip_addr, actions='normal' ) self._add_flow( table=fwaas_ovs_consts.FW_BASE_EGRESS_TABLE, priority=65, reg_port=port.ofport, ct_state=fwaas_ovs_consts.OF_STATE_NOT_TRACKED, dl_type=constants.ETHERTYPE_IP, in_port=port.ofport, dl_src=mac_addr, nw_src=ip_addr, actions='ct(table={:d},zone=NXM_NX_REG{:d}[0..15])'.format( fwaas_ovs_consts.FW_RULES_EGRESS_TABLE, fwaas_ovs_consts.REG_NET) ) # Apply mac/ip pairs for IPv6 allowed_pairs = port.allowed_pairs_v6.union( {(port.mac, ip_addr) for ip_addr in port.ipv6_addresses}) for mac_addr, ip_addr in allowed_pairs: self._add_flow( table=fwaas_ovs_consts.FW_BASE_EGRESS_TABLE, priority=65, reg_port=port.ofport, in_port=port.ofport, ct_state=fwaas_ovs_consts.OF_STATE_NOT_TRACKED, dl_type=constants.ETHERTYPE_IPV6, dl_src=mac_addr, ipv6_src=ip_addr, actions='ct(table={:d},zone=NXM_NX_REG{:d}[0..15])'.format( fwaas_ovs_consts.FW_RULES_EGRESS_TABLE, fwaas_ovs_consts.REG_NET) ) # DHCP discovery accept_or_ingress = fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE if self.sg_with_ovs: accept_or_ingress = ovs_consts.ACCEPT_OR_INGRESS_TABLE for dl_type, src_port, dst_port in ( (constants.ETHERTYPE_IP, 68, 67), (constants.ETHERTYPE_IPV6, 546, 547)): self._add_flow( table=fwaas_ovs_consts.FW_BASE_EGRESS_TABLE, priority=80, reg_port=port.ofport, in_port=port.ofport, dl_type=dl_type, nw_proto=lib_const.PROTO_NUM_UDP, tp_src=src_port, tp_dst=dst_port, actions='resubmit(,{:d})'.format(accept_or_ingress) ) # Ban dhcp service running on an instance for dl_type, src_port, dst_port in ( (constants.ETHERTYPE_IP, 67, 68), (constants.ETHERTYPE_IPV6, 547, 546)): self._add_flow( table=fwaas_ovs_consts.FW_BASE_EGRESS_TABLE, priority=70, in_port=port.ofport, reg_port=port.ofport, dl_type=dl_type, nw_proto=lib_const.PROTO_NUM_UDP, tp_src=src_port, tp_dst=dst_port, actions='resubmit(,%d)' % ovs_consts.DROPPED_TRAFFIC_TABLE ) # Drop Router Advertisements from instances self._add_flow( table=fwaas_ovs_consts.FW_BASE_EGRESS_TABLE, priority=70, in_port=port.ofport, reg_port=port.ofport, dl_type=constants.ETHERTYPE_IPV6, nw_proto=lib_const.PROTO_NUM_IPV6_ICMP, icmp_type=lib_const.ICMPV6_TYPE_RA, actions='resubmit(,%d)' % ovs_consts.DROPPED_TRAFFIC_TABLE ) # Drop all remaining not tracked egress connections self._add_flow( table=fwaas_ovs_consts.FW_BASE_EGRESS_TABLE, priority=10, ct_state=fwaas_ovs_consts.OF_STATE_NOT_TRACKED, in_port=port.ofport, reg_port=port.ofport, actions='resubmit(,%d)' % ovs_consts.DROPPED_TRAFFIC_TABLE ) # Fill in accept_or_ingress table by checking that traffic is ingress # and if not, accept it if self.sg_with_ovs: self._fwaas_process_colocated_ingress(port) else: for mac_addr in port.all_allowed_macs: self._add_flow( table=fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE, priority=100, dl_dst=mac_addr, reg_net=port.vlan_tag, actions='set_field:{:d}->reg{:d},resubmit(,{:d})'.format( port.ofport, fwaas_ovs_consts.REG_PORT, fwaas_ovs_consts.FW_BASE_INGRESS_TABLE), ) for ethertype in [constants.ETHERTYPE_IP, constants.ETHERTYPE_IPV6]: self._add_flow( table=fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE, priority=90, dl_type=ethertype, reg_port=port.ofport, ct_state=fwaas_ovs_consts.OF_STATE_NEW_NOT_ESTABLISHED, actions='ct(commit,zone=NXM_NX_REG{:d}[0..15]),' 'resubmit(,{:d})'.format( fwaas_ovs_consts.REG_NET, ovs_consts.ACCEPTED_EGRESS_TRAFFIC_TABLE) ) self._add_flow( table=fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE, priority=80, reg_port=port.ofport, actions='normal' ) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver # which differs in constants (table numbers) def _initialize_tracked_egress(self, port): # Drop invalid packets self._add_flow( table=fwaas_ovs_consts.FW_RULES_EGRESS_TABLE, priority=50, ct_state=fwaas_ovs_consts.OF_STATE_INVALID, actions='resubmit(,%d)' % ovs_consts.DROPPED_TRAFFIC_TABLE ) # Drop traffic for removed fwg rules self._add_flow( table=fwaas_ovs_consts.FW_RULES_EGRESS_TABLE, priority=50, reg_port=port.ofport, ct_mark=fwaas_ovs_consts.CT_MARK_INVALID, actions='resubmit(,%d)' % ovs_consts.DROPPED_TRAFFIC_TABLE ) for state in ( fwaas_ovs_consts.OF_STATE_ESTABLISHED_REPLY, fwaas_ovs_consts.OF_STATE_RELATED, ): self._add_flow( table=fwaas_ovs_consts.FW_RULES_EGRESS_TABLE, priority=50, ct_state=state, ct_mark=fwaas_ovs_consts.CT_MARK_NORMAL, reg_port=port.ofport, ct_zone=port.vlan_tag, actions='normal' ) self._add_flow( table=fwaas_ovs_consts.FW_RULES_EGRESS_TABLE, priority=40, reg_port=port.ofport, ct_state=fwaas_ovs_consts.OF_STATE_NOT_ESTABLISHED, actions='resubmit(,%d)' % ovs_consts.DROPPED_TRAFFIC_TABLE ) for ethertype in [constants.ETHERTYPE_IP, constants.ETHERTYPE_IPV6]: self._add_flow( table=fwaas_ovs_consts.FW_RULES_EGRESS_TABLE, priority=40, dl_type=ethertype, reg_port=port.ofport, ct_state=fwaas_ovs_consts.OF_STATE_ESTABLISHED, actions="ct(commit,zone=NXM_NX_REG{:d}[0..15]," "exec(set_field:{:s}->ct_mark))".format( fwaas_ovs_consts.REG_NET, fwaas_ovs_consts.CT_MARK_INVALID) ) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver # which differs in constants (table numbers) def _initialize_ingress_ipv6_icmp(self, port): for icmp_type in firewall.ICMPV6_ALLOWED_INGRESS_TYPES: self._add_flow( table=fwaas_ovs_consts.FW_BASE_INGRESS_TABLE, priority=100, reg_port=port.ofport, dl_dst=port.mac, dl_type=constants.ETHERTYPE_IPV6, nw_proto=lib_const.PROTO_NUM_IPV6_ICMP, icmp_type=icmp_type, actions='output:{:d}'.format(port.ofport) ) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver # which differs in constants (table numbers) def _initialize_ingress(self, port): # Allow incoming ARPs self._add_flow( table=fwaas_ovs_consts.FW_BASE_INGRESS_TABLE, priority=100, dl_type=constants.ETHERTYPE_ARP, reg_port=port.ofport, actions='output:{:d}'.format(port.ofport) ) self._initialize_ingress_ipv6_icmp(port) # DHCP offers for dl_type, src_port, dst_port in ( (constants.ETHERTYPE_IP, 67, 68), (constants.ETHERTYPE_IPV6, 547, 546)): self._add_flow( table=fwaas_ovs_consts.FW_BASE_INGRESS_TABLE, priority=95, reg_port=port.ofport, dl_type=dl_type, nw_proto=lib_const.PROTO_NUM_UDP, tp_src=src_port, tp_dst=dst_port, actions='output:{:d}'.format(port.ofport) ) # Track untracked for dl_type in (constants.ETHERTYPE_IP, constants.ETHERTYPE_IPV6): self._add_flow( table=fwaas_ovs_consts.FW_BASE_INGRESS_TABLE, priority=90, reg_port=port.ofport, dl_type=dl_type, ct_state=fwaas_ovs_consts.OF_STATE_NOT_TRACKED, actions='ct(table={:d},zone=NXM_NX_REG{:d}[0..15])'.format( fwaas_ovs_consts.FW_RULES_INGRESS_TABLE, fwaas_ovs_consts.REG_NET) ) self._add_flow( table=fwaas_ovs_consts.FW_BASE_INGRESS_TABLE, ct_state=fwaas_ovs_consts.OF_STATE_TRACKED, priority=80, reg_port=port.ofport, actions='resubmit(,{:d})'.format( fwaas_ovs_consts.FW_RULES_INGRESS_TABLE) ) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver # which differs in constants (table numbers) def _initialize_tracked_ingress(self, port): # Drop invalid packets self._add_flow( table=fwaas_ovs_consts.FW_RULES_INGRESS_TABLE, priority=50, ct_state=fwaas_ovs_consts.OF_STATE_INVALID, actions='resubmit(,%d)' % ovs_consts.DROPPED_TRAFFIC_TABLE ) # Drop traffic for removed fwg rules self._add_flow( table=fwaas_ovs_consts.FW_RULES_INGRESS_TABLE, priority=50, reg_port=port.ofport, ct_mark=fwaas_ovs_consts.CT_MARK_INVALID, actions='resubmit(,%d)' % ovs_consts.DROPPED_TRAFFIC_TABLE ) # Allow established and related connections for state in (fwaas_ovs_consts.OF_STATE_ESTABLISHED_REPLY, fwaas_ovs_consts.OF_STATE_RELATED): self._add_flow( table=fwaas_ovs_consts.FW_RULES_INGRESS_TABLE, priority=50, reg_port=port.ofport, ct_state=state, ct_mark=fwaas_ovs_consts.CT_MARK_NORMAL, ct_zone=port.vlan_tag, actions='output:{:d}'.format(port.ofport) ) self._add_flow( table=fwaas_ovs_consts.FW_RULES_INGRESS_TABLE, priority=40, reg_port=port.ofport, ct_state=fwaas_ovs_consts.OF_STATE_NOT_ESTABLISHED, actions='resubmit(,%d)' % ovs_consts.DROPPED_TRAFFIC_TABLE ) for ethertype in [constants.ETHERTYPE_IP, constants.ETHERTYPE_IPV6]: self._add_flow( table=fwaas_ovs_consts.FW_RULES_INGRESS_TABLE, priority=40, dl_type=ethertype, reg_port=port.ofport, ct_state=fwaas_ovs_consts.OF_STATE_ESTABLISHED, actions="ct(commit,zone=NXM_NX_REG{:d}[0..15]," "exec(set_field:{:s}->ct_mark))".format( fwaas_ovs_consts.REG_NET, fwaas_ovs_consts.CT_MARK_INVALID) ) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver # which differs in constants (table numbers) and rules_generator method def add_flows_from_rules(self, port): self._initialize_tracked_ingress(port) self._initialize_tracked_egress(port) LOG.debug('Creating flow rules for port %s that is port %d in OVS', port.id, port.ofport) for rule in self.create_rules_generator_for_port(port): flows = rules.create_flows_from_rule_and_port(rule, port) LOG.debug("RULGEN: Rules generated for flow %s are %s", rule, flows) for flow in flows: if rule.get('action') == ACTION_ALLOW: self._accept_flow(**flow) else: self._drop_flow(**flow) def create_rules_generator_for_port(self, port): """Returns a generator emitting rules valid for further processing Injects necessary fields to feed one-by-one to rules module to transform into valid openflow rules. """ def inject_fields(rule, direction, offset=0): """Add fields to rule dict to be able to utilize rules module Currently such fields are added: 'offset', 'direction', 'ethertype', 'source_port_range_min', 'source_port_range_max', 'port_range_min', 'port_range_max' """ # XXX NOTE(ivasilevskaya) maybe there's a clever way to do that version_ethertype_map = {lib_const.IP_VERSION_4: lib_const.IPv4, lib_const.IP_VERSION_6: lib_const.IPv6} rule['direction'] = direction rule['ethertype'] = version_ethertype_map[rule['ip_version']] rule['offset'] = offset # transfer destination_port into port_range_min/port_range_max def add_range(range_key, key_min, key_max): range_str = rule.get(range_key) if not range_str: return ports = range_str.split(':', 1) rule[key_min] = int(ports[0]) rule['port_range_max'] = ( int(ports[1]) if len(ports) == 2 else int(ports[0])) add_range('destination_port', 'port_range_min', 'port_range_max') add_range('source_port', 'source_port_range_min', 'source_port_range_max') # add direction field offset = len(port.fw_group.ingress_rules) - 1 for rule in port.fw_group.ingress_rules: inject_fields(rule, lib_const.INGRESS_DIRECTION, offset) offset -= 1 yield rule offset = len(port.fw_group.egress_rules) - 1 for rule in port.fw_group.egress_rules: inject_fields(rule, lib_const.EGRESS_DIRECTION, offset) offset -= 1 yield rule # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver # which differs in constants (table numbers) def delete_all_port_flows(self, port): """Delete all flows for given port""" accept_or_ingress = fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE if self.sg_with_ovs: accept_or_ingress = ovs_consts.ACCEPT_OR_INGRESS_TABLE for mac_addr in port.all_allowed_macs: self._strict_delete_flow(priority=95, table=ovs_consts.TRANSIENT_TABLE, dl_dst=mac_addr, dl_vlan=port.vlan_tag) self._delete_flows( table=accept_or_ingress, dl_dst=mac_addr, reg_net=port.vlan_tag) self._strict_delete_flow(priority=105, table=ovs_consts.TRANSIENT_TABLE, in_port=port.ofport) self._delete_flows(reg_port=port.ofport) def create_firewall_group(self, ports_for_fwg, firewall_group): egress_rules = firewall_group['egress_rule_list'] ingress_rules = firewall_group['ingress_rule_list'] fwg_id = firewall_group['id'] self.update_firewall_group_rules(fwg_id, ingress_rules, egress_rules) for port in ports_for_fwg: port['firewall_group'] = fwg_id self.update_port_filter(port) def update_firewall_group(self, ports_for_fwg, firewall_group): self.create_firewall_group(ports_for_fwg, firewall_group) def delete_firewall_group(self, ports_for_fwg, firewall_group): for port in ports_for_fwg: port['firewall_group'] = firewall_group['id'] self.remove_port_filter(port) ././@LongLink0000000000000000000000000000014700000000000011217 Lustar 00000000000000neutron-fwaas-12.0.2/neutron_fwaas/services/firewall/drivers/linux/l2/openvswitch_firewall/__init__.pyneutron-fwaas-12.0.2/neutron_fwaas/services/firewall/drivers/linux/l2/openvswitch_firewall/__init__.0000664000175000017500000000137113555577713034066 0ustar zuulzuul00000000000000# Copyright 2015 # 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_fwaas.services.firewall.drivers.linux.l2.openvswitch_firewall \ import firewall OVSFirewallDriver = firewall.OVSFirewallDriver neutron-fwaas-12.0.2/neutron_fwaas/services/firewall/drivers/linux/l2/noop/0000775000175000017500000000000013555600005027016 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/services/firewall/drivers/linux/l2/noop/noop_driver.py0000664000175000017500000000237013555577713031743 0ustar zuulzuul00000000000000# Copyright (C) 2017 Fujitsu Limited # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_log import helpers as log_helpers from neutron_fwaas.services.firewall.drivers.linux.l2 import driver_base class NoopFirewallL2Driver(driver_base.FirewallL2DriverBase): @log_helpers.log_method_call def create_firewall_group(self, ports, firewall_group): pass @log_helpers.log_method_call def update_firewall_group(self, ports, firewall_group): pass @log_helpers.log_method_call def delete_firewall_group(self, ports, firewall_group): pass @log_helpers.log_method_call def process_trusted_ports(self, ports): pass @log_helpers.log_method_call def remove_trusted_ports(self, port_ids): pass neutron-fwaas-12.0.2/neutron_fwaas/services/firewall/drivers/linux/l2/noop/__init__.py0000664000175000017500000000000013555577713031140 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/services/firewall/drivers/linux/l2/driver_base.py0000664000175000017500000000344213555577713030730 0ustar zuulzuul00000000000000# Copyright (C) 2017 Fujitsu Limited # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc import contextlib import six @six.add_metaclass(abc.ABCMeta) class FirewallL2DriverBase(object): """Abstract firewall L2 driver base""" def __init__(self, integration_bridge, sg_enabled=False): pass def filter_defer_apply_on(self): """Defer application of filtering rule.""" pass def filter_defer_apply_off(self): """Turn off deferral of rules and apply the rules now.""" pass @property def ports(self): """Returns filtered ports.""" pass @contextlib.contextmanager def defer_apply(self): """Defer apply context.""" self.filter_defer_apply_on() try: yield finally: self.filter_defer_apply_off() def create_firewall_group(self, ports, firewall_group): """Called when a firewall group is created. """ raise NotImplementedError() def update_firewall_group(self, ports, firewall_group): """Called when a firewall group is updated. """ raise NotImplementedError() def delete_firewall_group(self, ports, firewall_group): """Called when a firewall group is deleted. """ raise NotImplementedError() neutron-fwaas-12.0.2/neutron_fwaas/services/firewall/drivers/linux/l2/__init__.py0000664000175000017500000000000013555577713030165 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/services/firewall/drivers/fwaas_base_v2.py0000664000175000017500000000702013555577713027465 0ustar zuulzuul00000000000000# 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 neutron-fwaas-12.0.2/neutron_fwaas/services/firewall/drivers/fwaas_base.py0000664000175000017500000001035413555577713027062 0ustar zuulzuul00000000000000# 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 neutron-fwaas-12.0.2/neutron_fwaas/services/firewall/drivers/__init__.py0000664000175000017500000000000013555577713026511 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/services/firewall/drivers/conntrack_base.py0000664000175000017500000000342313555577713027742 0ustar zuulzuul00000000000000# 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 from neutron_lib.utils import runtime from oslo_config import cfg from oslo_log import log as logging from oslo_utils import excutils LOG = logging.getLogger(__name__) def load_and_init_conntrack_driver(*args, **kwargs): driver = cfg.CONF.fwaas.conntrack_driver try: conntrack_driver_cls = runtime.load_class_by_alias_or_classname( 'neutron_fwaas.services.firewall.drivers.linux', driver) except ImportError: with excutils.save_and_reraise_exception(): LOG.exception("Driver '%s' not found.", driver) conntrack_driver = conntrack_driver_cls() conntrack_driver.initialize(*args, **kwargs) return conntrack_driver @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""" neutron-fwaas-12.0.2/neutron_fwaas/services/firewall/__init__.py0000664000175000017500000000000013555577713025033 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/services/firewall/fwaas_plugin_v2.py0000664000175000017500000005401413555577713026400 0ustar zuulzuul00000000000000# 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 neutron.db import servicetype_db as st_db from neutron.services import provider_configuration as provider_conf from neutron_lib.api.definitions import firewall_v2 from neutron_lib.api.definitions import portbindings as pb_def 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 import context as neutron_context from neutron_lib.exceptions import firewall_v2 as f_exc from neutron_lib.plugins import constants as plugin_const from neutron_lib.plugins import directory from oslo_config import cfg from oslo_log import log as logging import oslo_messaging from neutron_fwaas.common import exceptions from neutron_fwaas.common import fwaas_constants from neutron_fwaas.db.firewall.v2 import firewall_db_v2 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('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 def get_firewall_group_for_port(self, context, **kwargs): """Get firewall_group is associated with a port.""" LOG.debug("get_firewall_group_for_port() called") ctx = context.elevated() return self.plugin.get_firewall_group_for_port( ctx, kwargs.get('port_id')) @registry.has_registry_receivers 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 = firewall_v2.API_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'] = [] fwg_with_rules['port_details'] = self._get_fwg_port_details( context, fwg_with_rules['add-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['tenant_id'] != tenant_id: raise f_exc.FirewallGroupPortInvalidProject( port_id=port_id, project_id=port_db['tenant_id']) device_owner = port_db.get('device_owner', '') if (device_owner not in [nl_constants.DEVICE_OWNER_ROUTER_INTF] and not device_owner.startswith( nl_constants.DEVICE_OWNER_COMPUTE_PREFIX)): raise f_exc.FirewallGroupPortInvalid(port_id=port_id) if (device_owner.startswith( nl_constants.DEVICE_OWNER_COMPUTE_PREFIX) and not self._is_supported_by_fw_l2_driver(context, port_id)): raise exceptions.FirewallGroupPortNotSupported(port_id=port_id) def _is_supported_by_fw_l2_driver(self, context, port_id): """Whether this port is supported by firewall l2 driver""" # Re-fetch to get up-to-date data from db port = self._core_plugin.get_port(context, id=port_id) # Skip port binding is unbound or failed if port[pb_def.VIF_TYPE] in [pb_def.VIF_TYPE_UNBOUND, pb_def.VIF_TYPE_BINDING_FAILED]: return False if not port['port_security_enabled']: return True if port[pb_def.VIF_TYPE] == pb_def.VIF_TYPE_OVS: # TODO(annp): remove these lines after we fully support for hybrid # port if not port[pb_def.VIF_DETAILS][pb_def.OVS_HYBRID_PLUG]: return True LOG.warning("Doesn't support hybrid port at the moment") else: LOG.warning("Doesn't support vif type %s", port[pb_def.VIF_TYPE]) return False 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 _get_fwg_port_details(self, context, fwg_ports): """Returns a dictionary list of port details. """ port_details = {} for port_id in fwg_ports: port_db = self._core_plugin.get_port(context, port_id) # Add more parameters below based on requirement. device_owner = port_db['device_owner'] port_details[port_id] = { 'device_owner': device_owner, 'device': port_db['id'], 'network_id': port_db['network_id'], 'fixed_ips': port_db['fixed_ips'], 'allowed_address_pairs': port_db.get('allowed_address_pairs', []), 'port_security_enabled': port_db.get('port_security_enabled', True), 'id': port_db['id'] } if device_owner.startswith( nl_constants.DEVICE_OWNER_COMPUTE_PREFIX): port_details[port_id].update( {'host': port_db[pb_def.HOST_ID]}) return port_details def get_project_id_from_port_id(self, context, port_id): """Returns an ID of project for specified port_id. """ return self._core_plugin.get_port(context, port_id)['project_id'] @registry.receives(resources.PORT, [events.AFTER_UPDATE]) def handle_update_port(self, resource, event, trigger, **kwargs): updated_port = kwargs['port'] if not updated_port['device_owner'].startswith( nl_constants.DEVICE_OWNER_COMPUTE_PREFIX): return if (kwargs.get('original_port')[pb_def.VIF_TYPE] != pb_def.VIF_TYPE_UNBOUND): # Checking newly vm port binding allows us to avoid call to DB # when a port update_event like restart, setting name, etc... # Moreover, that will help us in case of tenant admin wants to # only attach security group to vm port. return context = kwargs['context'] port_id = updated_port['id'] # Check port is supported by firewall l2 driver or not if not self._is_supported_by_fw_l2_driver(context, port_id): return project_id = updated_port['project_id'] LOG.debug("Try to associate port %s at %s", port_id, project_id) self.set_port_for_default_firewall_group(context, port_id, project_id) 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) 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-port-ids'] = [] fwg_with_rules['port_details'] = self._get_fwg_port_details( context, fwg_ports) 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_new_ports = firewall_group['firewall_group']['ports'] if len(fwg_new_ports) > 0: self._validate_ports_for_firewall_group( context, context.project_id, fwg_new_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']) fwg_with_rules['port_details'] = self._get_fwg_port_details( context, fwg_with_rules['del-port-ids']) fwg_with_rules['port_details'].update(self._get_fwg_port_details( context, fwg_with_rules['add-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) fwg_db = self._get_firewall_group(context, id) if fwg_db['status'] == nl_constants.ACTIVE: raise f_exc.FirewallGroupInUse(firewall_id=id) fwg_with_rules = ( self._make_firewall_group_dict_with_rules(context, id)) fwg_with_rules['del-port-ids'] = self._get_ports_in_firewall_group( context, id) fwg_with_rules['add-port-ids'] = [] if not fwg_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 fwg_with_rules fwg_with_rules['status'] = status['firewall_group']['status'] fwg_with_rules['port_details'] = self._get_fwg_port_details( context, fwg_with_rules['del-port-ids']) self.agent_rpc.delete_firewall_group(context, fwg_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 def insert_rule(self, context, id, rule_info): LOG.debug("insert_rule() called") self._ensure_update_firewall_policy(context, id) fwp = super(FirewallPluginV2, self).insert_rule(context, id, rule_info) self._rpc_update_firewall_policy(context, id) return fwp def remove_rule(self, context, id, rule_info): LOG.debug("remove_rule() called") self._ensure_update_firewall_policy(context, id) fwp = super(FirewallPluginV2, self).remove_rule(context, id, rule_info) self._rpc_update_firewall_policy(context, id) return fwp neutron-fwaas-12.0.2/neutron_fwaas/services/firewall/fwaas_plugin.py0000664000175000017500000004773313555577713026003 0ustar zuulzuul00000000000000# 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.common import rpc as n_rpc from neutron.common import utils as n_utils from neutron_lib.api.definitions import firewall as fw_ext 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 oslo_config import cfg from oslo_log import log as logging import oslo_messaging 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 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('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('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.API_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 _check_dvr_extensions(self, l3plugin): return ( n_utils.is_extension_supported( l3plugin, nl_constants.L3_AGENT_SCHEDULER_EXT_ALIAS) and n_utils.is_extension_supported( l3plugin, nl_constants.L3_DISTRIBUTED_EXT_ALIAS) and getattr(l3plugin, '_get_dvr_hosts_for_router', False)) 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)) scheduled_hosts = set() if no_broadcast: # This call checks for all scheduled routers to the network node agents = l3_plugin.get_l3_agents_hosting_routers( context, router_ids, admin_state_up=True, active=True) scheduled_hosts = set([a.host for a in agents]) # Now check for unscheduled DVR router on distributed compute hosts unscheduled_dvr_hosts = set() dvr_broadcast = self._check_dvr_extensions(l3_plugin) if (dvr_broadcast): for router_id in router_ids: hosts = set(l3_plugin._get_dvr_hosts_for_router( context, router_id)) unscheduled_dvr_hosts |= hosts if no_broadcast or dvr_broadcast: scheduled_hosts = scheduled_hosts.union(unscheduled_dvr_hosts) return scheduled_hosts # 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 neutron-fwaas-12.0.2/neutron_fwaas/services/__init__.py0000664000175000017500000000000013555577713023226 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/locale/0000775000175000017500000000000013555600005020520 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/locale/en_GB/0000775000175000017500000000000013555600005021472 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/locale/en_GB/LC_MESSAGES/0000775000175000017500000000000013555600005023257 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/locale/en_GB/LC_MESSAGES/neutron_fwaas.po0000664000175000017500000000720413555577713026520 0ustar zuulzuul00000000000000# Andi Chandler , 2017. #zanata msgid "" msgstr "" "Project-Id-Version: neutron-fwaas 12.0.0.0b2.dev16\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2017-12-07 05:25+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2017-12-12 08:39+0000\n" "Last-Translator: Andi Chandler \n" "Language-Team: English (United Kingdom)\n" "Language: en-GB\n" "X-Generator: Zanata 3.9.6\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" #, python-format msgid "Cannot get vlan tag for port %(port_id)s." msgstr "Cannot get VLAN tag for port %(port_id)s." msgid "Configuration error - no FWaaS device_driver specified" msgstr "Configuration error - no FWaaS device_driver specified" #, python-format msgid "" "Duplicate Firewall group found named '%s'. Database cannot be upgraded. " "Please, remove all duplicates before upgrading the database." msgstr "" "Duplicate Firewall group found named '%s'. Database cannot be upgraded. " "Please, remove all duplicates before upgrading the database." #, python-format msgid "" "Duplicate port(s) %(port_id)s records exist " "infirewall_group_port_associations_v2 table. Database cannotbe upgraded. " "Please remove all duplicated records beforeupgrading the database." msgstr "" "Duplicate port(s) %(port_id)s records exist in " "firewall_group_port_associations_v2 table. Database cannot be upgraded. " "Please remove all duplicated records before upgrading the database." msgid "Enable FWaaS" msgstr "Enable FWaaS" #, python-format msgid "Error importing FWaaS device driver: %s" msgstr "Error importing FWaaS device driver: %s" #, python-format msgid "" "FWaaS driver error in %(event)s_firewall_group for firewall group: %(fwg_id)s" msgstr "" "FWaaS driver error in %(event)s_firewall_group for firewall group: %(fwg_id)s" #, python-format msgid "Failed to move back in original netns: %s" msgstr "Failed to move back in original netns: %s" #, python-format msgid "Failed to send status for firewall_group(%s)" msgstr "Failed to send status for firewall_group(%s)" msgid "Firewall agent class" msgstr "Firewall agent class" #, python-format msgid "Firewall l2 driver: %s is not compatible" msgstr "Firewall l2 driver: %s is not compatible" msgid "Name of the FWaaS Conntrack Driver" msgstr "Name of the FWaaS Conntrack Driver" msgid "Name of the FWaaS Driver" msgstr "Name of the FWaaS Driver" msgid "Name of the firewall l2 driver" msgstr "Name of the firewall l2 driver" msgid "" "Number of firewall policies allowed per tenant. A negative value means " "unlimited." msgstr "" "Number of firewall policies allowed per tenant. A negative value means " "unlimited." msgid "" "Number of firewall rules allowed per tenant. A negative value means " "unlimited." msgstr "" "Number of firewall rules allowed per tenant. A negative value means " "unlimited." msgid "" "Number of firewalls allowed per tenant. A negative value means unlimited." msgstr "" "Number of firewalls allowed per tenant. A negative value means unlimited." #, python-format msgid "" "Operation cannot be performed on default object '%(resource_id)s' of type " "%(resource_type)s." msgstr "" "Operation cannot be performed on default object '%(resource_id)s' of type " "%(resource_type)s." #, python-format msgid "" "Operation cannot be performed since '%(name)s' is a reserved name for " "%(resource_type)s." msgstr "" "Operation cannot be performed since '%(name)s' is a reserved name for " "%(resource_type)s." #, python-format msgid "Port %(port_id)s is not managed by this agent." msgstr "Port %(port_id)s is not managed by this agent." neutron-fwaas-12.0.2/neutron_fwaas/extensions/0000775000175000017500000000000013555600005021460 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/extensions/firewallrouterinsertion.py0000664000175000017500000000376713555577713027073 0ustar zuulzuul00000000000000# 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.definitions import firewallrouterinsertion from neutron_lib.api import extensions class Firewallrouterinsertion(extensions.APIExtensionDescriptor): """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. """ api_definition = firewallrouterinsertion neutron-fwaas-12.0.2/neutron_fwaas/extensions/firewall_v2.py0000664000175000017500000001565413555577713024304 0ustar zuulzuul00000000000000# Copyright (c) 2016 Mirantis, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc from debtcollector import moves from neutron.api.v2 import resource_helper from neutron_lib.api.definitions import constants as api_const from neutron_lib.api.definitions import firewall_v2 from neutron_lib.api import extensions from neutron_lib.exceptions import firewall_v2 as f_exc from neutron_lib.services import base as service_base import six from neutron_fwaas.common import fwaas_constants FirewallGroupNotFound = moves.moved_class( f_exc.FirewallGroupNotFound, 'FirewallGroupNotFound', __name__) FirewallGroupInUse = moves.moved_class( f_exc.FirewallGroupInUse, 'FirewallGroupInUse', __name__) FirewallGroupInPendingState = moves.moved_class( f_exc.FirewallGroupInPendingState, 'FirewallGroupInPendingState', __name__) FirewallGroupPortInvalid = moves.moved_class( f_exc.FirewallGroupPortInvalid, 'FirewallGroupPortInvalid', __name__) FirewallGroupPortInvalidProject = moves.moved_class( f_exc.FirewallGroupPortInvalidProject, 'FirewallGroupPortInvalidProject', __name__) FirewallGroupPortInUse = moves.moved_class( f_exc.FirewallGroupPortInUse, 'FirewallGroupPortInUse', __name__) FirewallPolicyNotFound = moves.moved_class( f_exc.FirewallPolicyNotFound, 'FirewallPolicyNotFound', __name__) FirewallPolicyInUse = moves.moved_class( f_exc.FirewallPolicyInUse, 'FirewallPolicyInUse', __name__) FirewallPolicyConflict = moves.moved_class( f_exc.FirewallPolicyConflict, 'FirewallPolicyConflict', __name__) FirewallRuleSharingConflict = moves.moved_class( f_exc.FirewallRuleSharingConflict, 'FirewallRuleSharingConflict', __name__) FirewallPolicySharingConflict = moves.moved_class( f_exc.FirewallPolicySharingConflict, 'FirewallPolicySharingConflict', __name__) FirewallRuleNotFound = moves.moved_class( f_exc.FirewallRuleNotFound, 'FirewallRuleNotFound', __name__) FirewallRuleInUse = moves.moved_class( f_exc.FirewallRuleInUse, 'FirewallRuleInUse', __name__) FirewallRuleNotAssociatedWithPolicy = moves.moved_class( f_exc.FirewallRuleNotAssociatedWithPolicy, 'FirewallRuleNotAssociatedWithPolicy', __name__) FirewallRuleInvalidProtocol = moves.moved_class( f_exc.FirewallRuleInvalidProtocol, 'FirewallRuleInvalidProtocol', __name__) FirewallRuleInvalidAction = moves.moved_class( f_exc.FirewallRuleInvalidAction, 'FirewallRuleInvalidAction', __name__) FirewallRuleInvalidICMPParameter = moves.moved_class( f_exc.FirewallRuleInvalidICMPParameter, 'FirewallRuleInvalidICMPParameter', __name__) FirewallRuleWithPortWithoutProtocolInvalid = moves.moved_class( f_exc.FirewallRuleWithPortWithoutProtocolInvalid, 'FirewallRuleWithPortWithoutProtocolInvalid', __name__) FirewallRuleInvalidPortValue = moves.moved_class( f_exc.FirewallRuleInvalidPortValue, 'FirewallRuleInvalidPortValue', __name__) FirewallRuleInfoMissing = moves.moved_class( f_exc.FirewallRuleInfoMissing, 'FirewallRuleInfoMissing', __name__) FirewallIpAddressConflict = moves.moved_class( f_exc.FirewallIpAddressConflict, 'FirewallIpAddressConflict', __name__) FirewallInternalDriverError = moves.moved_class( f_exc.FirewallInternalDriverError, 'FirewallInternalDriverError', __name__) FirewallRuleConflict = moves.moved_class( f_exc.FirewallRuleConflict, 'FirewallRuleConflict', __name__) FirewallRuleAlreadyAssociated = moves.moved_class( f_exc.FirewallRuleAlreadyAssociated, 'FirewallRuleAlreadyAssociated', __name__) # TODO(Reedip): Remove the convert_to functionality after bug1706061 is fixed. def convert_to_string(value): if value is not None: return str(value) return None firewall_v2.RESOURCE_ATTRIBUTE_MAP[api_const.FIREWALL_RULES][ 'source_port']['convert_to'] = convert_to_string firewall_v2.RESOURCE_ATTRIBUTE_MAP[api_const.FIREWALL_RULES][ 'destination_port']['convert_to'] = convert_to_string class Firewall_v2(extensions.APIExtensionDescriptor): api_definition = firewall_v2 @classmethod def get_resources(cls): special_mappings = {'firewall_policies': 'firewall_policy'} plural_mappings = resource_helper.build_plural_mappings( special_mappings, firewall_v2.RESOURCE_ATTRIBUTE_MAP) return resource_helper.build_resource_info( plural_mappings, firewall_v2.RESOURCE_ATTRIBUTE_MAP, fwaas_constants.FIREWALL_V2, action_map=firewall_v2.ACTION_MAP, register_quota=True) @classmethod def get_plugin_interface(cls): return Firewallv2PluginBase @six.add_metaclass(abc.ABCMeta) class Firewallv2PluginBase(service_base.ServicePluginBase): def get_plugin_name(self): return fwaas_constants.FIREWALL_V2 def get_plugin_type(self): return fwaas_constants.FIREWALL_V2 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 neutron-fwaas-12.0.2/neutron_fwaas/extensions/firewall.py0000664000175000017500000001602213555577713023663 0ustar zuulzuul00000000000000# 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.definitions import constants as api_const from neutron_lib.api.definitions import firewall from neutron_lib.api import extensions 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_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') # TODO(Reedip): Remove the convert_to functionality after bug1706061 is fixed. def convert_to_string(value): if value is not None: return str(value) return None firewall.RESOURCE_ATTRIBUTE_MAP[api_const.FIREWALL_RULES][ 'source_port']['convert_to'] = convert_to_string firewall.RESOURCE_ATTRIBUTE_MAP[api_const.FIREWALL_RULES][ 'destination_port']['convert_to'] = convert_to_string class Firewall(extensions.APIExtensionDescriptor): api_definition = firewall @classmethod def get_resources(cls): special_mappings = {'firewall_policies': 'firewall_policy'} plural_mappings = resource_helper.build_plural_mappings( special_mappings, firewall.RESOURCE_ATTRIBUTE_MAP) return resource_helper.build_resource_info( plural_mappings, firewall.RESOURCE_ATTRIBUTE_MAP, fwaas_constants.FIREWALL, action_map=firewall.ACTION_MAP, register_quota=True) @classmethod def get_plugin_interface(cls): return FirewallPluginBase @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 neutron-fwaas-12.0.2/neutron_fwaas/extensions/__init__.py0000664000175000017500000000000013555577713023602 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/privileged/0000775000175000017500000000000013555600005021413 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/privileged/netlink_constants.py0000664000175000017500000000526113555577713025554 0ustar zuulzuul00000000000000# 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 # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # import socket CONNTRACK = 0 NFCT_O_PLAIN = 0 NFCT_OF_TIME_BIT = 1 NFCT_OF_TIME = 1 << NFCT_OF_TIME_BIT NFCT_Q_DESTROY = 2 NFCT_Q_FLUSH = 4 NFCT_Q_DUMP = 5 NFCT_T_DESTROY_BIT = 2 NFCT_T_DESTROY = 1 << NFCT_T_DESTROY_BIT ATTR_IPV4_SRC = 0 ATTR_IPV4_DST = 1 ATTR_IPV6_SRC = 4 ATTR_IPV6_DST = 5 ATTR_PORT_SRC = 8 ATTR_PORT_DST = 9 ATTR_ICMP_TYPE = 12 ATTR_ICMP_CODE = 13 ATTR_ICMP_ID = 14 ATTR_L3PROTO = 15 ATTR_L4PROTO = 17 NFCT_T_NEW_BIT = 0 NFCT_T_NEW = 1 << NFCT_T_NEW_BIT NFCT_T_UPDATE_BIT = 1 NFCT_T_UPDATE = 1 << NFCT_T_UPDATE_BIT NFCT_T_DESTROY_BIT = 2 NFCT_T_DESTROY = 1 << NFCT_T_DESTROY_BIT NFCT_T_ALL = NFCT_T_NEW | NFCT_T_UPDATE | NFCT_T_DESTROY NFCT_CB_CONTINUE = 1 NFCT_CB_FAILURE = -1 NFNL_SUBSYS_CTNETLINK = 0 BUFFER = 1024 # IPv6 address memory buffer ADDR_BUFFER_6 = 16 ADDR_BUFFER_4 = 4 IPVERSION_SOCKET = {4: socket.AF_INET, 6: socket.AF_INET6} IPVERSION_BUFFER = {4: ADDR_BUFFER_4, 6: ADDR_BUFFER_6} neutron-fwaas-12.0.2/neutron_fwaas/privileged/utils.py0000664000175000017500000000372713555577713023161 0ustar zuulzuul00000000000000# 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) neutron-fwaas-12.0.2/neutron_fwaas/privileged/netlink_lib.py0000664000175000017500000002567413555577713024320 0ustar zuulzuul00000000000000# 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 # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # import ctypes from ctypes import util from oslo_log import log as logging from neutron_lib import constants from neutron_fwaas import privileged from neutron_fwaas.privileged import netlink_constants as nl_constants from neutron_fwaas.privileged import utils as fwaas_utils LOG = logging.getLogger(__name__) nfct = 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)], 'icmpv6': [('type', 6), ('code', 7), ('src', 4), ('dst', 5), ('id', 8)], 'tcp': [('sport', 7), ('dport', 8), ('src', 5), ('dst', 6)], 'udp': [('sport', 6), ('dport', 7), ('src', 4), ('dst', 5)] } TARGET = {'src': {4: nl_constants.ATTR_IPV4_SRC, 6: nl_constants.ATTR_IPV6_SRC}, 'dst': {4: nl_constants.ATTR_IPV4_DST, 6: nl_constants.ATTR_IPV6_DST}, 'ipversion': {4: nl_constants.ATTR_L3PROTO, 6: nl_constants.ATTR_L3PROTO}, 'protocol': {4: nl_constants.ATTR_L4PROTO, 6: nl_constants.ATTR_L4PROTO}, 'code': {4: nl_constants.ATTR_ICMP_CODE, 6: nl_constants.ATTR_ICMP_CODE}, 'type': {4: nl_constants.ATTR_ICMP_TYPE, 6: nl_constants.ATTR_ICMP_TYPE}, 'id': {4: nl_constants.ATTR_ICMP_ID, 6: nl_constants.ATTR_ICMP_ID}, 'sport': {4: nl_constants.ATTR_PORT_SRC, 6: nl_constants.ATTR_PORT_SRC}, 'dport': {4: nl_constants.ATTR_PORT_DST, 6: nl_constants.ATTR_PORT_DST}} NFCT_CALLBACK = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_void_p, ctypes.c_void_p) class ConntrackOpenFailedExit(SystemExit): """Raised if we fail to open a new conntrack or conntrack handler""" class ConntrackManager(object): def __init__(self, family_socket=None): self.family_socket = family_socket self.set_functions = { 'src': {4: nfct.nfct_set_attr, 6: nfct.nfct_set_attr}, 'dst': {4: nfct.nfct_set_attr, 6: nfct.nfct_set_attr}, 'ipversion': {4: nfct.nfct_set_attr_u8, 6: nfct.nfct_set_attr_u8}, 'protocol': {4: nfct.nfct_set_attr_u8, 6: nfct.nfct_set_attr_u8}, 'type': {4: nfct.nfct_set_attr_u8, 6: nfct.nfct_set_attr_u8}, 'code': {4: nfct.nfct_set_attr_u8, 6: nfct.nfct_set_attr_u8}, 'id': {4: nfct.nfct_set_attr_u16, 6: nfct.nfct_set_attr_u16}, 'sport': {4: nfct.nfct_set_attr_u16, 6: nfct.nfct_set_attr_u16}, 'dport': {4: nfct.nfct_set_attr_u16, 6: nfct.nfct_set_attr_u16}, } self.converters = {'src': str, 'dst': str, '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("Netlink query failed") def _convert_text_to_binary(self, source, addr_family): dest = ctypes.create_string_buffer( nl_constants.IPVERSION_BUFFER[addr_family]) libc.inet_pton(nl_constants.IPVERSION_SOCKET[addr_family], source, dest) return dest.raw def _set_attributes(self, conntrack, entry): ipversion = entry.get('ipversion', 4) for attr, value in entry.items(): set_function = self.set_functions[attr][ipversion] target = TARGET[attr][ipversion] converter = self.converters[attr] if attr in ['src', 'dst']: # convert src and dst of IPv4 and IPv6 into same format value = self._convert_text_to_binary(value, ipversion) set_function(conntrack, target, converter(value)) def _callback_register(self, message_type, callback_func, data): nfct.nfct_callback_register(self.conntrack_handler, message_type, callback_func, data) def _get_ref(self, data): return ctypes.byref(ctypes.c_int(data)) def __enter__(self): self.conntrack_handler = nfct.nfct_open( nl_constants.CONNTRACK, nl_constants.NFNL_SUBSYS_CTNETLINK) if not self.conntrack_handler: msg = "Failed to open new conntrack handler" LOG.critical(msg) raise ConntrackOpenFailedExit(msg) return self def __exit__(self, *args): nfct.nfct_close(self.conntrack_handler) def _parse_entry(entry, ipversion): """Parse entry from text to Python tuple :param entry: conntrack entry in text :param ipversion: ipversion 4 or 6 :return: conntrack entry in Python tuple example: (4, 'tcp', '1', '2', '1.1.1.1', '2.2.2.2') The attributes are ordered to be easy to compare with other entries and compare with firewall rule """ protocol = entry[1] parsed_entry = [ipversion, protocol] for attr, position in ATTR_POSITIONS[protocol]: val = entry[position].partition('=')[2] parsed_entry.append(int(val) if attr in ['sport', 'dport', 'type', 'code', 'id'] else val) return tuple(parsed_entry) @privileged.default.entrypoint def flush_entries(namespace=None): """Delete all conntrack entries :param namespace: namespace to delete conntrack entries :return: None """ with fwaas_utils.in_namespace(namespace): for ipversion in IP_VERSIONS: with ConntrackManager(nl_constants.IPVERSION_SOCKET[ipversion]) \ as conntrack: conntrack.flush_entries() @privileged.default.entrypoint def list_entries(namespace=None): """List and parse all conntrack entries :param namespace: namespace to get conntrack entries :return: sorted list of conntrack entries in Python tuple example: [(4, 'icmp', '8', '0', '1.1.1.1', '2.2.2.2', '1234'), (4, 'tcp', '1', '2', '1.1.1.1', '2.2.2.2')] """ parsed_entries = [] with fwaas_utils.in_namespace(namespace): for ipversion in IP_VERSIONS: with ConntrackManager(nl_constants.IPVERSION_SOCKET[ipversion]) \ as conntrack: raw_entries = conntrack.list_entries() for raw_entry in raw_entries: parsed_entry = _parse_entry(raw_entry.split(), ipversion) parsed_entries.append(parsed_entry) return sorted(parsed_entries) @privileged.default.entrypoint def delete_entries(entries, namespace=None): """Delete selected entries :param entries: list of parsed (as tuple) entries to delete :param namespace: namespace to delete conntrack entries :return: None """ entry_args = [] for entry in entries: entry_arg = {'ipversion': entry[0], 'protocol': entry[1]} for idx, attr in enumerate(ATTR_POSITIONS[entry_arg['protocol']]): entry_arg[attr[0]] = entry[idx + 2] entry_args.append(entry_arg) with fwaas_utils.in_namespace(namespace): with ConntrackManager() as conntrack: conntrack.delete_entries(entry_args) neutron-fwaas-12.0.2/neutron_fwaas/privileged/__init__.py0000664000175000017500000000225113555577713023547 0ustar zuulzuul00000000000000# 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], ) neutron-fwaas-12.0.2/neutron_fwaas/privileged/tests/0000775000175000017500000000000013555600005022555 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/privileged/tests/__init__.py0000664000175000017500000000000013555577713024677 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/privileged/tests/functional/0000775000175000017500000000000013555600005024717 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/privileged/tests/functional/utils.py0000664000175000017500000000224113555577713026453 0ustar zuulzuul00000000000000# 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:[]" 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 neutron-fwaas-12.0.2/neutron_fwaas/privileged/tests/functional/__init__.py0000664000175000017500000000000013555577713027041 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/privileged/tests/functional/dummy.py0000664000175000017500000000211013555577713026441 0ustar zuulzuul00000000000000# 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) neutron-fwaas-12.0.2/neutron_fwaas/version.py0000664000175000017500000000126413555577713021346 0ustar zuulzuul00000000000000# Copyright 2011 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. import pbr.version version_info = pbr.version.VersionInfo('neutron-fwaas') neutron-fwaas-12.0.2/neutron_fwaas/opts.py0000664000175000017500000000163213555577713020645 0ustar zuulzuul00000000000000# 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.extensions.firewall 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) ] neutron-fwaas-12.0.2/neutron_fwaas/__init__.py0000664000175000017500000000136413555577713021421 0ustar zuulzuul00000000000000# 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') neutron-fwaas-12.0.2/neutron_fwaas/tests/0000775000175000017500000000000013555600005020423 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/0000775000175000017500000000000013555600005021402 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/0000775000175000017500000000000013555600005023225 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/0000775000175000017500000000000013555600005025032 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/test_fwaas_plugin.py0000664000175000017500000012001213555577713031141 0ustar zuulzuul00000000000000# 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 mock from neutron.api import extensions as api_ext from neutron.common import config from neutron.tests.common import helpers from neutron.tests import fake_notifier from neutron.tests.unit.extensions import test_agent from neutron.tests.unit.extensions import test_l3 as test_l3_plugin from neutron_lib.api import attributes as attr from neutron_lib.api.definitions import firewall as fwaas_def from neutron_lib.api.definitions import firewallrouterinsertion from neutron_lib import constants as nl_constants from neutron_lib import 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 oslo_config import cfg from oslo_utils import uuidutils import six import testtools from webob import exc from neutron_fwaas.db.firewall import firewall_db as fdb import neutron_fwaas.extensions from neutron_fwaas.extensions import firewall from neutron_fwaas.services.firewall import fwaas_plugin from neutron_fwaas.tests import base from neutron_fwaas.tests.unit.db.firewall import ( test_firewall_db as test_db_firewall) extensions_path = neutron_fwaas.extensions.__path__[0] FW_PLUGIN_KLASS = ( "neutron_fwaas.services.firewall.fwaas_plugin.FirewallPlugin" ) class FirewallTestExtensionManager(test_l3_plugin.L3TestExtensionManager): def get_resources(self): res = super(FirewallTestExtensionManager, self).get_resources() fwaas_def.RESOURCE_ATTRIBUTE_MAP['firewalls'].update( firewallrouterinsertion.RESOURCE_ATTRIBUTE_MAP['firewalls']) return res + firewall.Firewall.get_resources() def get_actions(self): return [] def get_request_extensions(self): return [] class TestFirewallRouterInsertionBase( test_db_firewall.FirewallPluginDbTestCase): def setUp(self, core_plugin=None, fw_plugin=None, ext_mgr=None): self.agentapi_del_fw_p = mock.patch(test_db_firewall.DELETEFW_PATH, create=True, new=test_db_firewall.FakeAgentApi().delete_firewall) self.agentapi_del_fw_p.start() # the plugin without L3 support plugin = 'neutron.tests.unit.extensions.test_l3.TestNoL3NatPlugin' # the L3 service plugin l3_plugin = ('neutron.tests.unit.extensions.test_l3.' 'TestL3NatAgentSchedulingServicePlugin') cfg.CONF.set_override('api_extensions_path', extensions_path) self.saved_attr_map = {} for resource, attrs in six.iteritems(attr.RESOURCES): self.saved_attr_map[resource] = attrs.copy() if not fw_plugin: fw_plugin = FW_PLUGIN_KLASS service_plugins = {'l3_plugin_name': l3_plugin, 'fw_plugin_name': fw_plugin} if not ext_mgr: ext_mgr = FirewallTestExtensionManager() super(test_db_firewall.FirewallPluginDbTestCase, self).setUp( plugin=plugin, service_plugins=service_plugins, ext_mgr=ext_mgr) self.addCleanup(self.restore_attribute_map) self.setup_notification_driver() self.l3_plugin = directory.get_plugin(plugin_constants.L3) self.plugin = directory.get_plugin('FIREWALL') self.callbacks = self.plugin.endpoints[0] def restore_attribute_map(self): # Remove the fwaasrouterinsertion extension fwaas_def.RESOURCE_ATTRIBUTE_MAP['firewalls'].pop('router_ids') # Restore the original RESOURCE_ATTRIBUTE_MAP attr.RESOURCES = self.saved_attr_map def _create_firewall(self, fmt, name, description, firewall_policy_id=None, admin_state_up=True, expected_res_status=None, **kwargs): tenant_id = kwargs.get('tenant_id', self._tenant_id) router_ids = kwargs.get('router_ids') if firewall_policy_id is None: res = self._create_firewall_policy(fmt, 'fwp', description="firewall_policy", shared=True, firewall_rules=[], audited=True) firewall_policy = self.deserialize(fmt or self.fmt, res) firewall_policy_id = firewall_policy["firewall_policy"]["id"] data = {'firewall': {'name': name, 'description': description, 'firewall_policy_id': firewall_policy_id, 'admin_state_up': admin_state_up, 'tenant_id': tenant_id}} if router_ids is not None: data['firewall']['router_ids'] = router_ids firewall_req = self.new_create_request('firewalls', data, fmt) firewall_res = firewall_req.get_response(self.ext_api) if expected_res_status: self.assertEqual(expected_res_status, firewall_res.status_int) return firewall_res class TestFirewallCallbacks(TestFirewallRouterInsertionBase): def test_set_firewall_status(self): ctx = context.get_admin_context() with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall( firewall_policy_id=fwp_id, admin_state_up=test_db_firewall.ADMIN_STATE_UP ) as fw: fw_id = fw['firewall']['id'] res = self.callbacks.set_firewall_status(ctx, fw_id, nl_constants.ACTIVE) fw_db = self.plugin.get_firewall(ctx, fw_id) self.assertEqual(nl_constants.ACTIVE, fw_db['status']) self.assertTrue(res) res = self.callbacks.set_firewall_status(ctx, fw_id, nl_constants.ERROR) fw_db = self.plugin.get_firewall(ctx, fw_id) self.assertEqual(nl_constants.ERROR, fw_db['status']) self.assertFalse(res) def test_set_firewall_status_pending_delete(self): ctx = context.get_admin_context() with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall( firewall_policy_id=fwp_id, admin_state_up=test_db_firewall.ADMIN_STATE_UP ) as fw: fw_id = fw['firewall']['id'] fw_db = self.plugin._get_firewall(ctx, fw_id) fw_db['status'] = nl_constants.PENDING_DELETE ctx.session.flush() res = self.callbacks.set_firewall_status(ctx, fw_id, nl_constants.ACTIVE) fw_db = self.plugin.get_firewall(ctx, fw_id) self.assertEqual(nl_constants.PENDING_DELETE, fw_db['status']) self.assertFalse(res) def test_firewall_deleted(self): ctx = context.get_admin_context() with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall(firewall_policy_id=fwp_id, admin_state_up=test_db_firewall.ADMIN_STATE_UP, do_delete=False) as fw: fw_id = fw['firewall']['id'] with ctx.session.begin(subtransactions=True): fw_db = self.plugin._get_firewall(ctx, fw_id) fw_db['status'] = nl_constants.PENDING_DELETE ctx.session.flush() res = self.callbacks.firewall_deleted(ctx, fw_id) self.assertTrue(res) self.assertRaises(f_exc.FirewallNotFound, self.plugin.get_firewall, ctx, fw_id) def test_firewall_deleted_concurrently(self): ctx = context.get_admin_context() alt_ctx = context.get_admin_context() _get_firewall = self.plugin._get_firewall def getdelete(context, firewall_id): fw_db = _get_firewall(context, firewall_id) # NOTE(cby): Use a different session to simulate a concurrent del self.plugin.delete_db_firewall_object(alt_ctx, firewall_id) return fw_db with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall( firewall_policy_id=fwp_id, admin_state_up=test_db_firewall.ADMIN_STATE_UP, do_delete=False ) as fw: fw_id = fw['firewall']['id'] with ctx.session.begin(subtransactions=True): fw_db = self.plugin._get_firewall(ctx, fw_id) fw_db['status'] = nl_constants.PENDING_DELETE ctx.session.flush() with mock.patch.object( self.plugin, '_get_firewall', side_effect=getdelete ): observed = self.callbacks.firewall_deleted(ctx, fw_id) self.assertTrue(observed) self.assertRaises(f_exc.FirewallNotFound, self.plugin.get_firewall, ctx, fw_id) def test_firewall_deleted_not_found(self): ctx = context.get_admin_context() observed = self.callbacks.firewall_deleted(ctx, 'notfound') self.assertTrue(observed) def test_firewall_deleted_error(self): ctx = context.get_admin_context() with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall( firewall_policy_id=fwp_id, admin_state_up=test_db_firewall.ADMIN_STATE_UP, ) as fw: fw_id = fw['firewall']['id'] res = self.callbacks.firewall_deleted(ctx, fw_id) self.assertFalse(res) fw_db = self.plugin._get_firewall(ctx, fw_id) self.assertEqual(nl_constants.ERROR, fw_db['status']) def test_get_firewall_for_tenant(self): tenant_id = 'test-tenant' ctx = context.Context('', tenant_id) with self.firewall_rule(name='fwr1', tenant_id=tenant_id) as fwr1, \ self.firewall_rule(name='fwr2', tenant_id=tenant_id) as fwr2, \ self.firewall_rule(name='fwr3', tenant_id=tenant_id) as fwr3: with self.firewall_policy(tenant_id=tenant_id) as fwp: fwp_id = fwp['firewall_policy']['id'] fr = [fwr1, fwr2, fwr3] fw_rule_ids = [r['firewall_rule']['id'] for r in fr] data = {'firewall_policy': {'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp_id) res = req.get_response(self.ext_api) attrs = self._get_test_firewall_attrs() attrs['firewall_policy_id'] = fwp_id with self.firewall( firewall_policy_id=fwp_id, tenant_id=tenant_id, admin_state_up=test_db_firewall.ADMIN_STATE_UP) as fw: fw_id = fw['firewall']['id'] res = self.callbacks.get_firewalls_for_tenant(ctx) fw_rules = ( self.plugin._make_firewall_dict_with_rules(ctx, fw_id) ) fw_rules['add-router-ids'] = [] fw_rules['del-router-ids'] = [] self.assertEqual(fw_rules, res[0]) self._compare_firewall_rule_lists( fwp_id, fr, res[0]['firewall_rule_list']) class TestFirewallAgentApi(base.BaseTestCase): def setUp(self): super(TestFirewallAgentApi, self).setUp() self.api = fwaas_plugin.FirewallAgentApi('topic', 'host') def test_init(self): self.assertEqual('topic', self.api.client.target.topic) self.assertEqual('host', self.api.host) def _call_test_helper(self, method_name, host): with mock.patch.object(self.api.client, 'cast') as rpc_mock, \ mock.patch.object(self.api.client, 'prepare') as prepare_mock: prepare_mock.return_value = self.api.client getattr(self.api, method_name)(mock.sentinel.context, 'test', host) prepare_args = {'server': host} prepare_mock.assert_called_once_with(**prepare_args) rpc_mock.assert_called_once_with(mock.sentinel.context, method_name, firewall='test', host='host') def test_create_firewall(self): self._call_test_helper('create_firewall', 'host') def test_update_firewall(self): self._call_test_helper('update_firewall', 'host') def test_delete_firewall(self): self._call_test_helper('delete_firewall', 'host') class TestFirewallPluginBase(TestFirewallRouterInsertionBase, test_l3_plugin.L3NatTestCaseMixin): def setUp(self): super(TestFirewallPluginBase, self).setUp(fw_plugin=FW_PLUGIN_KLASS) fake_notifier.reset() def test_create_firewall_routers_not_specified(self): """neutron firewall-create test-policy """ with self.router(name='router1', admin_state_up=True, tenant_id=self._tenant_id): with self.router(name='router2', admin_state_up=True, tenant_id=self._tenant_id): with self.firewall() as fw1: self.assertEqual(nl_constants.PENDING_CREATE, fw1['firewall']['status']) def test_create_firewall_routers_specified(self): """neutron firewall-create test-policy --router-ids "r1 r2" """ with self.router(name='router1', admin_state_up=True, tenant_id=self._tenant_id) as router1: with self.router(name='router2', admin_state_up=True, tenant_id=self._tenant_id) as router2: router_ids = [router1['router']['id'], router2['router']['id']] with self.firewall(router_ids=router_ids) as fw1: self.assertEqual(nl_constants.PENDING_CREATE, fw1['firewall']['status']) def test_create_firewall_routers_present_empty_list_specified(self): """neutron firewall-create test-policy --router-ids "" """ with self.router(name='router1', admin_state_up=True, tenant_id=self._tenant_id): with self.router(name='router2', admin_state_up=True, tenant_id=self._tenant_id): router_ids = [] with self.firewall(router_ids=router_ids) as fw1: self.assertEqual(nl_constants.INACTIVE, fw1['firewall']['status']) def test_create_firewall_no_routers_empty_list_specified(self): """neutron firewall-create test-policy --router-ids "" """ router_ids = [] with self.firewall(router_ids=router_ids) as fw1: self.assertEqual(nl_constants.INACTIVE, fw1['firewall']['status']) def test_create_second_firewall_on_same_tenant(self): """fw1 created with default routers, fw2 no routers on same tenant.""" with self.router(name='router1', admin_state_up=True, tenant_id=self._tenant_id): with self.router(name='router2', admin_state_up=True, tenant_id=self._tenant_id): router_ids = [] with self.firewall() as fw1: with self.firewall(router_ids=router_ids) as fw2: self.assertEqual(nl_constants.PENDING_CREATE, fw1['firewall']['status']) self.assertEqual(nl_constants.INACTIVE, fw2['firewall']['status']) def test_create_firewall_admin_not_affected_by_other_tenant(self): # Create fw with admin after creating fw with other tenant with self.firewall(tenant_id='other-tenant') as fw1: with self.firewall() as fw2: self.assertEqual('other-tenant', fw1['firewall']['tenant_id']) self.assertEqual(self._tenant_id, fw2['firewall']['tenant_id']) def test_update_firewall_calls_get_dvr_hosts_for_router(self): ctx = context.get_admin_context() name = "user_fw" attrs = self._get_test_firewall_attrs(name) check_attr1 = getattr(self.l3_plugin, "get_l3_agents_hosting_routers", False) check_attr2 = getattr(self.l3_plugin, "_get_dvr_hosts_for_router", False) # For third-party L3-service plugins do not run this test if check_attr1 is False or check_attr2 is False: return with self.router(name='router1', admin_state_up=True, tenant_id=self._tenant_id) as router1: with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] attrs['firewall_policy_id'] = fwp_id with self.firewall( firewall_policy_id=fwp_id, admin_state_up=test_db_firewall.ADMIN_STATE_UP, router_ids=[router1['router']['id']] ) as firewall: fw_id = firewall['firewall']['id'] self.callbacks.set_firewall_status(ctx, fw_id, nl_constants.ACTIVE) with mock.patch.object( self.l3_plugin, 'get_l3_agents_hosting_routers') as s_hosts, \ mock.patch.object( self.plugin, '_check_dvr_extensions') as dvr_exts, \ mock.patch.object( self.l3_plugin, '_get_dvr_hosts_for_router') as u_hosts: self.plugin.update_firewall(ctx, fw_id, firewall) dvr_exts.return_value = True self.assertTrue(u_hosts.called) self.assertTrue(s_hosts.called) def test_update_firewall(self): ctx = context.get_admin_context() name = "new_firewall1" attrs = self._get_test_firewall_attrs(name) with self.router(name='router1', admin_state_up=True, tenant_id=self._tenant_id) as router1: with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] attrs['firewall_policy_id'] = fwp_id with self.firewall( firewall_policy_id=fwp_id, admin_state_up=test_db_firewall.ADMIN_STATE_UP, router_ids=[router1['router']['id']] ) as firewall: fw_id = firewall['firewall']['id'] res = self.callbacks.set_firewall_status(ctx, fw_id, nl_constants.ACTIVE) data = {'firewall': {'name': name}} req = self.new_update_request('firewalls', data, fw_id) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) attrs = self._replace_firewall_status(attrs, nl_constants. PENDING_CREATE, nl_constants. PENDING_UPDATE) for k, v in six.iteritems(attrs): self.assertEqual(v, res['firewall'][k]) def test_update_firewall_fails_when_firewall_pending(self): name = "new_firewall1" attrs = self._get_test_firewall_attrs(name) with self.router(name='router1', admin_state_up=True, tenant_id=self._tenant_id) as router1: with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] attrs['firewall_policy_id'] = fwp_id with self.firewall( firewall_policy_id=fwp_id, admin_state_up=test_db_firewall.ADMIN_STATE_UP, router_ids=[router1['router']['id']] ) as firewall: fw_id = firewall['firewall']['id'] data = {'firewall': {'name': name}} req = self.new_update_request('firewalls', data, fw_id) res = req.get_response(self.ext_api) self.assertEqual(exc.HTTPConflict.code, res.status_int) def test_update_firewall_with_router_when_firewall_inactive(self): name = "firewall1" attrs = self._get_test_firewall_attrs(name) with self.router(name='router1', admin_state_up=True, tenant_id=self._tenant_id) as router1: with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] attrs['firewall_policy_id'] = fwp_id with self.firewall( name=name, firewall_policy_id=fwp_id, admin_state_up=test_db_firewall.ADMIN_STATE_UP, router_ids=[] ) as firewall: fw_id = firewall['firewall']['id'] data = { 'firewall': {'router_ids': [router1['router']['id']]}} req = self.new_update_request('firewalls', data, fw_id) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) attrs = self._replace_firewall_status(attrs, nl_constants. PENDING_CREATE, nl_constants. PENDING_UPDATE) for k, v in six.iteritems(attrs): self.assertEqual(v, res['firewall'][k]) @testtools.skip('bug/1622694') def test_update_firewall_shared_fails_for_non_admin(self): ctx = context.get_admin_context() with self.router(name='router1', admin_state_up=True, tenant_id=self._tenant_id) as router1: with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall( firewall_policy_id=fwp_id, admin_state_up=test_db_firewall.ADMIN_STATE_UP, tenant_id='noadmin', router_ids=[router1['router']['id']] ) as firewall: fw_id = firewall['firewall']['id'] self.callbacks.set_firewall_status(ctx, fw_id, nl_constants.ACTIVE) data = {'firewall': {'shared': True}} req = self.new_update_request( 'firewalls', data, fw_id, context=context.Context('', 'noadmin')) res = req.get_response(self.ext_api) self.assertEqual(exc.HTTPForbidden.code, res.status_int) def test_update_firewall_policy_fails_when_firewall_pending(self): name = "new_firewall1" attrs = self._get_test_firewall_attrs(name) with self.router(name='router1', admin_state_up=True, tenant_id=self._tenant_id): with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] attrs['firewall_policy_id'] = fwp_id with self.firewall( firewall_policy_id=fwp_id, admin_state_up=test_db_firewall.ADMIN_STATE_UP ): data = {'firewall_policy': {'name': name}} req = self.new_update_request('firewall_policies', data, fwp_id) res = req.get_response(self.ext_api) self.assertEqual(exc.HTTPConflict.code, res.status_int) def test_update_firewall_rule_fails_when_firewall_pending(self): with self.router(name='router1', admin_state_up=True, tenant_id=self._tenant_id): with self.firewall_rule(name='fwr1') as fr: with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] fr_id = fr['firewall_rule']['id'] fw_rule_ids = [fr_id] data = {'firewall_policy': {'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp_id) req.get_response(self.ext_api) with self.firewall( firewall_policy_id=fwp_id, admin_state_up=test_db_firewall.ADMIN_STATE_UP ): data = {'firewall_rule': {'protocol': 'udp'}} req = self.new_update_request('firewall_rules', data, fr_id) res = req.get_response(self.ext_api) self.assertEqual(exc.HTTPConflict.code, res.status_int) def test_delete_firewall_with_no_routers(self): ctx = context.get_admin_context() # stop the AgentRPC patch for this one to test pending states self.agentapi_del_fw_p.stop() with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall( firewall_policy_id=fwp_id, admin_state_up=test_db_firewall.ADMIN_STATE_UP, do_delete=False ) as fw: fw_id = fw['firewall']['id'] req = self.new_delete_request('firewalls', fw_id) res = req.get_response(self.ext_api) self.assertEqual(exc.HTTPNoContent.code, res.status_int) self.assertRaises(f_exc.FirewallNotFound, self.plugin.get_firewall, ctx, fw_id) def test_delete_firewall_after_agent_delete(self): ctx = context.get_admin_context() with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall(firewall_policy_id=fwp_id, do_delete=False) as fw: fw_id = fw['firewall']['id'] req = self.new_delete_request('firewalls', fw_id) res = req.get_response(self.ext_api) self.assertEqual(exc.HTTPNoContent.code, res.status_int) self.assertRaises(f_exc.FirewallNotFound, self.plugin.get_firewall, ctx, fw_id) def test_make_firewall_dict_with_in_place_rules(self): ctx = context.get_admin_context() with self.firewall_rule(name='fwr1') as fwr1, \ self.firewall_rule(name='fwr2') as fwr2, \ self.firewall_rule(name='fwr3') as fwr3: with self.firewall_policy() as fwp: fr = [fwr1, fwr2, fwr3] fwp_id = fwp['firewall_policy']['id'] fw_rule_ids = [r['firewall_rule']['id'] for r in fr] data = {'firewall_policy': {'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp_id) req.get_response(self.ext_api) attrs = self._get_test_firewall_attrs() attrs['firewall_policy_id'] = fwp_id with self.firewall( firewall_policy_id=fwp_id, admin_state_up=test_db_firewall.ADMIN_STATE_UP, router_ids=[] ) as fw: fw_id = fw['firewall']['id'] fw_rules = ( self.plugin._make_firewall_dict_with_rules(ctx, fw_id) ) self.assertEqual(fw_id, fw_rules['id']) self._compare_firewall_rule_lists( fwp_id, fr, fw_rules['firewall_rule_list']) def test_make_firewall_dict_with_in_place_rules_no_policy(self): ctx = context.get_admin_context() with self.firewall() as fw: fw_id = fw['firewall']['id'] fw_rules = self.plugin._make_firewall_dict_with_rules(ctx, fw_id) self.assertEqual([], fw_rules['firewall_rule_list']) def test_list_firewalls(self): with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall(name='fw1', firewall_policy_id=fwp_id, description='fw') as fwalls: self._test_list_resources('firewall', [fwalls], query_params='description=fw') def test_list_firewalls_with_filtering(self): with self.router(name='my_router', admin_state_up=True, tenant_id=self._tenant_id) as router: router_id = router['router']['id'] with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall(name='fw1', firewall_policy_id=fwp_id, description='fw', router_ids=[router_id]) as fwalls: filter_pattern = None fw = fwalls['firewall'] for filter_pattern in fw: query_params = 'fields=%s' % filter_pattern expect = [{filter_pattern: fw[filter_pattern]}] self._test_list_resources('firewall', expect, query_params=query_params) def test_insert_rule(self): ctx = context.get_admin_context() with self.firewall_rule() as fwr: fr_id = fwr['firewall_rule']['id'] rule_info = {'firewall_rule_id': fr_id} with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall(firewall_policy_id=fwp_id) as fw: fw_id = fw['firewall']['id'] self.plugin.insert_rule(ctx, fwp_id, rule_info) fw_rules = self.plugin._make_firewall_dict_with_rules( ctx, fw_id) self.assertEqual(1, len(fw_rules['firewall_rule_list'])) self.assertEqual(fr_id, fw_rules['firewall_rule_list'][0]['id']) def test_insert_rule_notif(self): ctx = context.get_admin_context() with self.firewall_rule() as fwr: fr_id = fwr['firewall_rule']['id'] rule_info = {'firewall_rule_id': fr_id} with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall(firewall_policy_id=fwp_id): self.plugin.insert_rule(ctx, fwp_id, rule_info) notifications = fake_notifier.NOTIFICATIONS expected_event_type = 'firewall_policy.update.insert_rule' event_types = [event['event_type'] for event in notifications] self.assertIn(expected_event_type, event_types) def test_remove_rule(self): ctx = context.get_admin_context() with self.firewall_rule() as fwr: fr_id = fwr['firewall_rule']['id'] rule_info = {'firewall_rule_id': fr_id} with self.firewall_policy(firewall_rules=[fr_id]) as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall(firewall_policy_id=fwp_id) as fw: fw_id = fw['firewall']['id'] self.plugin.remove_rule(ctx, fwp_id, rule_info) fw_rules = self.plugin._make_firewall_dict_with_rules( ctx, fw_id) self.assertEqual([], fw_rules['firewall_rule_list']) def test_remove_rule_notif(self): ctx = context.get_admin_context() with self.firewall_rule() as fwr: fr_id = fwr['firewall_rule']['id'] rule_info = {'firewall_rule_id': fr_id} with self.firewall_policy(firewall_rules=[fr_id]) as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall(firewall_policy_id=fwp_id): self.plugin.remove_rule(ctx, fwp_id, rule_info) notifications = fake_notifier.NOTIFICATIONS expected_event_type = 'firewall_policy.update.remove_rule' event_types = [event['event_type'] for event in notifications] self.assertIn(expected_event_type, event_types) def test_firewall_quota_lower(self): """Test quota using overridden value.""" cfg.CONF.set_override('quota_firewall', 3, group='QUOTAS') with self.firewall(name='quota1'), \ self.firewall(name='quota2'), \ self.firewall(name='quota3'): data = {'firewall': {'name': 'quota4', 'firewall_policy_id': None, 'tenant_id': self._tenant_id, 'shared': False}} req = self.new_create_request('firewalls', data, 'json') res = req.get_response(self.ext_api) self.assertIn('Quota exceeded', res.body.decode('utf-8')) self.assertEqual(exc.HTTPConflict.code, res.status_int) def test_firewall_quota_default(self): """Test quota using default value.""" with self.firewall(name='quota1'), \ self.firewall(name='quota2'), \ self.firewall(name='quota3'), \ self.firewall(name='quota4'), \ self.firewall(name='quota5'), \ self.firewall(name='quota6'), \ self.firewall(name='quota7'), \ self.firewall(name='quota8'), \ self.firewall(name='quota9'), \ self.firewall(name='quota10'): data = {'firewall': {'name': 'quota11', 'firewall_policy_id': None, 'tenant_id': self._tenant_id, 'shared': False}} req = self.new_create_request('firewalls', data, 'json') res = req.get_response(self.ext_api) self.assertIn('Quota exceeded', res.body.decode('utf-8')) self.assertEqual(exc.HTTPConflict.code, res.status_int) class TestFirewallRouterPluginBase(test_db_firewall.FirewallPluginDbTestCase, test_l3_plugin.L3NatTestCaseMixin, test_agent.AgentDBTestMixIn): def setUp(self, core_plugin=None, fw_plugin=None, ext_mgr=None): self.agentapi_del_fw_p = mock.patch(test_db_firewall.DELETEFW_PATH, create=True, new=test_db_firewall.FakeAgentApi().delete_firewall) self.agentapi_del_fw_p.start() self.client_mock = mock.MagicMock(name="mocked client") mock.patch('neutron.common.rpc.get_client' ).start().return_value = self.client_mock # the L3 routing with L3 agent scheduling service plugin l3_plugin = ('neutron.tests.unit.extensions.test_l3.' 'TestL3NatAgentSchedulingServicePlugin') cfg.CONF.set_override('api_extensions_path', extensions_path) if not fw_plugin: fw_plugin = FW_PLUGIN_KLASS service_plugins = {'l3_plugin_name': l3_plugin, 'fw_plugin_name': fw_plugin} fdb.Firewall_db_mixin.\ supported_extension_aliases = ["fwaas", "fwaasrouterinsertion"] fdb.Firewall_db_mixin.path_prefix = fwaas_def.API_PREFIX super(test_db_firewall.FirewallPluginDbTestCase, self).setUp( ext_mgr=ext_mgr, service_plugins=service_plugins ) if not ext_mgr: ext_mgr = FirewallTestExtensionManager() app = config.load_paste_app('extensions_test_app') self.ext_api = api_ext.ExtensionMiddleware(app, ext_mgr=ext_mgr) self.l3_plugin = directory.get_plugin(plugin_constants.L3) self.plugin = directory.get_plugin('FIREWALL') def test_get_firewall_tenant_ids_on_host_with_associated_router(self): agent = helpers.register_l3_agent("host1") tenant_id = uuidutils.generate_uuid() ctxt = context.get_admin_context() with self.router(name='router1', admin_state_up=True, tenant_id=tenant_id) as router1: router_id = router1['router']['id'] self.l3_plugin.add_router_to_l3_agent(ctxt, agent.id, router_id) with self.firewall(tenant_id=tenant_id, router_ids=[router_id]): tenant_ids = self.plugin.get_firewall_tenant_ids_on_host( ctxt, 'host1') self.assertEqual([tenant_id], tenant_ids) def test_get_firewall_tenant_ids_on_host_without_associated_router(self): agent1 = helpers.register_l3_agent("host1") helpers.register_l3_agent("host2") tenant_id = uuidutils.generate_uuid() ctxt = context.get_admin_context() with self.router(name='router1', admin_state_up=True, tenant_id=tenant_id) as router1: router_id = router1['router']['id'] self.l3_plugin.add_router_to_l3_agent(ctxt, agent1.id, router_id) with self.firewall(tenant_id=tenant_id, router_ids=[router_id]): tenant_ids = self.plugin.get_firewall_tenant_ids_on_host( ctxt, 'host_2') self.assertEqual([], tenant_ids) def test_get_firewall_tenant_ids_on_host_with_routers(self): agent1 = helpers.register_l3_agent("host1") tenant_id1 = uuidutils.generate_uuid() tenant_id2 = uuidutils.generate_uuid() ctxt = context.get_admin_context() with self.router(name='router1', admin_state_up=True, tenant_id=tenant_id1) as router1: with self.router(name='router2', admin_state_up=True, tenant_id=tenant_id2) as router2: router_id1 = router1['router']['id'] router_id2 = router2['router']['id'] self.l3_plugin.add_router_to_l3_agent(ctxt, agent1.id, router_id1) self.l3_plugin.add_router_to_l3_agent(ctxt, agent1.id, router_id2) with self.firewall(tenant_id=tenant_id1, router_ids=[router_id1]): with self.firewall(tenant_id=tenant_id2, router_ids=[router_id2]): tenant_ids = (self.plugin .get_firewall_tenant_ids_on_host( ctxt, 'host1')) self.assertItemsEqual([tenant_id1, tenant_id2], tenant_ids) neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/agents/0000775000175000017500000000000013555600005026313 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/agents/test_firewall_agent_api.py0000664000175000017500000000606313555577713033570 0ustar zuulzuul00000000000000# 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. import mock from neutron_fwaas.services.firewall.agents import firewall_agent_api as api from neutron_fwaas.services.firewall.drivers import fwaas_base from neutron_fwaas.services.firewall.drivers import fwaas_base_v2 from neutron_fwaas.tests import base class NoopFwaasDriver(fwaas_base.FwaasDriverBase): """Noop Fwaas Driver. v1 firewall driver which does nothing. This driver is for disabling Fwaas functionality. """ def create_firewall_group(self, agent_mode, apply_list, firewall): pass def delete_firewall_group(self, agent_mode, apply_list, firewall): pass def update_firewall_group(self, agent_mode, apply_list, firewall): pass def apply_default_policy(self, agent_mode, apply_list, firewall): pass class NoopFwaasDriverV2(fwaas_base_v2.FwaasDriverBase): """Noop Fwaas Driver. v2 firewall driver which does nothing. This driver is for disabling Fwaas functionality. """ def create_firewall_group(self, agent_mode, apply_list, firewall): pass def delete_firewall_group(self, agent_mode, apply_list, firewall): pass def update_firewall_group(self, agent_mode, apply_list, firewall): pass def apply_default_policy(self, agent_mode, apply_list, firewall): pass class TestFWaaSAgentApi(base.BaseTestCase): def setUp(self): super(TestFWaaSAgentApi, self).setUp() self.api = api.FWaaSPluginApiMixin( 'topic', 'host') def test_init(self): self.assertEqual('host', self.api.host) def _test_firewall_method(self, method_name, **kwargs): with mock.patch.object(self.api.client, 'call') as rpc_mock, \ mock.patch.object(self.api.client, 'prepare') as prepare_mock: prepare_mock.return_value = self.api.client getattr(self.api, method_name)(mock.sentinel.context, 'test', **kwargs) prepare_args = {} prepare_mock.assert_called_once_with(**prepare_args) rpc_mock.assert_called_once_with(mock.sentinel.context, method_name, firewall_id='test', host='host', **kwargs) def test_set_firewall_status(self): self._test_firewall_method('set_firewall_status', status='fake_status') def test_firewall_deleted(self): self._test_firewall_method('firewall_deleted') neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/agents/l3reference/0000775000175000017500000000000013555600005030510 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000016000000000000011212 Lustar 00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/agents/l3reference/test_firewall_l3_agent_v2.pyneutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/agents/l3reference/test_firewall_l3_0000664000175000017500000004710513555577713034066 0ustar zuulzuul00000000000000# 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 mock from neutron.agent.l3 import l3_agent_extension_api as l3_agent_api from neutron.agent.l3 import router_info from neutron.agent.linux import ip_lib from neutron.conf.agent.l3 import config as l3_config from neutron_lib import context from oslo_config import cfg from oslo_utils import uuidutils from neutron_fwaas.common import fwaas_constants from neutron_fwaas.services.firewall.agents import firewall_agent_api from neutron_fwaas.services.firewall.agents.l3reference \ import firewall_l3_agent_v2 from neutron_fwaas.services.firewall.drivers.linux \ import iptables_fwaas_v2 from neutron_fwaas.tests import base from neutron_fwaas.tests.unit.services.firewall.agents \ import test_firewall_agent_api class FWaasHelper(object): def __init__(self): pass class FWaasAgent(firewall_l3_agent_v2.L3WithFWaaS, FWaasHelper): neutron_service_plugins = [] def add_router(self, context, data): pass def delete_router(self, context, data): pass def update_router(self, context, data): pass def _setup_test_agent_class(service_plugins): class FWaasTestAgent(firewall_l3_agent_v2.L3WithFWaaS, FWaasHelper): neutron_service_plugins = service_plugins def __init__(self, conf): self.event_observers = mock.Mock() self.conf = conf firewall_agent_api._check_required_agent_extension = mock.Mock() super(FWaasTestAgent, self).__init__(conf) def delete_router(self, context, data): pass def update_router(self, context, data): pass return FWaasTestAgent class TestFWaaSL3AgentExtension(base.BaseTestCase): def setUp(self): super(TestFWaaSL3AgentExtension, self).setUp() self.conf = cfg.ConfigOpts() self.conf.register_opts(l3_config.OPTS) self.conf.register_opts(firewall_agent_api.FWaaSOpts, 'fwaas') self.conf.host = 'myhost' self.api = FWaasAgent(self.conf) self.api.agent_api = mock.Mock() self.api.fwaas_driver = test_firewall_agent_api.NoopFwaasDriverV2() self.adminContext = context.get_admin_context() self.context = mock.sentinel.context self.router_id = uuidutils.generate_uuid() self.agent_conf = mock.Mock() self.ri_kwargs = {'router': {'id': self.router_id, 'project_id': uuidutils.generate_uuid()}, 'agent_conf': self.agent_conf, 'interface_driver': mock.ANY, 'use_ipv6': mock.ANY } def test_fw_config_match(self): test_agent_class = _setup_test_agent_class([fwaas_constants.FIREWALL]) cfg.CONF.set_override('enabled', True, 'fwaas') with mock.patch('oslo_utils.importutils.import_object'): test_agent_class(cfg.CONF) def test_fw_config_mismatch_plugin_enabled_agent_disabled(self): self.skipTest('this is broken') test_agent_class = _setup_test_agent_class([fwaas_constants.FIREWALL]) cfg.CONF.set_override('enabled', False, 'fwaas') self.assertRaises(SystemExit, test_agent_class, cfg.CONF) def test_fw_plugin_list_unavailable(self): test_agent_class = _setup_test_agent_class(None) cfg.CONF.set_override('enabled', False, 'fwaas') with mock.patch('oslo_utils.importutils.import_object'): test_agent_class(cfg.CONF) def test_create_firewall_group(self): firewall_group = {'id': 0, 'project_id': 1, 'admin_state_up': True, 'add-port-ids': [1, 2]} self.api.plugin_rpc = mock.Mock() with mock.patch.object(self.api, '_get_firewall_group_ports' ) as mock_get_firewall_group_ports, \ mock.patch.object(self.api, '_get_in_ns_ports' ) as mock_get_in_ns_ports, \ mock.patch.object(self.api.fwaas_driver, 'create_firewall_group' ) as mock_driver_create_firewall_group, \ mock.patch.object(self.api.fwplugin_rpc, 'set_firewall_group_status' ) as mock_set_firewall_group_status: mock_driver_create_firewall_group.return_value = True self.api.create_firewall_group(self.context, firewall_group, host='host') mock_get_firewall_group_ports.assert_called_once_with(self.context, firewall_group) mock_get_in_ns_ports.assert_called assert mock_get_in_ns_ports mock_set_firewall_group_status.assert_called_once_with( self.context, firewall_group['id'], 'ACTIVE') def test_update_firewall_group_with_ports_added_and_deleted(self): firewall_group = {'id': 0, 'project_id': 1, 'admin_state_up': True, 'ports': [1, 2, 3, 4], 'add-port-ids': [1, 2], 'del-port-ids': [3, 4], 'router_ids': [], 'last-port': False} self.api.plugin_rpc = mock.Mock() with mock.patch.object(self.api, '_get_firewall_group_ports' ) as mock_get_firewall_group_ports, \ mock.patch.object(self.api, '_get_in_ns_ports' ) as mock_get_in_ns_ports, \ mock.patch.object(self.api.fwaas_driver, 'update_firewall_group' ) as mock_driver_update_firewall_group, \ mock.patch.object(self.api.fwaas_driver, 'delete_firewall_group' ) as mock_driver_delete_firewall_group, \ mock.patch.object(self.api.fwplugin_rpc, 'set_firewall_group_status' ) as mock_set_firewall_group_status: mock_driver_delete_firewall_group.return_value = True mock_driver_update_firewall_group.return_value = True calls = [mock.call(self.context, firewall_group, to_delete=True, require_new_plugin=True), mock.call(self.context, firewall_group)] self.api.update_firewall_group(self.context, firewall_group, host='host') self.assertEqual(mock_get_firewall_group_ports.call_args_list, calls) mock_get_in_ns_ports.assert_called mock_set_firewall_group_status.assert_called_once_with( self.context, firewall_group['id'], 'ACTIVE') def test_update_firewall_group_with_ports_added_and_admin_state_down(self): firewall_group = {'id': 0, 'project_id': 1, 'admin_state_up': False, 'ports': [1, 2], 'add-port-ids': [1, 2], 'del-port-ids': [], 'router_ids': [], 'last-port': False} self.api.plugin_rpc = mock.Mock() with mock.patch.object(self.api, '_get_firewall_group_ports' ) as mock_get_firewall_group_ports, \ mock.patch.object(self.api, '_get_in_ns_ports' ) as mock_get_in_ns_ports, \ mock.patch.object(self.api.fwaas_driver, 'update_firewall_group' ) as mock_driver_update_firewall_group, \ mock.patch.object(self.api.fwplugin_rpc, 'set_firewall_group_status' ) as mock_set_firewall_group_status: mock_driver_update_firewall_group.return_value = True self.api.update_firewall_group(self.context, firewall_group, host='host') mock_get_firewall_group_ports.assert_called mock_get_in_ns_ports.assert_called mock_set_firewall_group_status.assert_called_once_with( self.context, firewall_group['id'], 'DOWN') def test_update_firewall_group_with_all_ports_deleted(self): firewall_group = {'id': 0, 'project_id': 1, 'admin_state_up': True, 'ports': [3, 4], 'add-port-ids': [], 'del-port-ids': [3, 4], 'last-port': True} self.api.plugin_rpc = mock.Mock() with mock.patch.object(self.api, '_get_firewall_group_ports' ) as mock_get_firewall_group_ports, \ mock.patch.object(self.api, '_get_in_ns_ports' ) as mock_get_in_ns_ports, \ mock.patch.object(self.api.fwaas_driver, 'delete_firewall_group' ) as mock_driver_delete_firewall_group, \ mock.patch.object(self.api.fwplugin_rpc, 'set_firewall_group_status' ) as mock_set_firewall_group_status: mock_driver_delete_firewall_group.return_value = True self.api.update_firewall_group(self.context, firewall_group, host='host') calls = [ mock.call._get_firewall_group_ports( self.context, firewall_group, require_new_plugin=True, to_delete=True), mock.call._get_firewall_group_ports( self.context, firewall_group) ] mock_get_firewall_group_ports.assert_has_calls(calls) mock_get_in_ns_ports.assert_called mock_set_firewall_group_status.assert_called_once_with( self.context, firewall_group['id'], 'INACTIVE') def test_update_firewall_group_with_no_ports_added_or_deleted(self): firewall_group = {'id': 0, 'project_id': 1, 'admin_state_up': True, 'ports': [], 'add-port-ids': [], 'del-port-ids': [], 'router_ids': []} self.api.plugin_rpc = mock.Mock() with mock.patch.object(self.api.fwaas_driver, 'update_firewall_group' ) as mock_driver_update_firewall_group, \ mock.patch.object(self.api.fwplugin_rpc, 'set_firewall_group_status' ) as mock_set_firewall_group_status: mock_driver_update_firewall_group.return_value = True self.api.update_firewall_group(self.context, firewall_group, host='host') mock_set_firewall_group_status.assert_called_once_with( self.context, firewall_group['id'], 'INACTIVE') def test_update_firewall_group_with_only_ports_added(self): # This test is for bug/1634114 firewall_group = {'id': 0, 'project_id': 1, 'admin_state_up': True, 'ports': [1, 2], 'add-port-ids': ['1', '2'], 'del-port-ids': [], 'last-port': False } self.api.update_firewall_group(self.context, firewall_group, host='host') def test_delete_firewall_group(self): firewall_group = {'id': 0, 'project_id': 1, 'admin_state_up': True, 'ports': [3, 4], 'add-port-ids': [], 'del-port-ids': [3, 4], 'last-port': False} self.api.plugin_rpc = mock.Mock() with mock.patch.object(self.api, '_get_firewall_group_ports' ) as mock_get_firewall_group_ports, \ mock.patch.object(self.api, '_get_in_ns_ports' ) as mock_get_in_ns_ports, \ mock.patch.object(self.api.fwaas_driver, 'delete_firewall_group' ) as mock_driver_delete_firewall_group, \ mock.patch.object(self.api.fwplugin_rpc, 'firewall_group_deleted' ) as mock_firewall_group_deleted: mock_driver_delete_firewall_group.return_value = True self.api.delete_firewall_group(self.context, firewall_group, host='host') mock_get_firewall_group_ports.assert_called_once_with( self.context, firewall_group, to_delete=True) mock_get_in_ns_ports.assert_called mock_firewall_group_deleted.assert_called_once_with(self.context, firewall_group['id']) def _prepare_router_data(self): return router_info.RouterInfo(self.api, self.router_id, **self.ri_kwargs) def test_get_in_ns_ports_for_non_ns_fw(self): port_ids = [1, 2] ports = [{'id': pid} for pid in port_ids] ri = self._prepare_router_data() ri.internal_ports = ports router_info = {ri.router_id: ri} api_object = l3_agent_api.L3AgentExtensionAPI(router_info) self.api.consume_api(api_object) fw_port_ids = port_ids with mock.patch.object(ip_lib, 'list_network_namespaces') as mock_list_netns: mock_list_netns.return_value = [] ports_for_fw_list = self.api._get_in_ns_ports(fw_port_ids) mock_list_netns.assert_called_with() self.assertFalse(ports_for_fw_list) def test_get_in_ns_ports_for_fw(self): port_ids = [1, 2] ports = [{'id': pid} for pid in port_ids] ri = self._prepare_router_data() ri.internal_ports = ports router_info = {} router_info[ri.router_id] = ri api_object = l3_agent_api.L3AgentExtensionAPI(router_info) self.api.consume_api(api_object) fw_port_ids = port_ids ports_for_fw_expected = [(ri, port_ids)] with mock.patch.object(ip_lib, 'list_network_namespaces') as mock_list_netns: mock_list_netns.return_value = [ri.ns_name] ports_for_fw_actual = self.api._get_in_ns_ports(fw_port_ids) self.assertEqual(ports_for_fw_expected, ports_for_fw_actual) def test_add_router_for_check_input(self): fw_agent = _setup_test_agent_class([fwaas_constants.FIREWALL]) cfg.CONF.set_override('enabled', True, 'fwaas') updated_router = { '_interfaces': [{ 'device_owner': 'network: router_interface', 'id': '1', 'tenant_id': 'demo_tenant_id', }], 'tenant_id': 'demo_tenant_id', 'id': '0b109a4e-d228-479d-ad43-08bf3245adbb', 'name': 'demo_router' } fwg = { 'status': 'ACTIVE', 'admin_state_up': True, 'tenant_id': 'demo_tenant_id', 'ports': [1], 'del-port-ids': [], 'add-port-ids': ['1'], 'id': '2932b3d9-3a7b-48a1-a16c-bf9f7b2751a5' } with mock.patch('oslo_utils.importutils.import_object'): agent = fw_agent(cfg.CONF) agent.agent_api = mock.Mock() agent.fwplugin_rpc = mock.Mock() agent.conf.agent_mode = mock.Mock() agent.fwaas_driver = iptables_fwaas_v2.IptablesFwaasDriver() with mock.patch.object(agent.fwplugin_rpc, 'get_firewall_groups_for_project' ) as mock_get_firewall_groups_for_project, \ mock.patch.object(agent.agent_api, 'get_router_hosting_port' ) as mock_get_router_hosting_port, \ mock.patch.object(agent.fwaas_driver, '_get_ipt_mgrs_with_if_prefix' ) as mock_get_ipt_mgrs_with_if_prefix: mock_get_firewall_groups_for_project.return_value = [fwg] mock_get_router_hosting_port.return_value = mock.Mock() agent.add_router(self.context, updated_router) mock_get_ipt_mgrs_with_if_prefix.assert_any_call( agent.conf.agent_mode, mock.ANY) @mock.patch('oslo_utils.importutils.import_object') def test_add_router_with_several_ports(self, mock_import_object): fw_agent = _setup_test_agent_class([fwaas_constants.FIREWALL]) cfg.CONF.set_override('enabled', True, 'fwaas') updated_router = { '_interfaces': [ {'device_owner': 'network: router_interface', 'id': '1', 'tenant_id': 'demo_tenant_id'}, {'device_owner': 'network: router_interface', 'id': '2', 'tenant_id': 'demo_tenant_id'}, {'device_owner': 'network: router_interface', 'id': '3', 'tenant_id': 'demo_tenant_id'}], 'tenant_id': 'demo_tenant_id', 'id': '0b109a4e-d228-479d-ad43-08bf3245adbb', 'name': 'demo_router' } fwg1 = { 'status': 'ACTIVE', 'admin_state_up': True, 'tenant_id': 'demo_tenant_id', 'del-port-ids': [], 'add-port-ids': ['1', '3'], 'id': '2932b3d9-3a7b-48a1-a16c-bf9f7b2751a5' } fwg2 = { 'status': 'ACTIVE', 'admin_state_up': True, 'tenant_id': 'demo_tenant_id', 'del-port-ids': [], 'add-port-ids': ['2', '3'], 'id': '2932b3d9-3a7b-48a1-a16c-bf9f7b2751a5' } agent = fw_agent(cfg.CONF) agent.agent_api = mock.Mock() agent.fwplugin_rpc = mock.Mock() agent.conf.agent_mode = mock.Mock() agent.fwaas_driver = iptables_fwaas_v2.IptablesFwaasDriver() patch_project = mock.patch.object( agent.fwplugin_rpc, 'get_firewall_groups_for_project') patch_invoke = mock.patch.object( agent, '_invoke_driver_for_sync_from_plugin') with patch_project as mock_get_firewall_groups, \ patch_invoke as mock_invoke_driver: mock_get_firewall_groups.return_value = [fwg1, fwg2] agent.add_router(self.context, updated_router) # Check that mock_invoke_driver was called exactly twice with # correct arguments. self.assertEqual([ mock.call(mock.ANY, {'1', '3'}, fwg1), mock.call(mock.ANY, {'2'}, fwg2), ], mock_invoke_driver.call_args_list) #TODO(Margaret) Add test for add_router method. neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/agents/l3reference/__init__.py0000664000175000017500000000000013555577713032632 0ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000015500000000000011216 Lustar 00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/agents/l3reference/test_firewall_l3_agent.pyneutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/agents/l3reference/test_firewall_l3_0000664000175000017500000003635113555577713034067 0ustar zuulzuul00000000000000# 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. import mock from neutron.agent.l3 import l3_agent_extension_api as l3_agent_api from neutron.agent.l3 import router_info from neutron.agent.linux import ip_lib from neutron.conf.agent.l3 import config as l3_config from neutron.conf import common as base_config from neutron_lib import context from oslo_config import cfg from oslo_utils import uuidutils import testtools from neutron_fwaas.common import fwaas_constants from neutron_fwaas.services.firewall.agents import firewall_agent_api from neutron_fwaas.services.firewall.agents.l3reference \ import firewall_l3_agent from neutron_fwaas.tests import base from neutron_fwaas.tests.unit.services.firewall.agents \ import test_firewall_agent_api class FWaasHelper(object): def __init__(self, host): pass class FWaasAgent(firewall_l3_agent.FWaaSL3AgentExtension, FWaasHelper): neutron_service_plugins = [] def _setup_test_agent_class(service_plugins): class FWaasTestAgent(firewall_l3_agent.FWaaSL3AgentExtension, FWaasHelper): neutron_service_plugins = service_plugins def __init__(self, conf): self.event_observers = mock.Mock() self.conf = conf super(FWaasTestAgent, self).__init__("myhost", conf) return FWaasTestAgent class TestFwaasL3AgentRpcCallback(base.BaseTestCase): def setUp(self): super(TestFwaasL3AgentRpcCallback, self).setUp() self.conf = cfg.ConfigOpts() self.conf.register_opts(base_config.core_opts) self.conf.register_opts(l3_config.OPTS) self.conf.register_opts(firewall_agent_api.FWaaSOpts, 'fwaas') self.api = FWaasAgent(host=None, conf=self.conf) self.api.fwaas_driver = test_firewall_agent_api.NoopFwaasDriver() self.adminContext = context.get_admin_context() self.router_id = uuidutils.generate_uuid() self.agent_conf = mock.Mock() # For 'tenant_id' and 'project_id' keys project_id = uuidutils.generate_uuid() self.ri_kwargs = {'router': {'id': self.router_id, 'tenant_id': project_id, 'project_id': project_id}, 'agent_conf': self.agent_conf, 'interface_driver': mock.ANY, 'use_ipv6': mock.ANY, } def test_fw_config_match(self): test_agent_class = _setup_test_agent_class([fwaas_constants.FIREWALL]) cfg.CONF.set_override('enabled', True, 'fwaas') with mock.patch('oslo_utils.importutils.import_object'): test_agent_class(cfg.CONF) @testtools.skip('needs to be refactored for fwaas v2') def test_fw_config_mismatch_plugin_enabled_agent_disabled(self): test_agent_class = _setup_test_agent_class([fwaas_constants.FIREWALL]) cfg.CONF.set_override('enabled', False, 'fwaas') self.assertRaises(SystemExit, test_agent_class, cfg.CONF) def test_fw_plugin_list_unavailable(self): test_agent_class = _setup_test_agent_class(None) cfg.CONF.set_override('enabled', False, 'fwaas') with mock.patch('oslo_utils.importutils.import_object'): test_agent_class(cfg.CONF) def test_create_firewall(self): fake_firewall = {'id': 0, 'tenant_id': 1, 'admin_state_up': True, 'add-router-ids': [1, 2]} self.api.plugin_rpc = mock.Mock() with mock.patch.object(self.api, '_get_router_info_list_for_tenant' ) as mock_get_router_info_list_for_tenant, \ mock.patch.object(self.api.fwaas_driver, 'create_firewall' ) as mock_driver_create_firewall, \ mock.patch.object(self.api.fwplugin_rpc, 'set_firewall_status' ) as mock_set_firewall_status: mock_driver_create_firewall.return_value = True self.api.create_firewall( context=mock.sentinel.context, firewall=fake_firewall, host='host') mock_get_router_info_list_for_tenant.assert_called_once_with( fake_firewall['add-router-ids'], fake_firewall['tenant_id']) mock_set_firewall_status.assert_called_once_with( mock.sentinel.context, fake_firewall['id'], 'ACTIVE') def test_update_firewall_with_routers_added_and_deleted(self): fake_firewall = {'id': 0, 'tenant_id': 1, 'admin_state_up': True, 'add-router-ids': [1, 2], 'del-router-ids': [3, 4], 'router_ids': [], 'last-router': False} self.api.plugin_rpc = mock.Mock() with mock.patch.object(self.api, '_get_router_info_list_for_tenant' ) as mock_get_router_info_list_for_tenant, \ mock.patch.object(self.api.fwaas_driver, 'update_firewall' ) as mock_driver_delete_firewall, \ mock.patch.object(self.api.fwaas_driver, 'delete_firewall' ) as mock_driver_update_firewall, \ mock.patch.object(self.api.fwplugin_rpc, 'set_firewall_status' ) as mock_set_firewall_status: mock_driver_delete_firewall.return_value = True mock_driver_update_firewall.return_value = True calls = [mock.call(fake_firewall['del-router-ids'], fake_firewall['tenant_id']), mock.call(fake_firewall['add-router-ids'], fake_firewall['tenant_id'])] self.api.update_firewall( context=mock.sentinel.context, firewall=fake_firewall, host='host') self.assertEqual( mock_get_router_info_list_for_tenant.call_args_list, calls) mock_set_firewall_status.assert_called_once_with( mock.sentinel.context, fake_firewall['id'], 'ACTIVE') def test_update_firewall_with_routers_added_and_admin_state_down(self): fake_firewall = {'id': 0, 'tenant_id': 1, 'admin_state_up': False, 'add-router-ids': [1, 2], 'del-router-ids': [], 'router_ids': [], 'last-router': False} self.api.plugin_rpc = mock.Mock() with mock.patch.object(self.api, '_get_router_info_list_for_tenant' ) as mock_get_router_info_list_for_tenant, \ mock.patch.object(self.api.fwaas_driver, 'update_firewall' ) as mock_driver_update_firewall, \ mock.patch.object(self.api.fwplugin_rpc, 'set_firewall_status' ) as mock_set_firewall_status: mock_driver_update_firewall.return_value = True self.api.update_firewall( context=mock.sentinel.context, firewall=fake_firewall, host='host') mock_get_router_info_list_for_tenant.assert_called_once_with( fake_firewall['add-router-ids'], fake_firewall['tenant_id']) mock_set_firewall_status.assert_called_once_with( mock.sentinel.context, fake_firewall['id'], 'DOWN') def test_update_firewall_with_all_routers_deleted(self): fake_firewall = {'id': 0, 'tenant_id': 1, 'admin_state_up': True, 'add-router-ids': [], 'del-router-ids': [3, 4], 'last-router': True} self.api.plugin_rpc = mock.Mock() with mock.patch.object(self.api, '_get_router_info_list_for_tenant' ) as mock_get_router_info_list_for_tenant, \ mock.patch.object(self.api.fwaas_driver, 'delete_firewall' ) as mock_driver_delete_firewall, \ mock.patch.object(self.api.fwplugin_rpc, 'set_firewall_status' ) as mock_set_firewall_status: mock_driver_delete_firewall.return_value = True self.api.update_firewall( context=mock.sentinel.context, firewall=fake_firewall, host='host') mock_get_router_info_list_for_tenant.assert_called_once_with( fake_firewall['del-router-ids'], fake_firewall['tenant_id']) mock_set_firewall_status.assert_called_once_with( mock.sentinel.context, fake_firewall['id'], 'INACTIVE') def test_update_firewall_with_rtrs_and_no_rtrs_added_nor_deleted(self): fake_firewall = {'id': 0, 'tenant_id': 1, 'admin_state_up': True, 'add-router-ids': [], 'del-router-ids': [], 'router_ids': [1, 2]} self.api.plugin_rpc = mock.Mock() with mock.patch.object(self.api.fwaas_driver, 'update_firewall' ) as mock_driver_update_firewall, \ mock.patch.object(self.api, '_get_router_info_list_for_tenant' ) as mock_get_router_info_list_for_tenant, \ mock.patch.object(self.api.fwplugin_rpc, 'set_firewall_status' ) as mock_set_firewall_status: mock_driver_update_firewall.return_value = True self.api.update_firewall( context=mock.sentinel.context, firewall=fake_firewall, host='host') mock_get_router_info_list_for_tenant.assert_called_once_with( fake_firewall['router_ids'], fake_firewall['tenant_id']) mock_set_firewall_status.assert_called_once_with( mock.sentinel.context, fake_firewall['id'], 'ACTIVE') def test_update_firewall_with_no_rtrs_and_no_rtrs_added_nor_deleted(self): fake_firewall = {'id': 0, 'tenant_id': 1, 'admin_state_up': True, 'add-router-ids': [], 'del-router-ids': [], 'router_ids': []} self.api.plugin_rpc = mock.Mock() with mock.patch.object(self.api.fwaas_driver, 'update_firewall' ) as mock_driver_update_firewall, \ mock.patch.object(self.api.fwplugin_rpc, 'set_firewall_status' ) as mock_set_firewall_status: mock_driver_update_firewall.return_value = True self.api.update_firewall( context=mock.sentinel.context, firewall=fake_firewall, host='host') mock_set_firewall_status.assert_called_once_with( mock.sentinel.context, fake_firewall['id'], 'INACTIVE') def test_delete_firewall(self): fake_firewall = {'id': 0, 'tenant_id': 1, 'admin_state_up': True, 'add-router-ids': [], 'del-router-ids': [3, 4], 'last-router': True} self.api.plugin_rpc = mock.Mock() with mock.patch.object(self.api, '_get_router_info_list_for_tenant' ) as mock_get_router_info_list_for_tenant, \ mock.patch.object(self.api.fwaas_driver, 'delete_firewall' ) as mock_driver_delete_firewall, \ mock.patch.object(self.api.fwplugin_rpc, 'firewall_deleted' ) as mock_firewall_deleted: mock_driver_delete_firewall.return_value = True self.api.delete_firewall( context=mock.sentinel.context, firewall=fake_firewall, host='host') mock_get_router_info_list_for_tenant.assert_called_once_with( fake_firewall['del-router-ids'], fake_firewall['tenant_id']) mock_firewall_deleted.assert_called_once_with( mock.sentinel.context, fake_firewall['id']) def _prepare_router_data(self): return router_info.RouterInfo(self.api, self.router_id, **self.ri_kwargs) def test_get_router_info_list_for_tenant(self): ri = self._prepare_router_data() router_info = {ri.router_id: ri} self.api.router_info = router_info api_object = l3_agent_api.L3AgentExtensionAPI(router_info) self.api.consume_api(api_object) routers = [ri.router] router_ids = [router['id'] for router in routers] with mock.patch.object(ip_lib, 'list_network_namespaces') as mock_list_netns: mock_list_netns.return_value = [] router_info_list = self.api._get_router_info_list_for_tenant( router_ids, ri.router['tenant_id']) mock_list_netns.assert_called_once_with() self.assertFalse(router_info_list) def _get_router_info_list_router_without_router_info_helper(self, rtr_with_ri): # ri.router with associated router_info (ri) # rtr2 has no router_info ri = self._prepare_router_data() rtr2 = {'id': uuidutils.generate_uuid(), 'tenant_id': ri.router['tenant_id']} routers = [rtr2] router_info = {} ri_expected = [] if rtr_with_ri: router_info[ri.router_id] = ri routers.append(ri.router) ri_expected.append(ri) self.api.router_info = router_info router_ids = [router['id'] for router in routers] with mock.patch.object(ip_lib, 'list_network_namespaces') as mock_list_netns: mock_list_netns.return_value = [ri.ns_name] api_object = l3_agent_api.L3AgentExtensionAPI(router_info) self.api.consume_api(api_object) router_info_list = self.api._get_router_info_list_for_tenant( router_ids, ri.router['tenant_id']) self.assertEqual(ri_expected, router_info_list) def test_get_router_info_list_router_without_router_info(self): self._get_router_info_list_router_without_router_info_helper( rtr_with_ri=False) def test_get_router_info_list_two_routers_one_without_router_info(self): self._get_router_info_list_router_without_router_info_helper( rtr_with_ri=True) neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/agents/__init__.py0000664000175000017500000000000013555577713030435 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/agents/l2/0000775000175000017500000000000013555600005026630 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/agents/l2/test_fwaas_v2.py0000664000175000017500000007306213555577713032004 0ustar zuulzuul00000000000000# Copyright 2017 Cisco Systems # # 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 copy import mock from neutron_lib import constants as nl_consts from neutron_lib import context from neutron_lib.exceptions import firewall_v2 as f_exc from oslo_config import cfg from neutron_fwaas.common import fwaas_constants as consts from neutron_fwaas.services.firewall.agents.l2 import fwaas_v2 from neutron_fwaas.tests import base from neutron_fwaas.tests.unit.services.firewall.agents.l2 import fake_data class TestFWaasV2AgentExtensionBase(base.BaseTestCase): def setUp(self): super(TestFWaasV2AgentExtensionBase, self).setUp() self.fake = fake_data.FakeFWaaSL2Agent() self.port = self.fake.create('port') self.port_minimal = self.fake.create('port', minimal=True) self.fwg = self.fake.create('fwg') self.fwg_with_rule = self.fake.create('fwg_with_rule') self.port_id = self.port['port_id'] self.fwg_id = self.fwg['id'] self.host = fake_data.HOST self.ctx = context.get_admin_context() self.l2 = fwaas_v2.FWaaSV2AgentExtension() self.l2.consume_api(mock.Mock()) self.driver = mock.patch( 'neutron.manager.NeutronManager.load_class_for_provider').start() self.l2.initialize(None, 'ovs') self.l2.vlan_manager = mock.Mock() self.conf = cfg.ConfigOpts() self.l2.fwg_map = mock.Mock() self.l2.conf.host = self.host self.rpc = self.l2.plugin_rpc class TestFWaasV2AgentExtension(TestFWaasV2AgentExtensionBase): def setUp(self): super(TestFWaasV2AgentExtension, self).setUp() cfg.CONF.set_override('firewall_l2_driver', 'ovs', group='fwaas') def test_initialize(self): with mock.patch('neutron.common.rpc.create_connection') as conn: self.l2.initialize(None, 'ovs') self.driver.assert_called_with('neutron.agent.l2.firewall_drivers', 'ovs') conn.assert_called_with() self.l2.conn.create_consumer.assert_called_with( consts.FW_AGENT, [self.l2], fanout=False) self.l2.conn.consume_in_threads.assert_called_with() class TestHandlePort(TestFWaasV2AgentExtensionBase): def setUp(self): super(TestHandlePort, self).setUp() self.rpc.get_firewall_group_for_port = mock.Mock( return_value=self.fwg) self.l2._compute_status = mock.Mock(return_value=nl_consts.ACTIVE) self.l2._apply_fwg_rules = mock.Mock(return_value=True) self.l2._send_fwg_status = mock.Mock() self.ctx = context.get_admin_context() self.l2._add_rule_for_trusted_port = mock.Mock() def test_normal(self): self.l2.fwg_map.get_port_fwg.return_value = None self.l2.handle_port(self.ctx, self.port) self.rpc.get_firewall_group_for_port.assert_called_once_with( self.ctx, self.port['port_id']) self.l2._apply_fwg_rules.assert_called_once_with(self.fwg, [self.port]) self.l2._compute_status.assert_called_once_with( self.fwg, True, event=consts.HANDLE_PORT) self.l2.fwg_map.set_port_fwg.assert_called_once_with(self.port, self.fwg) self.l2._send_fwg_status.assert_called_once_with( self.ctx, fwg_id=self.fwg['id'], status=nl_consts.ACTIVE, host=self.l2.conf.host) def test_non_layer2_port(self): self.port['device_owner'] = 'network:router_gateway' self.l2.handle_port(self.ctx, self.port) self.rpc.get_firewall_group_for_port.assert_not_called() self.l2._apply_fwg_rules.assert_not_called() self.l2._compute_status.assert_not_called() self.l2.fwg_map.set_port_fwg.assert_not_called() self.l2._send_fwg_status.assert_not_called() def test_no_fwg_is_asossicate_to_port(self): self.l2.fwg_map.get_port_fwg.return_value = None self.rpc.get_firewall_group_for_port.return_value = None self.l2.handle_port(self.ctx, self.port) self.rpc.get_firewall_group_for_port.assert_called_once_with( self.ctx, self.port['port_id']) self.l2._apply_fwg_rules.assert_not_called() self.l2._compute_status.assert_not_called() self.l2.fwg_map.set_port_fwg.assert_not_called() self.l2._send_fwg_status.assert_not_called() def test_port_already_apply_fwg(self): self.l2.fwg_map.get_port_fwg.return_value = self.fwg self.l2.handle_port(self.ctx, self.port) self.rpc.get_firewall_group_for_port.assert_not_called() self.l2._apply_fwg_rules.assert_not_called() self.l2._compute_status.assert_not_called() self.l2.fwg_map.set_port_fwg.assert_not_called() self.l2._send_fwg_status.assert_not_called() def test_trusted_port(self): self.l2.fwg_map.get_port.return_value = None self.port['device_owner'] = 'network:foo' self.l2.handle_port(self.ctx, self.port) self.l2._add_rule_for_trusted_port.assert_called_once_with(self.port) self.l2.fwg_map.set_port.assert_called_once_with(self.port) self.rpc.get_firewall_group_for_port.assert_not_called() def test_trusted_port_registered_map(self): self.port['device_owner'] = 'network:dhcp' self.l2.fwg_map.get_port.return_value = self.port self.l2.handle_port(self.ctx, self.port) self.l2._add_rule_for_trusted_port.assert_not_called() self.l2.fwg_map.set_port.assert_not_called() self.rpc.get_firewall_group_for_port.assert_not_called() class TestDeletePort(TestFWaasV2AgentExtensionBase): def setUp(self): super(TestDeletePort, self).setUp() self.l2._compute_status = mock.Mock(return_value=nl_consts.ACTIVE) self.l2._apply_fwg_rules = mock.Mock(return_value=True) self.l2._send_fwg_status = mock.Mock() self.l2._delete_rule_for_trusted_port = mock.Mock() self.l2.fwg_map.get_port_fwg = mock.Mock(return_value=self.fwg) self.l2.fwg_map.set_fwg = mock.Mock() self.l2.fwg_map.get_port = mock.Mock(return_value=self.port) self.l2.fwg_map.remove_port = mock.Mock() def test_include_vif_port_attribute(self): self.port_minimal.update({'vif_port': None}) self.l2.fwg_map.get_port_fwg.return_value = None self.l2.delete_port(self.ctx, self.port_minimal) self.l2.fwg_map.get_port_fwg.assert_not_called() self.l2._apply_fwg_rules.assert_not_called() def test_port_belongs_to_fwg(self): expected_ports = self.fwg['ports'] self.fwg['ports'].append(self.port['port_id']) self.l2.delete_port(self.ctx, self.port_minimal) self.l2.fwg_map.get_port_fwg.assert_called_once_with(self.port) self.l2._apply_fwg_rules.assert_called_once_with( self.fwg, [self.port], event=consts.DELETE_FWG) # 'port_id' has been removed from 'ports' self.assertEqual(expected_ports, self.fwg['ports']) self.l2.fwg_map.set_fwg.assert_called_once_with(self.fwg) def test_port_belongs_to_no_fwg(self): expected_ports = self.fwg['ports'] self.l2.delete_port(self.ctx, self.port_minimal) self.l2.fwg_map.get_port_fwg.assert_called_once_with(self.port) self.l2._apply_fwg_rules.assert_called_once_with( self.fwg, [self.port], event=consts.DELETE_FWG) # 'ports' not changed during delete_port() self.assertEqual(expected_ports, self.fwg['ports']) self.l2.fwg_map.set_fwg.assert_called_once_with(self.fwg) def test_non_layer2_port(self): self.port['device_owner'] = 'network:router_gateway' self.l2.delete_port(self.ctx, self.port_minimal) self.l2.fwg_map.get_port_fwg.assert_not_called() def test_cannot_get_fwg_from_port(self): self.l2.fwg_map.get_port_fwg.return_value = None self.l2.delete_port(self.ctx, self.port_minimal) self.l2.fwg_map.get_port_fwg.assert_called_once_with(self.port) self.l2._apply_fwg_rules.assert_not_called() def test_trusted_port_with_map(self): self.port['device_owner'] = 'network:dhcp' self.l2.fwg_map.get_port.return_value = self.port self.l2.delete_port(self.ctx, self.port_minimal) self.l2._delete_rule_for_trusted_port.assert_called_once_with( self.port) self.l2.fwg_map.remove_port.assert_called_once_with(self.port) class TestCreateFirewallGroup(TestFWaasV2AgentExtensionBase): def setUp(self): super(TestCreateFirewallGroup, self).setUp() self.l2._apply_fwg_rules = mock.Mock(return_value=True) self.l2._compute_status = mock.Mock(return_value='ACTIVE') self.l2._send_fwg_status = mock.Mock() def test_create_event_is_create(self): fwg = self.fwg_with_rule fwg['ports'] = [fake_data.PORT1] ports = [fwg['port_details'][fake_data.PORT1]] self.l2._create_firewall_group( self.ctx, fwg, self.host, event=consts.CREATE_FWG) self.l2._apply_fwg_rules.assert_called_once_with( fwg, ports, consts.CREATE_FWG) self.l2._compute_status.assert_called_once_with( fwg, True, consts.CREATE_FWG) def test_create_event_is_not_create(self): fwg = self.fwg_with_rule fwg['ports'] = [fake_data.PORT1] ports = [fwg['port_details'][fake_data.PORT1]] self.l2._create_firewall_group( self.ctx, fwg, self.host, event=consts.UPDATE_FWG) self.l2._apply_fwg_rules.assert_called_once_with( fwg, ports, consts.UPDATE_FWG) def test_create_with_port(self): fwg = self.fwg_with_rule ports = [fwg['port_details'][fake_data.PORT1]] self.l2.create_firewall_group(self.ctx, fwg, self.host) self.l2._apply_fwg_rules.assert_called_once_with( fwg, ports, consts.CREATE_FWG) for idx, args in enumerate(self.l2._compute_status.call_args_list): self.assertEqual(fwg, args[0][0]) self.assertEqual(True, args[0][1]) self.assertEqual(consts.CREATE_FWG, args[0][2]) for idx, args in enumerate(self.l2._send_fwg_status.call_args_list): self.assertEqual(self.ctx, args[0][0]) self.assertEqual(fwg['id'], args[0][1]) self.assertEqual('ACTIVE', args[0][2]) self.assertEqual(self.host, args[0][3]) def test_create_with_no_ports(self): self.fwg_with_rule['add-port-ids'] = [] self.assertIsNone(self.l2.create_firewall_group( self.ctx, self.fwg_with_rule, self.host)) self.l2._apply_fwg_rules.assert_not_called() self.l2.fwg_map.set_port_fwg.assert_not_called() self.l2._send_fwg_status.assert_called_once_with( self.ctx, self.fwg_with_rule['id'], 'INACTIVE', self.host) def test_create_with_invalid_host(self): self.fwg_with_rule['port_details'][fake_data.PORT1]['host'] = 'invalid' self.l2.create_firewall_group(self.ctx, self.fwg_with_rule, self.host) self.l2._apply_fwg_rules.assert_not_called() self.l2._send_fwg_status.assert_called_once_with( self.ctx, self.fwg_with_rule['id'], 'INACTIVE', self.host) def test_illegal_create_with_no_l2_ports(self): fwg = { 'name': 'non-default', 'id': self.fwg_id, 'ports': [], 'add-port-ids': [self.port_id], 'admin_state_up': True, 'port_details': { self.port_id: { 'device_owner': 'network:router_interface' } } } self.l2.create_firewall_group(self.ctx, fwg, self.host) self.l2._apply_fwg_rules.assert_not_called() self.l2.fwg_map.set_port_fwg.assert_not_called() self.l2._send_fwg_status.assert_called_once_with( self.ctx, fwg['id'], 'INACTIVE', self.host) class TestDeleteFirewallGroup(TestFWaasV2AgentExtensionBase): def setUp(self): super(TestDeleteFirewallGroup, self).setUp() self.l2._apply_fwg_rules = mock.Mock(return_value=True) self.l2._compute_status = mock.Mock(return_value='ACTIVE') self.l2._send_fwg_status = mock.Mock() self.rpc.firewall_group_deleted = mock.Mock() def test_delete_with_port(self): fwg = self.fwg_with_rule ports = [fwg['port_details'][fake_data.PORT2]] self.assertIsNone(self.l2.delete_firewall_group( self.ctx, self.fwg_with_rule, self.host)) self.l2._apply_fwg_rules.assert_called_once_with( fwg, ports, event=consts.DELETE_FWG) self.l2.fwg_map.remove_fwg.assert_called_once_with(fwg) for idx, args in enumerate(self.l2._compute_status.call_args_list): self.assertEqual(fwg, args[0][0]) self.assertEqual(True, args[0][2]) self.assertEqual({'event': consts.CREATE_FWG}, args[1]) for idx, args in enumerate(self.l2._send_fwg_status.call_args_list): self.assertEqual(self.ctx, args[0][0]) self.assertEqual(fwg['id'], args[0][1]) self.assertEqual('ACTIVE', args[0][2]) self.assertEqual(self.host, args[0][3]) def test_delete_with_no_ports(self): self.fwg_with_rule['del-port-ids'] = [] self.l2.delete_firewall_group(self.ctx, self.fwg_with_rule, self.host) self.l2._apply_fwg_rules.assert_not_called() def test_delete_with_no_l2_ports(self): self.fwg_with_rule['port_details'][fake_data.PORT2][ 'device_owner'] = 'network:router_interface' self.l2.delete_firewall_group(self.ctx, self.fwg_with_rule, self.host) self.l2._apply_fwg_rules.assert_not_called() def test_delete_with_exception(self): self.l2._delete_firewall_group = mock.Mock(side_effect=Exception) self.assertIsNone(self.l2.delete_firewall_group( self.ctx, self.fwg_with_rule, self.host)) def test_delete_event_is_update(self): self.l2._delete_firewall_group( self.ctx, self.fwg_with_rule, self.host, event=consts.UPDATE_FWG) self.l2.fwg_map.remove_fwg.assert_not_called() self.rpc.firewall_group_deleted.assert_not_called() self.l2._compute_status.assert_called_once_with( self.fwg_with_rule, True, consts.UPDATE_FWG) self.l2._send_fwg_status.assert_called_once_with( self.ctx, self.fwg_with_rule['id'], 'ACTIVE', self.host) class TestUpdateFirewallGroup(TestFWaasV2AgentExtensionBase): def setUp(self): super(TestUpdateFirewallGroup, self).setUp() self.l2._delete_firewall_group = mock.Mock() self.l2._create_firewall_group = mock.Mock() self.l2._send_fwg_status = mock.Mock() def test_update(self): self.assertIsNone(self.l2.update_firewall_group( self.ctx, mock.ANY, self.host)) self.l2._delete_firewall_group.assert_called_once_with( self.ctx, mock.ANY, self.host, event=consts.UPDATE_FWG) self.l2._create_firewall_group.assert_called_once_with( self.ctx, mock.ANY, self.host, event=consts.UPDATE_FWG) def test_update_raised_in_delete_firewall_group(self): self.l2._delete_firewall_group.side_effect = Exception fwg = self.fwg_with_rule self.assertIsNone(self.l2.update_firewall_group( self.ctx, fwg, self.host)) self.l2._send_fwg_status.assert_called_once_with( self.ctx, fwg['id'], status='ERROR', host=self.host) def test_update_raised_in_create_firewall_group(self): self.l2._create_firewall_group.side_effect = Exception fwg = self.fwg_with_rule self.assertIsNone(self.l2.update_firewall_group( self.ctx, fwg, self.host)) self.l2._send_fwg_status.assert_called_once_with( self.ctx, fwg['id'], status='ERROR', host=self.host) class TestIsPortLayer2(TestFWaasV2AgentExtensionBase): def setUp(self): super(TestIsPortLayer2, self).setUp() def test_vm_port(self): self.assertTrue(self.l2._is_port_layer2(self.port)) def test_not_vm_port(self): for device_owner in [nl_consts.DEVICE_OWNER_ROUTER_INTF, nl_consts.DEVICE_OWNER_ROUTER_GW, nl_consts.DEVICE_OWNER_DHCP, nl_consts.DEVICE_OWNER_DVR_INTERFACE, nl_consts.DEVICE_OWNER_AGENT_GW, nl_consts.DEVICE_OWNER_ROUTER_SNAT, nl_consts.DEVICE_OWNER_LOADBALANCER, nl_consts.DEVICE_OWNER_LOADBALANCERV2, 'unknown device_owner', '']: self.port['device_owner'] = device_owner self.assertFalse(self.l2._is_port_layer2(self.port)) def test_illegal_no_device_owner(self): del self.port['device_owner'] self.assertFalse(self.l2._is_port_layer2(self.port)) class TestComputeStatus(TestFWaasV2AgentExtensionBase): def setUp(self): super(TestComputeStatus, self).setUp() self.ports = list(self.fwg_with_rule['port_details'].values()) def test_normal(self): result = True fwg = self.fwg_with_rule self.assertEqual('ACTIVE', self.l2._compute_status(fwg, result)) def test_event_is_delete(self): result = True fwg = self.fwg_with_rule self.assertIsNone(self.l2._compute_status( fwg, result, consts.DELETE_FWG)) def test_event_is_update(self): result = True fwg = self.fwg_with_rule self.assertEqual('ACTIVE', self.l2._compute_status( fwg, result, consts.UPDATE_FWG)) def test_event_is_update_and_has_last_port(self): result = True fwg = self.fake.create('fwg_with_rule', attrs={'last-port': False}) self.assertEqual('ACTIVE', self.l2._compute_status( fwg, result, consts.UPDATE_FWG)) fwg = self.fake.create('fwg_with_rule', attrs={'last-port': True}) self.assertEqual('INACTIVE', self.l2._compute_status( fwg, result, consts.UPDATE_FWG)) def test_event_is_update_and_has_no_last_port_but_has_ports(self): result = True fwg = self.fwg_with_rule self.assertEqual('ACTIVE', self.l2._compute_status( fwg, result, consts.UPDATE_FWG)) def test_event_is_update_and_has_no_last_port_and_ports(self): result = True fwg = self.fwg_with_rule fwg['ports'] = [] self.assertEqual('INACTIVE', self.l2._compute_status( fwg, result, consts.UPDATE_FWG)) def test_event_is_create(self): result = True fwg = self.fwg_with_rule self.assertEqual('ACTIVE', self.l2._compute_status( fwg, result, consts.CREATE_FWG)) def test_event_is_create_and_no_fwg_ports(self): result = True fwg = self.fwg_with_rule fwg['ports'] = [] self.assertEqual('INACTIVE', self.l2._compute_status( fwg, result, consts.CREATE_FWG)) def test_event_is_handle_port(self): result = True fwg = self.fwg_with_rule self.assertEqual('ACTIVE', self.l2._compute_status( fwg, result, consts.HANDLE_PORT)) def test_event_is_delete_port(self): result = True fwg = self.fwg_with_rule self.assertEqual('ACTIVE', self.l2._compute_status( fwg, result, consts.DELETE_PORT)) def test_event_is_delete_port_and_no_fwg_ports(self): result = True fwg = self.fwg_with_rule fwg['ports'] = [] self.assertEqual('INACTIVE', self.l2._compute_status( fwg, result, consts.DELETE_PORT)) def test_driver_result_is_false(self): result = False fwg = self.fwg_with_rule self.assertEqual('ERROR', self.l2._compute_status( fwg, result)) def test_admin_state_up_is_false(self): result = True self.fwg_with_rule['admin_state_up'] = False self.assertEqual('DOWN', self.l2._compute_status( self.fwg_with_rule, self.ports, result)) def test_active_inactive_patterns(self): result = True fwg = self.fwg_with_rule # Case1: ingress/egress_firewall_policy_id # Case2: ports --> already tested at above cases expect_and_attrs = [ ('INACTIVE', ('ingress_firewall_policy_id', 'egress_firewall_policy_id')), ('ACTIVE', ('ingress_firewall_policy_id',)), ('ACTIVE', ('egress_firewall_policy_id',)), ] for attr in expect_and_attrs: fwg = self.fake.create('fwg_with_rule') expect = attr[0] for p in attr[1]: fwg[p] = None self.assertEqual(expect, self.l2._compute_status(fwg, result)) class TestApplyFwgRules(TestFWaasV2AgentExtensionBase): def setUp(self): super(TestApplyFwgRules, self).setUp() class DummyVlan(object): def __init__(self, vlan=None): self.vlan = vlan self.l2.vlan_manager.get.return_value = DummyVlan(vlan='999') def test_event_is_create(self): fwg_ports = [self.fwg_with_rule['port_details'][fake_data.PORT1]] driver_ports = copy.deepcopy(fwg_ports) driver_ports[0].update({'lvlan': 999}) self.assertTrue(self.l2._apply_fwg_rules( self.fwg_with_rule, fwg_ports, event=consts.CREATE_FWG)) self.l2.driver.create_firewall_group.assert_called_once_with( driver_ports, self.fwg_with_rule) self.l2.driver.delete_firewall_group.assert_not_called() self.l2.driver.update_firewall_group.assert_not_called() def test_event_is_update(self): fwg_ports = [self.fwg_with_rule['port_details'][fake_data.PORT1]] driver_ports = copy.deepcopy(fwg_ports) driver_ports[0].update({'lvlan': 999}) self.assertTrue(self.l2._apply_fwg_rules( self.fwg_with_rule, fwg_ports, event=consts.UPDATE_FWG)) self.l2.driver.update_firewall_group.assert_called_once_with( driver_ports, self.fwg_with_rule) def test_event_is_delete(self): fwg_ports = [self.fwg_with_rule['port_details'][fake_data.PORT1]] driver_ports = copy.deepcopy(fwg_ports) driver_ports[0].update({'lvlan': 999}) self.assertTrue(self.l2._apply_fwg_rules( self.fwg_with_rule, fwg_ports, event=consts.DELETE_FWG)) self.l2.driver.delete_firewall_group.assert_called_once_with( fwg_ports, self.fwg_with_rule) def test_raised_in_driver(self): self.l2.driver.delete_firewall_group.side_effect = \ f_exc.FirewallInternalDriverError(driver='ovs firewall') fwg_ports = [self.fwg_with_rule['port_details'][fake_data.PORT1]] driver_ports = copy.deepcopy(fwg_ports) driver_ports[0].update({'lvlan': 999}) self.assertFalse(self.l2._apply_fwg_rules( self.fwg_with_rule, fwg_ports, event=consts.DELETE_FWG)) self.l2.driver.delete_firewall_group.assert_called_once_with( fwg_ports, self.fwg_with_rule) class TestSendFwgStatus(TestFWaasV2AgentExtensionBase): def setUp(self): super(TestSendFwgStatus, self).setUp() self.rpc.set_firewall_group_status = mock.Mock() def test_success(self): self.assertIsNone(self.l2._send_fwg_status( self.ctx, self.fwg_id, 'ACTIVE', self.host)) def test_failure(self): self.rpc.set_firewall_group_status.side_effect = Exception self.assertIsNone(self.l2._send_fwg_status( self.ctx, self.fwg_id, 'ACTIVE', self.host)) class TestAddLocalVlanToPorts(TestFWaasV2AgentExtensionBase): def setUp(self): super(TestAddLocalVlanToPorts, self).setUp() class DummyVlan(object): def __init__(self, vlan=None): self.vlan = vlan self.l2.vlan_manager.get.return_value = DummyVlan(vlan='999') self.port_with_detail = { 'port_id': fake_data.PORT1, 'id': fake_data.PORT1, 'network_id': fake_data.NETWORK_ID, 'port_details': { fake_data.PORT1: { 'device': 'c12e5c1e-d68e-45bd-a2d3-1f2f32604e41', 'device_owner': 'compute:nova', 'host': self.host, 'network_id': fake_data.NETWORK_ID, 'fixed_ips': [ {'subnet_id': fake_data.SUBNET_ID, 'ip_address': '172.24.4.5'}], 'allowed_address_pairs': [], 'port_security_enabled': True, 'id': fake_data.PORT1 } } } def test_port_has_detail_and_port_id(self): del self.port_with_detail['id'] expect = [copy.deepcopy(self.port_with_detail)] expect[0].update({'lvlan': 999}) actual = self.l2._add_local_vlan_to_ports([self.port_with_detail]) self.l2.vlan_manager.get.assert_called_once_with( self.port_with_detail['network_id']) self.assertEqual(expect, actual) def test_port_has_detail_and_id(self): del self.port_with_detail['port_id'] expect = [copy.deepcopy(self.port_with_detail)] expect[0].update({'lvlan': 999}) actual = self.l2._add_local_vlan_to_ports([self.port_with_detail]) self.l2.vlan_manager.get.assert_called_once_with( self.port_with_detail['network_id']) self.assertEqual(expect, actual) def test_port_has_no_detail(self): del self.port_with_detail['port_details'] expect = [copy.deepcopy(self.port_with_detail)] expect[0].update({'lvlan': 999}) actual = self.l2._add_local_vlan_to_ports([self.port_with_detail]) self.l2.vlan_manager.get.assert_called_once_with( self.port_with_detail['network_id']) self.assertEqual(expect, actual) class TestFWaaSL2PluginApi(TestFWaasV2AgentExtensionBase): def setUp(self): super(TestFWaaSL2PluginApi, self).setUp() self.plugin = fwaas_v2.FWaaSL2PluginApi( consts.FIREWALL_PLUGIN, self.host) self.plugin.client = mock.Mock() self.cctxt = self.plugin.client.prepare() def test_get_firewall_group_for_port(self): self.plugin.get_firewall_group_for_port(self.ctx, mock.ANY) self.cctxt.call.assert_called_once_with( self.ctx, 'get_firewall_group_for_port', port_id=mock.ANY ) def test_set_firewall_group_status(self): self.plugin.set_firewall_group_status( self.ctx, self.fwg_id, 'ACTIVE', self.host) self.cctxt.call.assert_called_once_with( self.ctx, 'set_firewall_group_status', fwg_id=self.fwg_id, status='ACTIVE', host=self.host, ) def test_firewall_group_deleted(self): self.plugin.firewall_group_deleted(self.ctx, self.fwg_id, self.host) self.cctxt.call.assert_called_once_with( self.ctx, 'firewall_group_deleted', fwg_id=self.fwg_id, host=self.host, ) class TestPortFirewallGroupMap(base.BaseTestCase): def setUp(self): super(TestPortFirewallGroupMap, self).setUp() self.fake = fake_data.FakeFWaaSL2Agent() self.map = fwaas_v2.PortFirewallGroupMap() self.fwg = self.fake.create('fwg') self.fwg_id = self.fwg['id'] self.port = self.fake.create('port') self.fwg['ports'] = [] def test_set_and_get(self): self.map.set_fwg(self.fwg) self.assertEqual(self.fwg, self.map.get_fwg(self.fwg_id)) def test_set_and_get_port_fwg(self): port1 = self.port port2 = self.fake.create('port') self.map.set_port_fwg(port1, self.fwg) self.map.set_port_fwg(port2, self.fwg) self.assertEqual(self.fwg, self.map.get_port_fwg(port1)) self.assertEqual(self.fwg, self.map.get_port_fwg(port2)) self.assertIsNone(self.map.get_port_fwg('unknown')) def test_remove_port(self): port1 = self.port port2 = self.fake.create('port') self.map.set_port_fwg(port1, self.fwg) self.map.remove_port(port2) self.map.set_port_fwg(port2, self.fwg) self.map.remove_port(port1) self.assertIsNone(self.map.get_port(port1)) self.assertEqual([port2['port_id']], self.map.get_fwg(self.fwg_id)['ports']) self.map.remove_port(port2) self.assertIsNone(self.map.get_port(port2)) self.assertEqual([], self.map.get_fwg(self.fwg_id)['ports']) def test_remove_non_exist_port(self): port1 = self.port port2 = self.fake.create('port') self.map.set_port_fwg(port1, self.fwg) self.map.remove_port(port2) self.assertIsNone(self.map.get_port(port2)) def test_illegal_remove_port_no_relation_with_fwg(self): port1 = self.port port1_id = port1['port_id'] self.map.set_port_fwg(port1, self.fwg) self.map.port_fwg[port1_id] = None self.map.remove_port(port1) self.assertIsNone(self.map.get_port(port1)) def test_remove_fwg(self): self.map.set_fwg(self.fwg) self.assertEqual(self.fwg, self.map.get_fwg(self.fwg_id)) self.map.remove_fwg(self.fwg) self.assertIsNone(self.map.get_fwg(self.fwg_id)) def test_remove_fwg_non_exist(self): self.map.remove_fwg(self.fwg) self.assertIsNone(self.map.get_fwg(self.fwg_id)) neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/agents/l2/fake_data.py0000664000175000017500000001222713555577713031130 0ustar zuulzuul00000000000000# Copyright 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 copy import mock from neutron_lib import constants as nl_consts from oslo_utils import uuidutils TENANT_UUID = uuidutils.generate_uuid() TENANT_ID = TENANT_UUID PROJECT_ID = TENANT_UUID NETWORK_ID = uuidutils.generate_uuid() SUBNET_ID = uuidutils.generate_uuid() DEVICE_ID = uuidutils.generate_uuid() PORT1 = uuidutils.generate_uuid() PORT2 = uuidutils.generate_uuid() PORT3 = uuidutils.generate_uuid() PORT4 = uuidutils.generate_uuid() HOST = 'fake_host' class FakeFWaaSL2Agent(object): def __init__(self): super(FakeFWaaSL2Agent, self).__init__() def create(self, resource, attrs=None, minimal=False): """Create a fake fwaas v2 resources :param resource: A dictionary with all attributes :type resource: string :param attrs: A dictionary of each attribute you need to modify :type attrs: dictionary :param minimal: True if minimal port_detail is necessary otherwise False :type minimal: boolean :return: A OrderedDict faking the fwaas v2 resource """ target = getattr(self, "_" + resource) return copy.deepcopy(target(attrs=attrs, minimal=minimal)) def _fwg(self, **kwargs): fwg = { 'id': uuidutils.generate_uuid(), 'name': 'my-group-' + uuidutils.generate_uuid(), 'ingress_firewall_policy_id': uuidutils.generate_uuid(), 'egress_firewall_policy_id': uuidutils.generate_uuid(), 'description': 'my firewall group', 'status': nl_consts.PENDING_CREATE, 'ports': [PORT3, PORT4], 'admin_state_up': True, 'shared': False, 'tenant_id': TENANT_ID, 'project_id': PROJECT_ID } attrs = kwargs.get('attrs', None) if attrs: fwg.update(attrs) return fwg def _fwg_with_rule(self, **kwargs): fwg_with_rule = self.create('fwg', attrs={'ports': [PORT1, PORT2]}) rules = { 'ingress_rule_list': [mock.Mock()], 'egress_rule_list': [mock.Mock()], 'add-port-ids': [PORT1], 'del-port-ids': [PORT2], 'port_details': { PORT1: { 'device': uuidutils.generate_uuid(), 'device_owner': 'compute:nova', 'host': HOST, 'network_id': NETWORK_ID, 'fixed_ips': [ {'subnet_id': SUBNET_ID, 'ip_address': '172.24.4.5'}], 'allowed_address_pairs': [], 'port_security_enabled': True, 'id': PORT1 }, PORT2: { 'device': uuidutils.generate_uuid(), 'device_owner': 'compute:nova', 'host': HOST, 'network_id': NETWORK_ID, 'fixed_ips': [ {'subnet_id': SUBNET_ID, 'ip_address': '172.24.4.6'}], 'allowed_address_pairs': [], 'port_security_enabled': True, 'id': PORT2 } }, } fwg_with_rule.update(rules) if kwargs.get('minimal', None): fwg_with_rule.update({'ports': []}) fwg_with_rule.update({'add-port-ids': []}) fwg_with_rule.update({'del-port-ids': []}) fwg_with_rule.update({'port_details': {}}) attrs = kwargs.get('attrs', None) if attrs: fwg_with_rule.update(attrs) return fwg_with_rule def _port(self, **kwargs): if kwargs.get('minimal', None): return {'port_id': uuidutils.generate_uuid()} port_detail = { 'profile': {}, 'network_qos_policy_id': None, 'qos_policy_id': None, 'allowed_address_pairs': [], 'admin_state_up': True, 'network_id': NETWORK_ID, 'segmentation_id': None, 'fixed_ips': [ {'subnet_id': SUBNET_ID, 'ip_address': '172.24.4.5'}], 'vif_port': mock.Mock(), 'device_owner': 'compute:node', 'physical_network': 'physnet', 'mac_address': 'fa:16:3e:8a:80:2b', 'device': DEVICE_ID, 'port_security_enabled': True, 'port_id': uuidutils.generate_uuid(), 'network_type': 'flat', 'security_groups': [] } attrs = kwargs.get('attrs', None) if attrs: port_detail.update(attrs) return port_detail neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/agents/l2/__init__.py0000664000175000017500000000000013555577713030752 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/agents/test_firewall_service.py0000664000175000017500000000455613555577713033306 0ustar zuulzuul00000000000000# 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.tests import base from oslo_config import cfg from neutron_fwaas.services.firewall.agents import firewall_service FWAAS_NOP_DEVICE = ('neutron_fwaas.tests.unit.services.firewall.agents.' 'test_firewall_agent_api.NoopFwaasDriver') class TestFirewallDeviceDriverLoading(base.BaseTestCase): def setUp(self): super(TestFirewallDeviceDriverLoading, self).setUp() self.service = firewall_service.FirewallService() def test_loading_firewall_device_driver(self): """Get the sole device driver for FWaaS.""" cfg.CONF.set_override('driver', FWAAS_NOP_DEVICE, 'fwaas') driver = self.service.load_device_drivers() self.assertIsNotNone(driver) self.assertIn(driver.__class__.__name__, FWAAS_NOP_DEVICE) def test_fail_no_such_firewall_device_driver(self): """Failure test of import error for FWaaS device driver.""" cfg.CONF.set_override('driver', 'no.such.class', 'fwaas') self.assertRaises(ImportError, self.service.load_device_drivers) def test_fail_firewall_no_device_driver_specified(self): """Failure test when no FWaaS device driver is specified. This is a configuration error, as the user must specify a device driver, when enabling the firewall service (and there is no default configuration set. We'll simulate that by using an empty string. """ cfg.CONF.set_override('driver', '', 'fwaas') self.assertRaises(ValueError, self.service.load_device_drivers) neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/plugins/0000775000175000017500000000000013555600005026513 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/plugins/__init__.py0000664000175000017500000000000013555577713030635 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/drivers/0000775000175000017500000000000013555600005026510 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/drivers/linux/0000775000175000017500000000000013555600005027647 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000014700000000000011217 Lustar 00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/drivers/linux/test_legacy_conntrack.pyneutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/drivers/linux/test_legacy_conntrack.0000664000175000017500000001363713555577713034252 0ustar zuulzuul00000000000000# 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 mock import testtools from neutron.tests import base from neutron_fwaas.services.firewall.drivers.linux import legacy_conntrack from neutron_lib import constants FW_RULES = [ {'position': '1', 'protocol': 'icmp', 'ip_version': 4, 'enabled': True, 'action': 'reject', 'id': 'fake-fw-rule1'}, {'source_port': '0:10', 'destination_port': '0:10', 'position': '2', 'protocol': 'tcp', 'ip_version': 4, 'enabled': True, 'action': 'reject', 'id': 'fake-fw-rule2'}, {'source_port': '0:10', 'destination_port': '0:20', 'position': '3', 'protocol': 'udp', 'ip_version': 4, 'enabled': True, 'action': 'reject', 'id': 'fake-fw-rule3'}, {'source_port': '1', 'destination_port': '0:10', 'position': '4', 'protocol': 'tcp', 'ip_version': 4, 'enabled': True, 'action': 'reject', 'id': 'fake-fw-rule5'}, {'source_port': '0:10', 'destination_port': None, 'position': '5', 'protocol': 'udp', 'ip_version': 4, 'enabled': True, 'action': 'reject', 'id': 'fake-fw-rule5'}, {'source_port': '1', 'destination_port': '3', 'position': '6', 'protocol': 'tcp', 'ip_version': 4, 'enabled': True, 'action': 'reject', 'id': 'fake-fw-rule6'}, {'source_port': '1', 'destination_port': '2', 'position': '7', 'protocol': 'udp', 'ip_version': 4, 'enabled': True, 'action': 'reject', 'id': 'fake-fw-rule7'}, ] ICMP_ENTRY = (4, 'icmp', 8, 0, '1.1.1.1', '2.2.2.2', '1234') TCP_ENTRY = (4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2') UDP_ENTRY = (4, 'udp', 1, 2, '1.1.1.1', '2.2.2.2') CONNTRACK_LIST = '''\ icmp 1 27 src=1.1.1.1 dst=2.2.2.2 type=8 code=0 id=18127 src=2.2.2.2 dst=1.1.1.1 type=0 code=0 id=18127 mark=0 use=1 tcp 6 88 SYN_SENT src=1.1.1.1 dst=2.2.2.2 sport=36567 dport=5000 [UNREPLIED] src=2.2.2.2 dst=1.1.1.1 sport=5000 dport=36567 mark=0 use=1 unknown 2 551 src=0.0.0.0 dst=224.0.0.1 [UNREPLIED] src=224.0.0.1 dst=0.0.0.0 mark=0 use=1 udp 17 28 src=0.0.0.0 dst=255.255.255.255 sport=68 dport=67 [UNREPLIED] src=255.255.255.255 dst=0.0.0.0 sport=67 dport=68 mark=0 use=1 ''' # nopep8 ROUTER_NAMESPACE = 'qrouter-fake-namespace' class ConntrackLegacyTestCase(base.BaseTestCase): def setUp(self): super(ConntrackLegacyTestCase, self).setUp() self.utils_exec = mock.Mock() self.conntrack_driver = legacy_conntrack.ConntrackLegacy() self.conntrack_driver.initialize(execute=self.utils_exec) def test_excecute_command_failed(self): with testtools.ExpectedException(RuntimeError): self.conntrack_driver._execute_command(['fake', 'command']) raise RuntimeError("Failed execute conntrack command fake command") def test_flush_entries(self): self.conntrack_driver.flush_entries(ROUTER_NAMESPACE) self.utils_exec.assert_called_with( ['ip', 'netns', 'exec', ROUTER_NAMESPACE, 'conntrack', '-D'], check_exit_code=True, extra_ok_codes=[1], run_as_root=True) def test_list_entries(self): def get_contrack_entries(conntrack_cmd): if 'ipv' + str(constants.IP_VERSION_4) in conntrack_cmd: return CONNTRACK_LIST return '' self.conntrack_driver._execute_command = mock.Mock( side_effect=get_contrack_entries) entries = self.conntrack_driver.list_entries(ROUTER_NAMESPACE) protocols = set([entry[1] for entry in entries]) supported_protocols = set(legacy_conntrack.ATTR_POSITIONS.keys()) self.assertTrue(protocols.issubset(supported_protocols)) def test_delete_entries(self): list_entries_mock = mock.patch( 'neutron_fwaas.services.firewall.drivers.linux' '.legacy_conntrack.ConntrackLegacy.list_entries') self.list_entries = list_entries_mock.start() self.conntrack_driver.list_entries.return_value = [ ICMP_ENTRY, TCP_ENTRY, UDP_ENTRY] self.conntrack_driver.delete_entries(FW_RULES, ROUTER_NAMESPACE) calls = [ mock.call(['ip', 'netns', 'exec', ROUTER_NAMESPACE, 'conntrack', '-D', '-f', 'ipv4', '-p', 'icmp', '--icmp-type', 8, '--icmp-code', 0, '-s', '1.1.1.1', '-d', '2.2.2.2', '--icmp-id', '1234'], check_exit_code=True, extra_ok_codes=[1], run_as_root=True), mock.call(['ip', 'netns', 'exec', ROUTER_NAMESPACE, 'conntrack', '-D', '-f', 'ipv4', '-p', 'tcp', '--sport', 1, '--dport', 2, '-s', '1.1.1.1', '-d', '2.2.2.2'], check_exit_code=True, extra_ok_codes=[1], run_as_root=True), mock.call(['ip', 'netns', 'exec', ROUTER_NAMESPACE, 'conntrack', '-D', '-f', 'ipv4', '-p', 'udp', '--sport', 1, '--dport', 2, '-s', '1.1.1.1', '-d', '2.2.2.2'], check_exit_code=True, extra_ok_codes=[1], run_as_root=True), ] self.utils_exec.assert_has_calls(calls) neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/drivers/linux/__init__.py0000664000175000017500000000000013555577713031771 0ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000015000000000000011211 Lustar 00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/drivers/linux/test_iptables_fwaas_v2.pyneutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/drivers/linux/test_iptables_fwaas_v20000664000175000017500000004500013555577713034246 0ustar zuulzuul00000000000000# 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 copy import mock from neutron.tests import base from neutron.tests.unit.api.v2 import test_base as test_api_v2 import neutron_fwaas.services.firewall.drivers.linux.iptables_fwaas_v2 as fwaas _uuid = test_api_v2._uuid FAKE_SRC_PREFIX = '10.0.0.0/24' FAKE_DST_PREFIX = '20.0.0.0/24' FAKE_PROTOCOL = 'tcp' FAKE_SRC_PORT = 5000 FAKE_DST_PORT = 22 FAKE_FW_ID = 'fake-fw-uuid' FAKE_PORT_IDS = ('1_fake-port-uuid', '2_fake-port-uuid') FW_LEGACY = 'legacy' MAX_INTF_NAME_LEN = 14 class IptablesFwaasTestCase(base.BaseTestCase): def setUp(self): super(IptablesFwaasTestCase, self).setUp() self.iptables_cls_p = mock.patch( 'neutron.agent.linux.iptables_manager.IptablesManager') self.iptables_cls_p.start() self.firewall = fwaas.IptablesFwaasDriver() self.firewall.conntrack.delete_entries = mock.Mock() self.firewall.conntrack.flush_entries = mock.Mock() def _fake_rules_v4(self, fwid, apply_list): rule_list = [] rule1 = {'enabled': True, 'action': 'allow', 'ip_version': 4, 'protocol': 'tcp', 'destination_port': '80', 'source_ip_address': '10.24.4.2', 'id': 'fake-fw-rule1'} rule2 = {'enabled': True, 'action': 'deny', 'ip_version': 4, 'protocol': 'tcp', 'destination_port': '22', 'id': 'fake-fw-rule2'} rule3 = {'enabled': True, 'action': 'reject', 'ip_version': 4, 'protocol': 'tcp', 'destination_port': '23', 'id': 'fake-fw-rule3'} ingress_chain = ('iv4%s' % fwid)[:11] egress_chain = ('ov4%s' % fwid)[:11] for router_info_inst, port_ids in apply_list: v4filter_inst = router_info_inst.iptables_manager.ipv4['filter'] v4filter_inst.chains.append(ingress_chain) v4filter_inst.chains.append(egress_chain) rule_list.append(rule1) rule_list.append(rule2) rule_list.append(rule3) return rule_list def _fake_firewall_no_rule(self): rule_list = [] fw_inst = {'id': FAKE_FW_ID, 'admin_state_up': True, 'tenant_id': 'tenant-uuid', 'egress_rule_list': rule_list, 'ingress_rule_list': rule_list} return fw_inst def _fake_firewall(self, rule_list): _rule_list = copy.deepcopy(rule_list) for rule in _rule_list: rule['position'] = str(_rule_list.index(rule)) fw_inst = {'id': FAKE_FW_ID, 'admin_state_up': True, 'tenant_id': 'tenant-uuid', 'egress_rule_list': _rule_list, 'ingress_rule_list': _rule_list} return fw_inst def _fake_firewall_with_admin_down(self, rule_list): fw_inst = {'id': FAKE_FW_ID, 'admin_state_up': False, 'tenant_id': 'tenant-uuid', 'egress_rule_list': rule_list, 'ingress_rule_list': rule_list} return fw_inst def _fake_apply_list(self, router_count=1, distributed=False, distributed_mode=None): apply_list = [] while router_count > 0: iptables_inst = mock.Mock() if distributed: router_inst = {'distributed': distributed} else: router_inst = {} v4filter_inst = mock.Mock() v6filter_inst = mock.Mock() v4filter_inst.chains = [] v6filter_inst.chains = [] iptables_inst.ipv4 = {'filter': v4filter_inst} iptables_inst.ipv6 = {'filter': v6filter_inst} router_info_inst = mock.Mock() router_info_inst.iptables_manager = iptables_inst router_info_inst.snat_iptables_manager = iptables_inst if distributed_mode == 'dvr': router_info_inst.rtr_fip_connect = True router_info_inst.router = router_inst apply_list.append((router_info_inst, FAKE_PORT_IDS)) router_count -= 1 return apply_list def _get_intf_name(self, if_prefix, port_id): _name = "%s%s" % (if_prefix, port_id) return _name[:MAX_INTF_NAME_LEN] def _setup_firewall_with_rules(self, func, router_count=1, distributed=False, distributed_mode=None): apply_list = self._fake_apply_list(router_count=router_count, distributed=distributed, distributed_mode=distributed_mode) rule_list = self._fake_rules_v4(FAKE_FW_ID, apply_list) firewall = self._fake_firewall(rule_list) if distributed: if distributed_mode == 'dvr_snat': if_prefix = 'sg-' if distributed_mode == 'dvr': if_prefix = 'rfp-' else: if_prefix = 'qr-' distributed_mode = 'legacy' func(distributed_mode, apply_list, firewall) invalid_rule = '-m state --state INVALID -j DROP' est_rule = '-m state --state RELATED,ESTABLISHED -j ACCEPT' rule1 = '-p tcp -s 10.24.4.2/32 -m tcp --dport 80 -j ACCEPT' rule2 = '-p tcp -m tcp --dport 22 -j DROP' rule3 = '-p tcp -m tcp --dport 23 -j REJECT' ingress_chain = 'iv4%s' % firewall['id'] egress_chain = 'ov4%s' % firewall['id'] bname = fwaas.iptables_manager.binary_name ipt_mgr_ichain = '%s-%s' % (bname, ingress_chain[:11]) ipt_mgr_echain = '%s-%s' % (bname, egress_chain[:11]) for router_info_inst, port_ids in apply_list: v4filter_inst = router_info_inst.iptables_manager.ipv4['filter'] calls = [mock.call.remove_chain('iv4fake-fw-uuid'), mock.call.remove_chain('ov4fake-fw-uuid'), mock.call.remove_chain('fwaas-default-policy'), mock.call.add_chain('fwaas-default-policy'), mock.call.add_rule('fwaas-default-policy', '-j DROP'), mock.call.add_chain(ingress_chain), mock.call.add_rule(ingress_chain, invalid_rule), mock.call.add_rule(ingress_chain, est_rule), mock.call.add_chain(egress_chain), mock.call.add_rule(egress_chain, invalid_rule), mock.call.add_rule(egress_chain, est_rule), mock.call.add_rule(ingress_chain, rule1), mock.call.add_rule(ingress_chain, rule2), mock.call.add_rule(ingress_chain, rule3), mock.call.add_rule(egress_chain, rule1), mock.call.add_rule(egress_chain, rule2), mock.call.add_rule(egress_chain, rule3)] for port in FAKE_PORT_IDS: intf_name = self._get_intf_name(if_prefix, port) calls.append(mock.call.add_rule('FORWARD', '-o %s -j %s' % (intf_name, ipt_mgr_ichain))) for port in FAKE_PORT_IDS: intf_name = self._get_intf_name(if_prefix, port) calls.append(mock.call.add_rule('FORWARD', '-i %s -j %s' % (intf_name, ipt_mgr_echain))) for direction in ['o', 'i']: for port_id in FAKE_PORT_IDS: intf_name = self._get_intf_name(if_prefix, port_id) calls.append(mock.call.add_rule('FORWARD', '-%s %s -j %s-fwaas-defau' % (direction, intf_name, bname))) v4filter_inst.assert_has_calls(calls) def test_create_firewall_group_no_rules(self): apply_list = self._fake_apply_list() first_ri = apply_list[0][0] firewall = self._fake_firewall_no_rule() self.firewall.create_firewall_group('legacy', apply_list, firewall) invalid_rule = '-m state --state INVALID -j DROP' est_rule = '-m state --state RELATED,ESTABLISHED -j ACCEPT' bname = fwaas.iptables_manager.binary_name for ip_version in (4, 6): ingress_chain = ('iv%s%s' % (ip_version, firewall['id'])) egress_chain = ('ov%s%s' % (ip_version, firewall['id'])) calls = [mock.call.remove_chain( 'iv%sfake-fw-uuid' % ip_version), mock.call.remove_chain( 'ov%sfake-fw-uuid' % ip_version), mock.call.remove_chain('fwaas-default-policy'), mock.call.add_chain('fwaas-default-policy'), mock.call.add_rule('fwaas-default-policy', '-j DROP'), mock.call.add_chain(ingress_chain), mock.call.add_rule(ingress_chain, invalid_rule), mock.call.add_rule(ingress_chain, est_rule), mock.call.add_chain(egress_chain), mock.call.add_rule(egress_chain, invalid_rule), mock.call.add_rule(egress_chain, est_rule)] for port_id in FAKE_PORT_IDS: for direction in ['o', 'i']: mock.call.add_rule('FORWARD', '-%s qr-%s -j %s-fwaas-defau' % (port_id, direction, bname)) if ip_version == 4: v4filter_inst = first_ri.iptables_manager.ipv4['filter'] v4filter_inst.assert_has_calls(calls) else: v6filter_inst = first_ri.iptables_manager.ipv6['filter'] v6filter_inst.assert_has_calls(calls) def test_create_firewall_group_with_rules(self): self._setup_firewall_with_rules(self.firewall.create_firewall_group) def test_create_firewall_group_with_rules_without_distributed_attr(self): self._setup_firewall_with_rules(self.firewall.create_firewall_group, distributed=None) def test_create_firewall_group_with_rules_two_routers(self): self._setup_firewall_with_rules(self.firewall.create_firewall_group, router_count=2) def test_update_firewall_group_with_rules(self): self._setup_firewall_with_rules(self.firewall.update_firewall_group) def test_update_firewall_group_with_rules_without_distributed_attr(self): self._setup_firewall_with_rules(self.firewall.update_firewall_group, distributed=None) def _test_delete_firewall_group(self, distributed=False): apply_list = self._fake_apply_list(distributed=distributed) first_ri = apply_list[0][0] firewall = self._fake_firewall_no_rule() self.firewall.delete_firewall_group('legacy', apply_list, firewall) ingress_chain = 'iv4%s' % firewall['id'] egress_chain = 'ov4%s' % firewall['id'] calls = [mock.call.remove_chain(ingress_chain), mock.call.remove_chain(egress_chain), mock.call.remove_chain('fwaas-default-policy')] first_ri.iptables_manager.ipv4['filter'].assert_has_calls(calls) def test_delete_firewall_group(self): self._test_delete_firewall_group() def test_delete_firewall_group_without_distributed_attr(self): self._test_delete_firewall_group(distributed=None) def test_create_firewall_group_with_admin_down(self): apply_list = self._fake_apply_list() first_ri = apply_list[0][0] rule_list = self._fake_rules_v4(FAKE_FW_ID, apply_list) firewall = self._fake_firewall_with_admin_down(rule_list) self.firewall.create_firewall_group('legacy', apply_list, firewall) calls = [mock.call.remove_chain('iv4fake-fw-uuid'), mock.call.remove_chain('ov4fake-fw-uuid'), mock.call.remove_chain('fwaas-default-policy'), mock.call.add_chain('fwaas-default-policy'), mock.call.add_rule('fwaas-default-policy', '-j DROP')] first_ri.iptables_manager.ipv4['filter'].assert_has_calls(calls) def test_create_firewall_group_with_rules_dvr_snat(self): self._setup_firewall_with_rules(self.firewall.create_firewall_group, distributed=True, distributed_mode='dvr_snat') def test_update_firewall_group_with_rules_dvr_snat(self): self._setup_firewall_with_rules(self.firewall.update_firewall_group, distributed=True, distributed_mode='dvr_snat') def test_create_firewall_group_with_rules_dvr(self): self._setup_firewall_with_rules(self.firewall.create_firewall_group, distributed=True, distributed_mode='dvr') def test_update_firewall_group_with_rules_dvr(self): self._setup_firewall_with_rules(self.firewall.update_firewall_group, distributed=True, distributed_mode='dvr') def test_remove_conntrack_new_firewall(self): apply_list = self._fake_apply_list() firewall = self._fake_firewall_no_rule() self.firewall.create_firewall_group(FW_LEGACY, apply_list, firewall) for router_info_inst, port_ids in apply_list: namespace = router_info_inst.iptables_manager.namespace calls = [mock.call(namespace)] self.firewall.conntrack.flush_entries.assert_has_calls(calls) def test_remove_conntrack_inserted_rule(self): apply_list = self._fake_apply_list() rule_list = self._fake_rules_v4(FAKE_FW_ID, apply_list) firewall = self._fake_firewall(rule_list) self.firewall.create_firewall_group(FW_LEGACY, apply_list, firewall) self.firewall.pre_firewall = dict(firewall) insert_rule = {'enabled': True, 'action': 'deny', 'ip_version': 4, 'protocol': 'icmp', 'id': 'fake-fw-rule'} rule_list.insert(2, insert_rule) firewall = self._fake_firewall(rule_list) self.firewall.update_firewall_group(FW_LEGACY, apply_list, firewall) rules_changed = [ {'destination_port': '23', 'position': '2', 'protocol': 'tcp', 'ip_version': 4, 'enabled': True, 'action': 'reject', 'id': 'fake-fw-rule3'}, {'destination_port': '23', 'position': '3', 'protocol': 'tcp', 'ip_version': 4, 'enabled': True, 'action': 'reject', 'id': 'fake-fw-rule3'} ] * 2 # Egress and ingress rule lists rules_inserted = [ {'id': 'fake-fw-rule', 'protocol': 'icmp', 'ip_version': 4, 'enabled': True, 'action': 'deny', 'position': '2'} ] * 2 # Egress and ingress rule lists for router_info_inst, port_ids in apply_list: namespace = router_info_inst.iptables_manager.namespace self.firewall.conntrack.delete_entries.assert_called_once_with( rules_changed + rules_inserted, namespace ) def test_remove_conntrack_removed_rule(self): apply_list = self._fake_apply_list() rule_list = self._fake_rules_v4(FAKE_FW_ID, apply_list) firewall = self._fake_firewall(rule_list) self.firewall.create_firewall_group(FW_LEGACY, apply_list, firewall) self.firewall.pre_firewall = dict(firewall) remove_rule = rule_list[1] rule_list.remove(remove_rule) firewall = self._fake_firewall(rule_list) self.firewall.update_firewall_group(FW_LEGACY, apply_list, firewall) rules_changed = [ {'destination_port': '23', 'position': '2', 'protocol': 'tcp', 'ip_version': 4, 'enabled': True, 'action': 'reject', 'id': 'fake-fw-rule3'}, {'destination_port': '23', 'position': '1', 'protocol': 'tcp', 'ip_version': 4, 'enabled': True, 'action': 'reject', 'id': 'fake-fw-rule3'} ] * 2 # Egress and ingress rule lists rules_removed = [ {'enabled': True, 'position': '1', 'protocol': 'tcp', 'id': 'fake-fw-rule2', 'ip_version': 4, 'action': 'deny', 'destination_port': '22'} ] * 2 # Egress and ingress rule lists for router_info_inst, port_ids in apply_list: namespace = router_info_inst.iptables_manager.namespace self.firewall.conntrack.delete_entries.assert_called_once_with( rules_changed + rules_removed, namespace ) def test_remove_conntrack_changed_rule(self): apply_list = self._fake_apply_list() rule_list = self._fake_rules_v4(FAKE_FW_ID, apply_list) firewall = self._fake_firewall(rule_list) self.firewall.create_firewall_group(FW_LEGACY, apply_list, firewall) income_rule = {'enabled': True, 'action': 'deny', 'ip_version': 4, 'protocol': 'tcp', 'id': 'fake-fw-rule3'} rule_list[2] = income_rule firewall = self._fake_firewall(rule_list) self.firewall.update_firewall_group(FW_LEGACY, apply_list, firewall) rules_changed = [ {'id': 'fake-fw-rule3', 'enabled': True, 'action': 'reject', 'position': '2', 'destination_port': '23', 'ip_version': 4, 'protocol': 'tcp'}, {'position': '2', 'enabled': True, 'action': 'deny', 'id': 'fake-fw-rule3', 'ip_version': 4, 'protocol': 'tcp'} ] * 2 # Egress and ingress rule lists for router_info_inst, port_ids in apply_list: namespace = router_info_inst.iptables_manager.namespace self.firewall.conntrack.delete_entries.assert_called_once_with( rules_changed, namespace ) neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/drivers/linux/test_iptables_fwaas.py0000664000175000017500000004310713555577713034274 0ustar zuulzuul00000000000000# 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 copy import mock from neutron.tests import base from neutron.tests.unit.api.v2 import test_base as test_api_v2 import neutron_fwaas.services.firewall.drivers.linux.iptables_fwaas as fwaas _uuid = test_api_v2._uuid FAKE_SRC_PREFIX = '10.0.0.0/24' FAKE_DST_PREFIX = '20.0.0.0/24' FAKE_PROTOCOL = 'tcp' FAKE_SRC_PORT = 5000 FAKE_DST_PORT = 22 FAKE_FW_ID = 'fake-fw-uuid' FW_LEGACY = 'legacy' class IptablesFwaasTestCase(base.BaseTestCase): def setUp(self): super(IptablesFwaasTestCase, self).setUp() self.conntrack_driver = mock.Mock() self.conntrack_driver.initialize = mock.Mock() self.conntrack_driver.delete_entries = mock.Mock() self.conntrack_driver.flush_entries = mock.Mock() self.iptables_cls_p = mock.patch( 'neutron.agent.linux.iptables_manager.IptablesManager') self.iptables_cls_p.start() self.firewall = fwaas.IptablesFwaasDriver() self.firewall.conntrack = self.conntrack_driver def _fake_rules_v4(self, fwid, apply_list): rule_list = [] rule1 = {'enabled': True, 'action': 'allow', 'ip_version': 4, 'protocol': 'tcp', 'destination_port': '80', 'source_ip_address': '10.24.4.2', 'id': 'fake-fw-rule1'} rule2 = {'enabled': True, 'action': 'deny', 'ip_version': 4, 'protocol': 'tcp', 'destination_port': '22', 'id': 'fake-fw-rule2'} rule3 = {'enabled': True, 'action': 'reject', 'ip_version': 4, 'protocol': 'tcp', 'destination_port': '23', 'id': 'fake-fw-rule3'} ingress_chain = ('iv4%s' % fwid)[:11] egress_chain = ('ov4%s' % fwid)[:11] for router_info_inst in apply_list: v4filter_inst = router_info_inst.iptables_manager.ipv4['filter'] v4filter_inst.chains.append(ingress_chain) v4filter_inst.chains.append(egress_chain) rule_list.append(rule1) rule_list.append(rule2) rule_list.append(rule3) return rule_list def _fake_firewall_no_rule(self): rule_list = [] fw_inst = {'id': FAKE_FW_ID, 'admin_state_up': True, 'tenant_id': 'tenant-uuid', 'firewall_rule_list': rule_list} return fw_inst def _fake_firewall(self, rule_list): _rule_list = copy.deepcopy(rule_list) for rule in _rule_list: rule['position'] = str(_rule_list.index(rule)) fw_inst = {'id': FAKE_FW_ID, 'admin_state_up': True, 'tenant_id': 'tenant-uuid', 'firewall_rule_list': _rule_list} return fw_inst def _fake_firewall_with_admin_down(self, rule_list): fw_inst = {'id': FAKE_FW_ID, 'admin_state_up': False, 'tenant_id': 'tenant-uuid', 'firewall_rule_list': rule_list} return fw_inst def _fake_apply_list(self, router_count=1, distributed=False, distributed_mode=None): apply_list = [] while router_count > 0: iptables_inst = mock.Mock() if distributed is not None: router_inst = {'distributed': distributed} else: router_inst = {} v4filter_inst = mock.Mock() v6filter_inst = mock.Mock() v4filter_inst.chains = [] v6filter_inst.chains = [] iptables_inst.ipv4 = {'filter': v4filter_inst} iptables_inst.ipv6 = {'filter': v6filter_inst} router_info_inst = mock.Mock() router_info_inst.iptables_manager = iptables_inst router_info_inst.snat_iptables_manager = iptables_inst if distributed_mode == 'dvr': router_info_inst.rtr_fip_connect = True router_info_inst.router = router_inst apply_list.append(router_info_inst) router_count -= 1 return apply_list def _setup_firewall_with_rules(self, func, router_count=1, distributed=False, distributed_mode=None): apply_list = self._fake_apply_list(router_count=router_count, distributed=distributed, distributed_mode=distributed_mode) rule_list = self._fake_rules_v4(FAKE_FW_ID, apply_list) firewall = self._fake_firewall(rule_list) if distributed: if distributed_mode == 'dvr_snat': if_prefix = 'sg-+' if distributed_mode == 'dvr': if_prefix = 'rfp-+' else: if_prefix = 'qr-+' distributed_mode = 'legacy' func(distributed_mode, apply_list, firewall) invalid_rule = '-m state --state INVALID -j DROP' est_rule = '-m state --state RELATED,ESTABLISHED -j ACCEPT' rule1 = '-p tcp -s 10.24.4.2/32 -m tcp --dport 80 -j ACCEPT' rule2 = '-p tcp -m tcp --dport 22 -j DROP' rule3 = '-p tcp -m tcp --dport 23 -j REJECT' ingress_chain = 'iv4%s' % firewall['id'] egress_chain = 'ov4%s' % firewall['id'] bname = fwaas.iptables_manager.binary_name ipt_mgr_ichain = '%s-%s' % (bname, ingress_chain[:11]) ipt_mgr_echain = '%s-%s' % (bname, egress_chain[:11]) for router_info_inst in apply_list: v4filter_inst = router_info_inst.iptables_manager.ipv4['filter'] calls = [mock.call.remove_chain('iv4fake-fw-uuid'), mock.call.remove_chain('ov4fake-fw-uuid'), mock.call.remove_chain('fwaas-default-policy'), mock.call.add_chain('fwaas-default-policy'), mock.call.add_rule('fwaas-default-policy', '-j DROP'), mock.call.add_chain(ingress_chain), mock.call.add_rule(ingress_chain, invalid_rule), mock.call.add_rule(ingress_chain, est_rule), mock.call.add_chain(egress_chain), mock.call.add_rule(egress_chain, invalid_rule), mock.call.add_rule(egress_chain, est_rule), mock.call.add_rule(ingress_chain, rule1), mock.call.add_rule(egress_chain, rule1), mock.call.add_rule(ingress_chain, rule2), mock.call.add_rule(egress_chain, rule2), mock.call.add_rule(ingress_chain, rule3), mock.call.add_rule(egress_chain, rule3), mock.call.add_rule('FORWARD', '-o %s -j %s' % (if_prefix, ipt_mgr_ichain)), mock.call.add_rule('FORWARD', '-i %s -j %s' % (if_prefix, ipt_mgr_echain)), mock.call.add_rule('FORWARD', '-o %s -j %s-fwaas-defau' % (if_prefix, bname)), mock.call.add_rule('FORWARD', '-i %s -j %s-fwaas-defau' % (if_prefix, bname))] v4filter_inst.assert_has_calls(calls) def test_create_firewall_no_rules(self): apply_list = self._fake_apply_list() firewall = self._fake_firewall_no_rule() self.firewall.create_firewall('legacy', apply_list, firewall) invalid_rule = '-m state --state INVALID -j DROP' est_rule = '-m state --state RELATED,ESTABLISHED -j ACCEPT' bname = fwaas.iptables_manager.binary_name for ip_version in (4, 6): ingress_chain = ('iv%s%s' % (ip_version, firewall['id'])) egress_chain = ('ov%s%s' % (ip_version, firewall['id'])) calls = [mock.call.remove_chain( 'iv%sfake-fw-uuid' % ip_version), mock.call.remove_chain( 'ov%sfake-fw-uuid' % ip_version), mock.call.remove_chain('fwaas-default-policy'), mock.call.add_chain('fwaas-default-policy'), mock.call.add_rule('fwaas-default-policy', '-j DROP'), mock.call.add_chain(ingress_chain), mock.call.add_rule(ingress_chain, invalid_rule), mock.call.add_rule(ingress_chain, est_rule), mock.call.add_chain(egress_chain), mock.call.add_rule(egress_chain, invalid_rule), mock.call.add_rule(egress_chain, est_rule), mock.call.add_rule('FORWARD', '-o qr-+ -j %s-fwaas-defau' % bname), mock.call.add_rule('FORWARD', '-i qr-+ -j %s-fwaas-defau' % bname)] if ip_version == 4: v4filter_inst = apply_list[0].iptables_manager.ipv4['filter'] v4filter_inst.assert_has_calls(calls) else: v6filter_inst = apply_list[0].iptables_manager.ipv6['filter'] v6filter_inst.assert_has_calls(calls) def test_create_firewall_with_rules(self): self._setup_firewall_with_rules(self.firewall.create_firewall) def test_create_firewall_with_rules_without_distributed_attr(self): self._setup_firewall_with_rules(self.firewall.create_firewall, distributed=None) def test_create_firewall_with_rules_two_routers(self): self._setup_firewall_with_rules(self.firewall.create_firewall, router_count=2) def test_update_firewall_with_rules(self): self._setup_firewall_with_rules(self.firewall.update_firewall) def test_update_firewall_with_rules_without_distributed_attr(self): self._setup_firewall_with_rules(self.firewall.update_firewall, distributed=None) def _test_delete_firewall(self, distributed=False): apply_list = self._fake_apply_list(distributed=distributed) firewall = self._fake_firewall_no_rule() self.firewall.delete_firewall('legacy', apply_list, firewall) ingress_chain = 'iv4%s' % firewall['id'] egress_chain = 'ov4%s' % firewall['id'] calls = [mock.call.remove_chain(ingress_chain), mock.call.remove_chain(egress_chain), mock.call.remove_chain('fwaas-default-policy')] apply_list[0].iptables_manager.ipv4['filter'].assert_has_calls(calls) def test_delete_firewall(self): self._test_delete_firewall() def test_delete_firewall_without_distributed_attr(self): self._test_delete_firewall(distributed=None) def test_create_firewall_with_admin_down(self): apply_list = self._fake_apply_list() rule_list = self._fake_rules_v4(FAKE_FW_ID, apply_list) firewall = self._fake_firewall_with_admin_down(rule_list) self.firewall.create_firewall('legacy', apply_list, firewall) calls = [mock.call.remove_chain('iv4fake-fw-uuid'), mock.call.remove_chain('ov4fake-fw-uuid'), mock.call.remove_chain('fwaas-default-policy'), mock.call.add_chain('fwaas-default-policy'), mock.call.add_rule('fwaas-default-policy', '-j DROP')] apply_list[0].iptables_manager.ipv4['filter'].assert_has_calls(calls) def test_create_firewall_with_rules_dvr_snat(self): self._setup_firewall_with_rules(self.firewall.create_firewall, distributed=True, distributed_mode='dvr_snat') def test_update_firewall_with_rules_dvr_snat(self): self._setup_firewall_with_rules(self.firewall.update_firewall, distributed=True, distributed_mode='dvr_snat') def test_create_firewall_with_rules_dvr(self): self._setup_firewall_with_rules(self.firewall.create_firewall, distributed=True, distributed_mode='dvr') def test_update_firewall_with_rules_dvr(self): self._setup_firewall_with_rules(self.firewall.update_firewall, distributed=True, distributed_mode='dvr') def test_remove_conntrack_new_firewall(self): apply_list = self._fake_apply_list() firewall = self._fake_firewall_no_rule() self.firewall.create_firewall(FW_LEGACY, apply_list, firewall) for router_info_inst in apply_list: namespace = router_info_inst.iptables_manager.namespace calls = [mock.call(namespace)] self.conntrack_driver.flush_entries.assert_has_calls(calls) def test_remove_conntrack_inserted_rule(self): apply_list = self._fake_apply_list() rule_list = self._fake_rules_v4(FAKE_FW_ID, apply_list) firewall = self._fake_firewall(rule_list) self.firewall.create_firewall(FW_LEGACY, apply_list, firewall) self.firewall.pre_firewall = dict(firewall) insert_rule = {'enabled': True, 'action': 'deny', 'ip_version': 4, 'protocol': 'icmp', 'id': 'fake-fw-rule'} rule_list.insert(2, insert_rule) firewall = self._fake_firewall(rule_list) self.firewall.update_firewall(FW_LEGACY, apply_list, firewall) rules_changed = [ {'destination_port': '23', 'position': '2', 'protocol': 'tcp', 'ip_version': 4, 'enabled': True, 'action': 'reject', 'id': 'fake-fw-rule3'}, {'destination_port': '23', 'position': '3', 'protocol': 'tcp', 'ip_version': 4, 'enabled': True, 'action': 'reject', 'id': 'fake-fw-rule3'} ] rules_inserted = [ {'id': 'fake-fw-rule', 'protocol': 'icmp', 'ip_version': 4, 'enabled': True, 'action': 'deny', 'position': '2'} ] for router_info_inst in apply_list: namespace = router_info_inst.iptables_manager.namespace self.conntrack_driver.delete_entries.assert_called_once_with( rules_changed + rules_inserted, namespace ) def test_remove_conntrack_removed_rule(self): apply_list = self._fake_apply_list() rule_list = self._fake_rules_v4(FAKE_FW_ID, apply_list) firewall = self._fake_firewall(rule_list) self.firewall.create_firewall(FW_LEGACY, apply_list, firewall) self.firewall.pre_firewall = dict(firewall) remove_rule = rule_list[1] rule_list.remove(remove_rule) firewall = self._fake_firewall(rule_list) self.firewall.update_firewall(FW_LEGACY, apply_list, firewall) rules_changed = [ {'destination_port': '23', 'position': '2', 'protocol': 'tcp', 'ip_version': 4, 'enabled': True, 'action': 'reject', 'id': 'fake-fw-rule3'}, {'destination_port': '23', 'position': '1', 'protocol': 'tcp', 'ip_version': 4, 'enabled': True, 'action': 'reject', 'id': 'fake-fw-rule3'} ] rules_removed = [ {'enabled': True, 'position': '1', 'protocol': 'tcp', 'id': 'fake-fw-rule2', 'ip_version': 4, 'action': 'deny', 'destination_port': '22'} ] for router_info_inst in apply_list: namespace = router_info_inst.iptables_manager.namespace self.conntrack_driver.delete_entries.assert_called_once_with( rules_changed + rules_removed, namespace ) def test_remove_conntrack_changed_rule(self): apply_list = self._fake_apply_list() rule_list = self._fake_rules_v4(FAKE_FW_ID, apply_list) firewall = self._fake_firewall(rule_list) self.firewall.create_firewall(FW_LEGACY, apply_list, firewall) income_rule = {'enabled': True, 'action': 'deny', 'ip_version': 4, 'protocol': 'tcp', 'id': 'fake-fw-rule3'} rule_list[2] = income_rule firewall = self._fake_firewall(rule_list) self.firewall.update_firewall(FW_LEGACY, apply_list, firewall) rules_changed = [ {'id': 'fake-fw-rule3', 'enabled': True, 'action': 'reject', 'position': '2', 'destination_port': '23', 'ip_version': 4, 'protocol': 'tcp'}, {'position': '2', 'enabled': True, 'action': 'deny', 'id': 'fake-fw-rule3', 'ip_version': 4, 'protocol': 'tcp'} ] for router_info_inst in apply_list: namespace = router_info_inst.iptables_manager.namespace self.conntrack_driver.delete_entries.assert_called_once_with( rules_changed, namespace ) neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/drivers/linux/l2/0000775000175000017500000000000013555600005030164 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000014700000000000011217 Lustar 00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/drivers/linux/l2/openvswitch_firewall/neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/drivers/linux/l2/openvswitch_firewal0000775000175000017500000000000013555600005034167 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000016400000000000011216 Lustar 00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/drivers/linux/l2/openvswitch_firewall/test_rules.pyneutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/drivers/linux/l2/openvswitch_firewal0000664000175000017500000003116313555577713034220 0ustar zuulzuul00000000000000# 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. import mock from neutron_lib import constants from neutron.common import constants as n_const from neutron.tests import base from neutron_fwaas.services.firewall.drivers.linux.l2.openvswitch_firewall \ import constants as fwaas_ovs_consts from neutron_fwaas.services.firewall.drivers.linux.l2.openvswitch_firewall \ import firewall as ovsfw from neutron_fwaas.services.firewall.drivers.linux.l2.openvswitch_firewall \ import rules TESTING_VLAN_TAG = 1 class TestIsValidPrefix(base.BaseTestCase): def test_valid_prefix_ipv4(self): is_valid = rules.is_valid_prefix('10.0.0.0/0') self.assertTrue(is_valid) def test_invalid_prefix_ipv4(self): is_valid = rules.is_valid_prefix('0.0.0.0/0') self.assertFalse(is_valid) def test_valid_prefix_ipv6(self): is_valid = rules.is_valid_prefix('ffff::0/0') self.assertTrue(is_valid) def test_invalid_prefix_ipv6(self): is_valid = rules.is_valid_prefix('0000:0::0/0') self.assertFalse(is_valid) is_valid = rules.is_valid_prefix('::0/0') self.assertFalse(is_valid) is_valid = rules.is_valid_prefix('::/0') self.assertFalse(is_valid) class TestCreateFlowsFromRuleAndPort(base.BaseTestCase): def setUp(self): super(TestCreateFlowsFromRuleAndPort, self).setUp() ovs_port = mock.Mock(vif_mac='00:00:00:00:00:00') ovs_port.ofport = 1 port_dict = {'device': 'port_id'} self.port = ovsfw.OFPort( port_dict, ovs_port, vlan_tag=TESTING_VLAN_TAG) self.create_flows_mock = mock.patch.object( rules, 'create_protocol_flows').start() @property def passed_flow_template(self): return self.create_flows_mock.call_args[0][1] def _test_create_flows_from_rule_and_port_helper( self, rule, expected_template): rules.create_flows_from_rule_and_port(rule, self.port) self.assertEqual(expected_template, self.passed_flow_template) def test_create_flows_from_rule_and_port_no_ip_ipv4(self): rule = { 'ethertype': constants.IPv4, 'direction': constants.INGRESS_DIRECTION, } expected_template = { 'priority': 70, 'dl_type': n_const.ETHERTYPE_IP, 'reg_port': self.port.ofport, } self._test_create_flows_from_rule_and_port_helper(rule, expected_template) def test_create_flows_from_rule_and_port_src_and_dst_ipv4(self): rule = { 'ethertype': constants.IPv4, 'direction': constants.INGRESS_DIRECTION, 'source_ip_prefix': '192.168.0.0/24', 'dest_ip_prefix': '10.0.0.1/32', } expected_template = { 'priority': 70, 'dl_type': n_const.ETHERTYPE_IP, 'reg_port': self.port.ofport, 'nw_src': '192.168.0.0/24', 'nw_dst': '10.0.0.1/32', } self._test_create_flows_from_rule_and_port_helper(rule, expected_template) def test_create_flows_from_rule_and_port_src_and_dst_with_zero_ipv4(self): rule = { 'ethertype': constants.IPv4, 'direction': constants.INGRESS_DIRECTION, 'source_ip_prefix': '192.168.0.0/24', 'dest_ip_prefix': '0.0.0.0/0', } expected_template = { 'priority': 70, 'dl_type': n_const.ETHERTYPE_IP, 'reg_port': self.port.ofport, 'nw_src': '192.168.0.0/24', } self._test_create_flows_from_rule_and_port_helper(rule, expected_template) def test_create_flows_from_rule_and_port_no_ip_ipv6(self): rule = { 'ethertype': constants.IPv6, 'direction': constants.INGRESS_DIRECTION, } expected_template = { 'priority': 70, 'dl_type': n_const.ETHERTYPE_IPV6, 'reg_port': self.port.ofport, } self._test_create_flows_from_rule_and_port_helper(rule, expected_template) def test_create_flows_from_rule_and_port_src_and_dst_ipv6(self): rule = { 'ethertype': constants.IPv6, 'direction': constants.INGRESS_DIRECTION, 'source_ip_prefix': '2001:db8:bbbb::1/64', 'dest_ip_prefix': '2001:db8:aaaa::1/64', } expected_template = { 'priority': 70, 'dl_type': n_const.ETHERTYPE_IPV6, 'reg_port': self.port.ofport, 'ipv6_src': '2001:db8:bbbb::1/64', 'ipv6_dst': '2001:db8:aaaa::1/64', } self._test_create_flows_from_rule_and_port_helper(rule, expected_template) def test_create_flows_from_rule_and_port_src_and_dst_with_zero_ipv6(self): rule = { 'ethertype': constants.IPv6, 'direction': constants.INGRESS_DIRECTION, 'source_ip_prefix': '2001:db8:bbbb::1/64', 'dest_ip_prefix': '::/0', } expected_template = { 'priority': 70, 'dl_type': n_const.ETHERTYPE_IPV6, 'reg_port': self.port.ofport, 'ipv6_src': '2001:db8:bbbb::1/64', } self._test_create_flows_from_rule_and_port_helper(rule, expected_template) class TestCreateProtocolFlows(base.BaseTestCase): def setUp(self): super(TestCreateProtocolFlows, self).setUp() ovs_port = mock.Mock(vif_mac='00:00:00:00:00:00') ovs_port.ofport = 1 port_dict = {'device': 'port_id'} self.port = ovsfw.OFPort( port_dict, ovs_port, vlan_tag=TESTING_VLAN_TAG) def _test_create_protocol_flows_helper(self, direction, rule, expected_flows): flow_template = {'some_settings': 'foo'} for flow in expected_flows: flow.update(flow_template) flows = rules.create_protocol_flows( direction, flow_template, self.port, rule) self.assertEqual(expected_flows, flows) def test_create_protocol_flows_ingress(self): rule = {'protocol': constants.PROTO_NUM_TCP} expected_flows = [{ 'table': fwaas_ovs_consts.FW_RULES_INGRESS_TABLE, 'actions': 'output:1', 'nw_proto': constants.PROTO_NUM_TCP }] self._test_create_protocol_flows_helper( constants.INGRESS_DIRECTION, rule, expected_flows) def test_create_protocol_flows_egress(self): rule = {'protocol': constants.PROTO_NUM_TCP} expected_flows = [{ 'table': fwaas_ovs_consts.FW_RULES_EGRESS_TABLE, 'actions': 'resubmit(,{:d})'.format( fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE), 'nw_proto': constants.PROTO_NUM_TCP, }] self._test_create_protocol_flows_helper( constants.EGRESS_DIRECTION, rule, expected_flows) def test_create_protocol_flows_no_protocol(self): rule = {} expected_flows = [{ 'table': fwaas_ovs_consts.FW_RULES_EGRESS_TABLE, 'actions': 'resubmit(,{:d})'.format( fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE), }] self._test_create_protocol_flows_helper( constants.EGRESS_DIRECTION, rule, expected_flows) def test_create_protocol_flows_icmp6(self): rule = {'ethertype': constants.IPv6, 'protocol': constants.PROTO_NUM_IPV6_ICMP} expected_flows = [{ 'table': fwaas_ovs_consts.FW_RULES_EGRESS_TABLE, 'actions': 'resubmit(,{:d})'.format( fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE), 'nw_proto': constants.PROTO_NUM_IPV6_ICMP, }] self._test_create_protocol_flows_helper( constants.EGRESS_DIRECTION, rule, expected_flows) def test_create_protocol_flows_port_range(self): rule = {'ethertype': constants.IPv4, 'protocol': constants.PROTO_NUM_TCP, 'port_range_min': 22, 'port_range_max': 23} expected_flows = [{ 'table': fwaas_ovs_consts.FW_RULES_EGRESS_TABLE, 'actions': 'resubmit(,{:d})'.format( fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE), 'nw_proto': constants.PROTO_NUM_TCP, 'tcp_dst': '0x0016/0xfffe' }] self._test_create_protocol_flows_helper( constants.EGRESS_DIRECTION, rule, expected_flows) def test_create_protocol_flows_icmp(self): rule = {'ethertype': constants.IPv4, 'protocol': constants.PROTO_NUM_ICMP, 'port_range_min': 0} expected_flows = [{ 'table': fwaas_ovs_consts.FW_RULES_EGRESS_TABLE, 'actions': 'resubmit(,{:d})'.format( fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE), 'nw_proto': constants.PROTO_NUM_ICMP, 'icmp_type': 0 }] self._test_create_protocol_flows_helper( constants.EGRESS_DIRECTION, rule, expected_flows) def test_create_protocol_flows_ipv6_icmp(self): rule = {'ethertype': constants.IPv6, 'protocol': constants.PROTO_NUM_IPV6_ICMP, 'port_range_min': 5, 'port_range_max': 0} expected_flows = [{ 'table': fwaas_ovs_consts.FW_RULES_EGRESS_TABLE, 'actions': 'resubmit(,{:d})'.format( fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE), 'nw_proto': constants.PROTO_NUM_IPV6_ICMP, 'icmp_type': 5, 'icmp_code': 0, }] self._test_create_protocol_flows_helper( constants.EGRESS_DIRECTION, rule, expected_flows) class TestCreatePortRangeFlows(base.BaseTestCase): def _test_create_port_range_flows_helper(self, expected_flows, rule): flow_template = {'some_settings': 'foo'} for flow in expected_flows: flow.update(flow_template) port_range_flows = rules.create_port_range_flows(flow_template, rule) self.assertEqual(expected_flows, port_range_flows) def test_create_port_range_flows_with_source_and_destination(self): rule = { 'protocol': constants.PROTO_NUM_TCP, 'source_port_range_min': 123, 'source_port_range_max': 124, 'port_range_min': 10, 'port_range_max': 11, } expected_flows = [ {'tcp_src': '0x007b', 'tcp_dst': '0x000a/0xfffe'}, {'tcp_src': '0x007c', 'tcp_dst': '0x000a/0xfffe'}, ] self._test_create_port_range_flows_helper(expected_flows, rule) def test_create_port_range_flows_with_source(self): rule = { 'protocol': constants.PROTO_NUM_TCP, 'source_port_range_min': 123, 'source_port_range_max': 124, } expected_flows = [ {'tcp_src': '0x007b'}, {'tcp_src': '0x007c'}, ] self._test_create_port_range_flows_helper(expected_flows, rule) def test_create_port_range_flows_with_destination(self): rule = { 'protocol': constants.PROTO_NUM_TCP, 'port_range_min': 10, 'port_range_max': 11, } expected_flows = [ {'tcp_dst': '0x000a/0xfffe'}, ] self._test_create_port_range_flows_helper(expected_flows, rule) def test_create_port_range_flows_without_port_range(self): rule = { 'protocol': constants.PROTO_NUM_TCP, } expected_flows = [] self._test_create_port_range_flows_helper(expected_flows, rule) def test_create_port_range_with_icmp_protocol(self): # NOTE: such call is prevented by create_protocols_flows rule = { 'protocol': constants.PROTO_NUM_ICMP, 'port_range_min': 10, 'port_range_max': 11, } expected_flows = [] self._test_create_port_range_flows_helper(expected_flows, rule) ././@LongLink0000000000000000000000000000016200000000000011214 Lustar 00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/drivers/linux/l2/openvswitch_firewall/__init__.pyneutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/drivers/linux/l2/openvswitch_firewal0000664000175000017500000000000013555577713034202 0ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000016700000000000011221 Lustar 00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/drivers/linux/l2/openvswitch_firewall/test_firewall.pyneutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/drivers/linux/l2/openvswitch_firewal0000664000175000017500000007153313555577713034225 0ustar zuulzuul00000000000000# Copyright 2017 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 mock from neutron_lib import constants import testtools from neutron.agent.common import ovs_lib from neutron.common import constants as n_const from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants \ as ovs_consts from neutron.tests import base from neutron_fwaas.services.firewall.drivers.linux.l2.openvswitch_firewall \ import constants as fwaas_ovs_consts from neutron_fwaas.services.firewall.drivers.linux.l2.openvswitch_firewall \ import exceptions from neutron_fwaas.services.firewall.drivers.linux.l2.openvswitch_firewall \ import firewall as ovsfw TESTING_VLAN_TAG = 1 def create_ofport(port_dict): ovs_port = mock.Mock(vif_mac='00:00:00:00:00:00', ofport=1, port_name="port-name") return ovsfw.OFPort(port_dict, ovs_port, vlan_tag=TESTING_VLAN_TAG) class TestCreateRegNumbers(base.BaseTestCase): def test_no_registers_defined(self): flow = {'foo': 'bar'} ovsfw.create_reg_numbers(flow) self.assertEqual({'foo': 'bar'}, flow) def test_both_registers_defined(self): flow = {'foo': 'bar', 'reg_port': 1, 'reg_net': 2} expected_flow = {'foo': 'bar', 'reg{:d}'.format(fwaas_ovs_consts.REG_PORT): 1, 'reg{:d}'.format(fwaas_ovs_consts.REG_NET): 2} ovsfw.create_reg_numbers(flow) self.assertEqual(expected_flow, flow) class TestFirewallGroup(base.BaseTestCase): def setUp(self): super(TestFirewallGroup, self).setUp() self.fwg = ovsfw.FirewallGroup('123') self.fwg.members = {'type': [1, 2, 3, 4]} def test_update_rules(self): ingress_rules = [{'foo-ingress': 'bar', 'rule': 'all'}, {'bar-ingress': 'foo'}] egress_rules = [{'foo-egress': '123456'}, {'bar-egress': 'bar'}] self.fwg.update_rules(ingress_rules, egress_rules) self.assertEqual(ingress_rules, self.fwg.ingress_rules) self.assertEqual(egress_rules, self.fwg.egress_rules) def test_update_rules_protocols(self): # XXX FIXME(ivasilevskaya) figure out what this test does and fix # appropriately # leaving failing as it may be important rules = [ {'foo': 'bar', 'protocol': constants.PROTO_NAME_ICMP, 'ethertype': constants.IPv4}, {'foo': 'bar', 'protocol': constants.PROTO_NAME_ICMP, 'ethertype': constants.IPv6}, {'foo': 'bar', 'protocol': constants.PROTO_NAME_IPV6_ICMP_LEGACY, 'ethertype': constants.IPv6}, {'foo': 'bar', 'protocol': constants.PROTO_NAME_TCP}, {'foo': 'bar', 'protocol': '94'}, {'foo': 'bar', 'protocol': 'baz'}, {'foo': 'no_proto'}] self.fwg.update_rules(rules, []) self.assertEqual({'foo': 'no_proto'}, self.fwg.ingress_rules.pop()) protos = [rule['protocol'] for rule in self.fwg.ingress_rules] self.assertEqual([constants.PROTO_NUM_ICMP, constants.PROTO_NUM_IPV6_ICMP, constants.PROTO_NUM_IPV6_ICMP, constants.PROTO_NUM_TCP, 94, 'baz'], protos) def test_get_ethertype_filtered_addresses(self): addresses = self.fwg.get_ethertype_filtered_addresses('type') expected_addresses = [1, 2, 3, 4] self.assertEqual(expected_addresses, addresses) class TestOFPort(base.BaseTestCase): def setUp(self): super(TestOFPort, self).setUp() self.ipv4_addresses = ['10.0.0.1', '192.168.0.1'] self.ipv6_addresses = ['fe80::f816:3eff:fe2e:1'] port_dict = {'device': 1, 'fixed_ips': [ {'subnet_id': 's_%s' % ip, 'ip_address': ip} for ip in self.ipv4_addresses + self.ipv6_addresses]} self.port = create_ofport(port_dict) def test_ipv4_address(self): ipv4_addresses = self.port.ipv4_addresses self.assertEqual(self.ipv4_addresses, ipv4_addresses) def test_ipv6_address(self): ipv6_addresses = self.port.ipv6_addresses self.assertEqual(self.ipv6_addresses, ipv6_addresses) def test__get_allowed_pairs(self): port = { 'allowed_address_pairs': [ {'mac_address': 'foo', 'ip_address': '10.0.0.1'}, {'mac_address': 'bar', 'ip_address': '192.168.0.1'}, {'mac_address': 'qux', 'ip_address': '169.254.0.0/16'}, {'mac_address': 'baz', 'ip_address': '2003::f'}, ]} allowed_pairs_v4 = ovsfw.OFPort._get_allowed_pairs(port, version=4) allowed_pairs_v6 = ovsfw.OFPort._get_allowed_pairs(port, version=6) expected_aap_v4 = {('foo', '10.0.0.1'), ('bar', '192.168.0.1'), ('qux', '169.254.0.0/16')} expected_aap_v6 = {('baz', '2003::f')} self.assertEqual(expected_aap_v4, allowed_pairs_v4) self.assertEqual(expected_aap_v6, allowed_pairs_v6) def test__get_allowed_pairs_empty(self): port = {} allowed_pairs = ovsfw.OFPort._get_allowed_pairs(port, version=4) self.assertFalse(allowed_pairs) def test_update(self): old_port_dict = self.port.neutron_port_dict new_port_dict = old_port_dict.copy() added_ips = [1, 2, 3] new_port_dict.update({ 'fixed_ips': added_ips, 'allowed_address_pairs': [ {'mac_address': '00:00:00:00:00:01', 'ip_address': '192.168.0.1'}, {'mac_address': '00:00:00:00:00:01', 'ip_address': '2003::f'}], }) self.port.update(new_port_dict) self.assertEqual(new_port_dict, self.port.neutron_port_dict) self.assertIsNot(new_port_dict, self.port.neutron_port_dict) self.assertEqual(added_ips, self.port.fixed_ips) self.assertEqual({('00:00:00:00:00:01', '192.168.0.1')}, self.port.allowed_pairs_v4) self.assertIn(('00:00:00:00:00:01', '2003::f'), self.port.allowed_pairs_v6) class TestFWGPortMap(base.BaseTestCase): def setUp(self): super(TestFWGPortMap, self).setUp() self.map = ovsfw.FWGPortMap() def test_get_or_create_fwg_existing_fwg(self): self.map.fw_groups['id'] = mock.sentinel fwg = self.map.get_or_create_fwg('id') self.assertIs(mock.sentinel, fwg) def test_get_or_create_fwg_nonexisting_fwg(self): with mock.patch.object(ovsfw, 'FirewallGroup') as fwg_mock: fwg = self.map.get_or_create_fwg('id') self.assertEqual(fwg_mock.return_value, fwg) def _check_port(self, port_id, expected_id): port = self.map.ports[port_id] expected_fwg = self.map.fw_groups[expected_id] self.assertEqual(expected_fwg, port.fw_group) def _check_fwg(self, fwg_id, expected_port_ids): fwg = self.map.fw_groups[fwg_id] expected_ports = {self.map.ports[port_id] for port_id in expected_port_ids} self.assertEqual(expected_ports, fwg.ports) def _create_ports_and_fwgs(self): fwg_1 = ovsfw.FirewallGroup(1) fwg_2 = ovsfw.FirewallGroup(2) fwg_3 = ovsfw.FirewallGroup(3) port_a = create_ofport({'device': 'a'}) port_b = create_ofport({'device': 'b'}) port_c = create_ofport({'device': 'c'}) self.map.ports = {'a': port_a, 'b': port_b, 'c': port_c} self.map.fw_groups = {1: fwg_1, 2: fwg_2, 3: fwg_3} # XXX FIXME(ivasilevskaya) see note for OFPORT port_a.fw_group = fwg_1 port_b.fw_group = fwg_2 port_c.fw_group = fwg_2 fwg_1.ports = {port_a} fwg_2.ports = {port_b, port_c} def test_create_port(self): """Create a port and assign it to firewall group It is implied that 1 port can be assigned to one firewall group only """ port = create_ofport({'device': 'a'}) port_dict = {'some-port-attributes-go-here': 42, 'firewall_group': 1} self.map.create_port(port, port_dict) self._check_port('a', 1) self._check_fwg(1, ['a']) def test_update_port_another_fwg_added(self): """Update a port with new firewall group id It is implied that 1 port can be assigned to one firewall group only """ self._create_ports_and_fwgs() self._check_port('b', 2) port_dict = {'firewall_group': 3} self.map.update_port(self.map.ports['b'], port_dict) self._check_port('a', 1) self._check_port('b', 3) self._check_port('c', 2) self._check_fwg(1, ['a']) self._check_fwg(2, ['c']) self._check_fwg(3, ['b']) def test_remove_port(self): self._create_ports_and_fwgs() self.map.remove_port(self.map.ports['c']) self._check_port('b', 2) self._check_fwg(1, ['a']) self._check_fwg(2, ['b']) self.assertNotIn('c', self.map.ports) def test_update_rules(self): """Just make sure it doesn't crash""" self.map.update_rules(42, [], []) def test_update_members(self): """Just make sure it doesn't crash""" self.map.update_members(42, []) class FakeOVSPort(object): def __init__(self, name, port, mac): self.port_name = name self.ofport = port self.vif_mac = mac class TestOVSFirewallDriver(base.BaseTestCase): def setUp(self): super(TestOVSFirewallDriver, self).setUp() mock_bridge = mock.patch.object( ovs_lib, 'OVSBridge', autospec=True).start() self.firewall = ovsfw.OVSFirewallDriver(mock_bridge) self.mock_bridge = self.firewall.int_br self.mock_bridge.reset_mock() self.fake_ovs_port = FakeOVSPort('port', 1, '00:00:00:00:00:00') self.mock_bridge.br.get_vif_port_by_id.return_value = \ self.fake_ovs_port def _prepare_firewall_group(self): ingress_rules = [ {'position': '1', 'protocol': 'tcp', 'ip_version': 4, 'destination_port': '123', 'enabled': True, 'action': 'allow', 'id': 'fake-fw-rule1'} ] egress_rules = [ {'position': '2', 'protocol': 'udp', 'ip_version': 4, 'enabled': True, 'action': 'allow', 'id': 'fake-fw-rule2'}, {'position': '3', 'protocol': 'tcp', 'ip_version': 6, 'enabled': True, 'action': 'allow', 'id': 'fake-fw-rule3'}] self.firewall.update_firewall_group_rules(1, ingress_rules, []) self.firewall.update_firewall_group_rules(2, [], egress_rules) @property def port_ofport(self): return self.mock_bridge.br.get_vif_port_by_id.return_value.ofport @property def port_mac(self): return self.mock_bridge.br.get_vif_port_by_id.return_value.vif_mac def test_initialize_bridge(self): br = self.firewall.initialize_bridge(self.mock_bridge) self.assertEqual(br, self.mock_bridge.deferred.return_value) def test__add_flow_dl_type_formatted_to_string(self): dl_type = 0x0800 self.firewall._add_flow(dl_type=dl_type) def test__add_flow_registers_are_replaced(self): self.firewall._add_flow(in_port=1, reg_port=1, reg_net=2) expected_calls = {'in_port': 1, 'reg{:d}'.format(fwaas_ovs_consts.REG_PORT): 1, 'reg{:d}'.format(fwaas_ovs_consts.REG_NET): 2} self.mock_bridge.br.add_flow.assert_called_once_with( **expected_calls) def test__drop_all_unmatched_flows(self): self.firewall._drop_all_unmatched_flows() expected_calls = [ mock.call(actions='drop', priority=0, table=fwaas_ovs_consts.FW_BASE_EGRESS_TABLE), mock.call(actions='drop', priority=0, table=fwaas_ovs_consts.FW_RULES_EGRESS_TABLE), mock.call(actions='drop', priority=0, table=fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE), mock.call(actions='drop', priority=0, table=fwaas_ovs_consts.FW_BASE_INGRESS_TABLE), mock.call(actions='drop', priority=0, table=fwaas_ovs_consts.FW_RULES_INGRESS_TABLE)] actual_calls = self.firewall.int_br.br.add_flow.call_args_list self.assertEqual(expected_calls, actual_calls) def test_get_or_create_ofport_non_existing(self): port_dict = { 'device': 'port-id', 'firewall_group': 123, 'lvlan': TESTING_VLAN_TAG, } port = self.firewall.get_or_create_ofport(port_dict) port_dict = { 'device': 'port-id', 'firewall_group': 456, 'lvlan': TESTING_VLAN_TAG, } port = self.firewall.get_or_create_ofport(port_dict) sg1, sg2 = sorted( self.firewall.fwg_port_map.fw_groups.values(), key=lambda x: x.id) self.assertIn(port, self.firewall.fwg_port_map.ports.values()) self.assertEqual(port.fw_group, sg2) self.assertEqual(set(), sg1.ports) self.assertIn(port, sg2.ports) def test_get_or_create_ofport_existing(self): port_dict = { 'device': 'port-id', 'firewall_group': 123} of_port = create_ofport(port_dict) self.firewall.fwg_port_map.ports[of_port.id] = of_port port = self.firewall.get_or_create_ofport(port_dict) [sg1] = sorted(self.firewall.fwg_port_map.fw_groups.values(), key=lambda x: x.id) self.assertIs(of_port, port) self.assertIn(port, self.firewall.fwg_port_map.ports.values()) self.assertEqual(port.fw_group, sg1) self.assertIn(port, sg1.ports) def test_get_or_create_ofport_changed(self): port_dict = { 'device': 'port-id', 'firewall_group': 123} of_port = create_ofport(port_dict) self.firewall.fwg_port_map.ports[of_port.id] = of_port fake_ovs_port = FakeOVSPort('port', 2, '00:00:00:00:00:00') self.mock_bridge.br.get_vif_port_by_id.return_value = \ fake_ovs_port port = self.firewall.get_or_create_ofport(port_dict) self.assertEqual(port.ofport, 2) def test_get_or_create_ofport_missing(self): port_dict = { 'device': 'port-id', 'firewall_group': 123} self.mock_bridge.br.get_vif_port_by_id.return_value = None with testtools.ExpectedException(exceptions.OVSFWaaSPortNotFound): self.firewall.get_or_create_ofport(port_dict) def test_get_or_create_ofport_missing_nocreate(self): port_dict = { 'device': 'port-id', 'firewall_group': 123} self.mock_bridge.br.get_vif_port_by_id.return_value = None self.assertIsNone(self.firewall.get_ofport(port_dict)) self.assertFalse(self.mock_bridge.br.get_vif_port_by_id.called) def test_is_port_managed_managed_port(self): port_dict = {'device': 'port-id'} self.firewall.fwg_port_map.ports[port_dict['device']] = object() is_managed = self.firewall.is_port_managed(port_dict) self.assertTrue(is_managed) def test_is_port_managed_not_managed_port(self): port_dict = {'device': 'port-id'} is_managed = self.firewall.is_port_managed(port_dict) self.assertFalse(is_managed) def test_prepare_port_filter(self): port_dict = {'device': 'port-id', 'firewall_group': 1, 'fixed_ips': [{'subnet_id': "some_subnet_id_here", 'ip_address': "10.0.0.1"}], 'lvlan': TESTING_VLAN_TAG} self._prepare_firewall_group() self.firewall.prepare_port_filter(port_dict) exp_egress_classifier = mock.call( actions='set_field:{:d}->reg5,set_field:{:d}->reg6,' 'resubmit(,{:d})'.format( self.port_ofport, TESTING_VLAN_TAG, fwaas_ovs_consts.FW_BASE_EGRESS_TABLE), in_port=self.port_ofport, priority=105, table=ovs_consts.TRANSIENT_TABLE) exp_ingress_classifier = mock.call( actions='set_field:{:d}->reg5,set_field:{:d}->reg6,' 'strip_vlan,resubmit(,{:d})'.format( self.port_ofport, TESTING_VLAN_TAG, fwaas_ovs_consts.FW_BASE_INGRESS_TABLE), dl_dst=self.port_mac, dl_vlan='0x%x' % TESTING_VLAN_TAG, priority=95, table=ovs_consts.TRANSIENT_TABLE) filter_rule = mock.call( actions='ct(commit,zone=NXM_NX_REG6[0..15]),' 'output:{:d},resubmit(,{:d})'.format( self.port_ofport, ovs_consts.ACCEPTED_INGRESS_TRAFFIC_TABLE), dl_type="0x{:04x}".format(n_const.ETHERTYPE_IP), nw_proto=constants.PROTO_NUM_TCP, priority=70, reg5=self.port_ofport, ct_state=fwaas_ovs_consts.OF_STATE_NEW_NOT_ESTABLISHED, table=fwaas_ovs_consts.FW_RULES_INGRESS_TABLE, tcp_dst='0x007b') calls = self.mock_bridge.br.add_flow.call_args_list for call in exp_ingress_classifier, exp_egress_classifier, filter_rule: self.assertIn(call, calls) def test_prepare_port_filter_in_coexistence_mode(self): port_dict = {'device': 'port-id', 'firewall_group': 1, 'fixed_ips': [{'subnet_id': "some_subnet_id_here", 'ip_address': "10.0.0.1"}], 'lvlan': TESTING_VLAN_TAG} self._prepare_firewall_group() self.firewall.sg_with_ovs = True self.firewall.prepare_port_filter(port_dict) exp_egress_classifier = mock.call( actions='set_field:{:d}->reg5,set_field:{:d}->reg6,' 'resubmit(,{:d})'.format( self.port_ofport, TESTING_VLAN_TAG, fwaas_ovs_consts.FW_BASE_EGRESS_TABLE), in_port=self.port_ofport, priority=105, table=ovs_consts.TRANSIENT_TABLE) exp_ingress_classifier = mock.call( actions='set_field:{:d}->reg5,set_field:{:d}->reg6,' 'strip_vlan,resubmit(,{:d})'.format( self.port_ofport, TESTING_VLAN_TAG, fwaas_ovs_consts.FW_BASE_INGRESS_TABLE), dl_dst=self.port_mac, dl_vlan='0x%x' % TESTING_VLAN_TAG, priority=95, table=ovs_consts.TRANSIENT_TABLE) filter_rule = mock.call( actions='resubmit(,{:d})'.format(ovs_consts.RULES_INGRESS_TABLE), dl_type="0x{:04x}".format(n_const.ETHERTYPE_IP), nw_proto=constants.PROTO_NUM_TCP, priority=70, reg5=self.port_ofport, ct_state=fwaas_ovs_consts.OF_STATE_NEW_NOT_ESTABLISHED, table=fwaas_ovs_consts.FW_RULES_INGRESS_TABLE, tcp_dst='0x007b') calls = self.mock_bridge.br.add_flow.call_args_list for call in exp_ingress_classifier, exp_egress_classifier, filter_rule: self.assertIn(call, calls) def test_prepare_port_filter_port_security_disabled(self): port_dict = {'device': 'port-id', 'firewall_group': 1, 'port_security_enabled': False} self._prepare_firewall_group() with mock.patch.object( self.firewall, 'initialize_port_flows') as m_init_flows: self.firewall.prepare_port_filter(port_dict) self.assertFalse(m_init_flows.called) def test_prepare_port_filter_initialized_port(self): port_dict = {'device': 'port-id', 'firewall_group': 1, 'lvlan': TESTING_VLAN_TAG} self._prepare_firewall_group() self.firewall.prepare_port_filter(port_dict) self.assertFalse(self.mock_bridge.br.delete_flows.called) self.firewall.prepare_port_filter(port_dict) self.assertTrue(self.mock_bridge.br.delete_flows.called) def test_update_port_filter(self): port_dict = {'device': 'port-id', 'firewall_group': 1, 'lvlan': TESTING_VLAN_TAG} self._prepare_firewall_group() self.firewall.prepare_port_filter(port_dict) port_dict['firewall_group'] = 2 self.mock_bridge.reset_mock() self.firewall.update_port_filter(port_dict) self.assertTrue(self.mock_bridge.br.delete_flows.called) filter_rules = [ mock.call( actions='resubmit(,{:d})'.format( fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE), dl_type="0x{:04x}".format(n_const.ETHERTYPE_IP), nw_proto=constants.PROTO_NUM_UDP, priority=71, ct_state=fwaas_ovs_consts.OF_STATE_NEW_NOT_ESTABLISHED, reg5=self.port_ofport, table=fwaas_ovs_consts.FW_RULES_EGRESS_TABLE), # XXX FIXME NOTE(ivasilevskaya) this test originally tested that # flows for SG with remote_group=this group were generated with # proper conjunction action. If the original idea that conj_manager # isn't needed for firewall groups proves to be wrong this needs to # be revizited and properly fixed/covered with tests mock.call( actions='resubmit(,{:d})'.format( fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE), ct_state=fwaas_ovs_consts.OF_STATE_ESTABLISHED_NOT_REPLY, dl_type=mock.ANY, nw_proto=6, priority=70, reg5=self.port_ofport, table=fwaas_ovs_consts.FW_RULES_EGRESS_TABLE)] self.mock_bridge.br.add_flow.assert_has_calls(filter_rules, any_order=True) def test_update_port_filter_in_coexistence_mode(self): port_dict = {'device': 'port-id', 'firewall_group': 1, 'lvlan': TESTING_VLAN_TAG} self._prepare_firewall_group() self.firewall.sg_with_ovs = True self.firewall.prepare_port_filter(port_dict) port_dict['firewall_group'] = 2 self.mock_bridge.reset_mock() self.firewall.update_port_filter(port_dict) self.assertTrue(self.mock_bridge.br.delete_flows.called) filter_rules = [ mock.call( actions='resubmit(,{:d})'.format( ovs_consts.RULES_EGRESS_TABLE), dl_type="0x{:04x}".format(n_const.ETHERTYPE_IP), nw_proto=constants.PROTO_NUM_UDP, priority=71, ct_state=fwaas_ovs_consts.OF_STATE_NEW_NOT_ESTABLISHED, reg5=self.port_ofport, table=fwaas_ovs_consts.FW_RULES_EGRESS_TABLE), # XXX FIXME NOTE(ivasilevskaya) this test originally tested that # flows for SG with remote_group=this group were generated with # proper conjunction action. If the original idea that conj_manager # isn't needed for firewall groups proves to be wrong this needs to # be revizited and properly fixed/covered with tests mock.call( actions='resubmit(,{:d})'.format( ovs_consts.RULES_EGRESS_TABLE), ct_state=fwaas_ovs_consts.OF_STATE_ESTABLISHED_NOT_REPLY, dl_type=mock.ANY, nw_proto=6, priority=70, reg5=self.port_ofport, table=fwaas_ovs_consts.FW_RULES_EGRESS_TABLE)] self.mock_bridge.br.add_flow.assert_has_calls(filter_rules, any_order=True) def test_update_port_filter_create_new_port_if_not_present(self): port_dict = {'device': 'port-id', 'firewall_group': 1} self._prepare_firewall_group() with mock.patch.object( self.firewall, 'prepare_port_filter') as prepare_mock: self.firewall.update_port_filter(port_dict) self.assertTrue(prepare_mock.called) def test_update_port_filter_port_security_disabled(self): port_dict = {'device': 'port-id', 'firewall_group': 1, 'lvlan': TESTING_VLAN_TAG} self._prepare_firewall_group() self.firewall.prepare_port_filter(port_dict) port_dict['port_security_enabled'] = False self.firewall.update_port_filter(port_dict) self.assertTrue(self.mock_bridge.br.delete_flows.called) def test_remove_port_filter(self): port_dict = {'device': 'port-id', 'firewall_group': 1, 'lvlan': TESTING_VLAN_TAG} self._prepare_firewall_group() self.firewall.prepare_port_filter(port_dict) self.firewall.remove_port_filter(port_dict) self.assertTrue(self.mock_bridge.br.delete_flows.called) self.assertIn(1, self.firewall.fwg_to_delete) def test_remove_port_filter_port_security_disabled(self): port_dict = {'device': 'port-id', 'firewall_group': 1} self.firewall.remove_port_filter(port_dict) self.assertFalse(self.mock_bridge.br.delete_flows.called) def test_update_firewall_group_rules(self): """Just make sure it doesn't crash""" new_rules_ingress = [ {'ip_version': 4, 'action': 'allow', 'protocol': constants.PROTO_NAME_ICMP}, {'ip_version': 4, 'direction': 'deny'}] self.firewall.update_firewall_group_rules(1, new_rules_ingress, []) def test__cleanup_stale_sg(self): self._prepare_firewall_group() self.firewall.fwg_to_delete = {1} with mock.patch.object(self.firewall.fwg_port_map, 'delete_fwg') as delete_fwg_mock: self.firewall._cleanup_stale_fwg() delete_fwg_mock.assert_called_once_with(1) def test_get_ovs_port(self): ovs_port = self.firewall.get_ovs_port('port_id') self.assertEqual(self.fake_ovs_port, ovs_port) def test_get_ovs_port_non_existent(self): self.mock_bridge.br.get_vif_port_by_id.return_value = None with testtools.ExpectedException(exceptions.OVSFWaaSPortNotFound): self.firewall.get_ovs_port('port_id') def test__initialize_egress_no_port_security_sends_to_egress(self): port_dict = {'device': 'port-id', 'firewall_group': 1, 'lvlan': TESTING_VLAN_TAG} self.firewall._initialize_egress_no_port_security(port_dict) expected_call = mock.call( table=ovs_consts.TRANSIENT_TABLE, priority=100, in_port=self.fake_ovs_port.ofport, actions='set_field:%d->reg%d,' 'set_field:%d->reg%d,' 'resubmit(,%d)' % ( self.fake_ovs_port.ofport, fwaas_ovs_consts.REG_PORT, TESTING_VLAN_TAG, fwaas_ovs_consts.REG_NET, fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE) ) calls = self.mock_bridge.br.add_flow.call_args_list self.assertIn(expected_call, calls) def test__initialize_egress_no_port_security_no_tag(self): port_dict = {'device': 'port-id', 'firewall_group': 1, 'lvlan': None} self.firewall._initialize_egress_no_port_security(port_dict) self.assertFalse(self.mock_bridge.br.add_flow.called) def test__remove_egress_no_port_security_deletes_flow(self): self.mock_bridge.br.db_get_val.return_value = {'tag': TESTING_VLAN_TAG} self.firewall.fwg_port_map.unfiltered['port_id'] = 1 self.firewall._remove_egress_no_port_security('port_id') expected_call = mock.call( table=ovs_consts.TRANSIENT_TABLE, in_port=self.fake_ovs_port.ofport, ) calls = self.mock_bridge.br.delete_flows.call_args_list self.assertIn(expected_call, calls) def test__remove_egress_no_port_security_no_tag(self): self.mock_bridge.br.db_get_val.return_value = {} self.firewall._remove_egress_no_port_security('port_id') self.assertFalse(self.mock_bridge.br.delete_flows.called) neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/drivers/linux/l2/noop/0000775000175000017500000000000013555600005031137 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/drivers/linux/l2/noop/__init__.py0000664000175000017500000000000013555577713033261 0ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000015200000000000011213 Lustar 00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/drivers/linux/l2/noop/test_noop_driver.pyneutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/drivers/linux/l2/noop/test_noop_driv0000664000175000017500000000326313555577713034147 0ustar zuulzuul00000000000000# Copyright 2017 Mirantis 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 mock from neutron import manager from neutron_fwaas.services.firewall.drivers.linux.l2.noop import noop_driver from neutron_fwaas.tests import base class TestNoopDriver(base.BaseTestCase): def setUp(self): super(TestNoopDriver, self).setUp() mock_br = mock.Mock() self.firewall = noop_driver.NoopFirewallL2Driver(mock_br) def test_basic_methods(self): # just make sure it doesn't crash fwg_mock = mock.Mock() self.firewall.create_firewall_group(ports=[], firewall_group=fwg_mock) self.firewall.update_firewall_group(ports=[], firewall_group=fwg_mock) self.firewall.delete_firewall_group(ports=[], firewall_group=fwg_mock) self.firewall.filter_defer_apply_on() self.firewall.filter_defer_apply_off() self.firewall.defer_apply() self.firewall.ports def test_load_firewall_class(self): res = manager.NeutronManager.load_class_for_provider( 'neutron.agent.l2.firewall_drivers', 'noop') self.assertEqual(res, noop_driver.NoopFirewallL2Driver) neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/drivers/linux/l2/__init__.py0000664000175000017500000000000013555577713032306 0ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000015000000000000011211 Lustar 00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/drivers/linux/test_netlink_conntrack.pyneutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/drivers/linux/test_netlink_conntrack0000664000175000017500000002424713555577713034373 0ustar zuulzuul00000000000000# 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 mock from neutron_fwaas.services.firewall.drivers.linux import netlink_conntrack from neutron_fwaas.tests import base FW_RULES = [ {'position': '1', 'protocol': 'icmp', 'ip_version': 4, 'enabled': True, 'action': 'reject', 'id': 'fake-fw-rule1'}, {'source_port': '0:10', 'destination_port': '0:10', 'position': '2', 'protocol': 'tcp', 'ip_version': 4, 'enabled': True, 'action': 'reject', 'id': 'fake-fw-rule2'}, {'source_port': '0:10', 'destination_port': '0:20', 'position': '3', 'protocol': 'udp', 'ip_version': 4, 'enabled': True, 'action': 'reject', 'id': 'fake-fw-rule3'}, {'source_port': None, 'destination_port': '0:10', 'position': '2', 'protocol': 'tcp', 'ip_version': 4, 'enabled': True, 'action': 'reject', 'id': 'fake-fw-rule5'}, {'source_port': '0:10', 'destination_port': None, 'position': '3', 'protocol': 'udp', 'ip_version': 4, 'enabled': True, 'action': 'reject', 'id': 'fake-fw-rule5'}, ] ICMP_ENTRY = (4, 'icmp', 8, 0, '1.1.1.1', '2.2.2.2', '1234') TCP_ENTRY = (4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2') UDP_ENTRY = (4, 'udp', 1, 2, '1.1.1.1', '2.2.2.2') ROUTER_NAMESPACE = 'qrouter-fake-namespace' class ConntrackNetlinkTestCase(base.BaseTestCase): def setUp(self): super(ConntrackNetlinkTestCase, self).setUp() self.conntrack_driver = netlink_conntrack.ConntrackNetlink() self.conntrack_driver.initialize() nl_flush_entries = mock.patch('neutron_fwaas.privileged.' 'netlink_lib.flush_entries') self.flush_entries = nl_flush_entries.start() nl_list_entries = mock.patch('neutron_fwaas.privileged.' 'netlink_lib.list_entries') self.list_entries = nl_list_entries.start() nl_delete_entries = mock.patch('neutron_fwaas.privileged.' 'netlink_lib.delete_entries') self.delete_entries = nl_delete_entries.start() def test_flush_entries(self): self.conntrack_driver.flush_entries(ROUTER_NAMESPACE) self.flush_entries.assert_called_with(ROUTER_NAMESPACE) def test_delete_with_empty_conntrack_entries(self): self.list_entries.return_value = [] self.conntrack_driver.delete_entries([], ROUTER_NAMESPACE) self.list_entries.assert_called_with(ROUTER_NAMESPACE) self.delete_entries.assert_not_called() def test_delete_icmp_entry(self): """Testing delete an icmp entry The icmp entry can be deleted if there is an icmp conntrack entry matched with an icmp firewall rule. The information passing to nl_lib.kill_entry will include: (ipversion, protocol, icmp_type, icmp_code, src_address, dst_addres, icmp_ip) """ self.list_entries.return_value = [ICMP_ENTRY] self.conntrack_driver.delete_entries(FW_RULES, ROUTER_NAMESPACE) self.list_entries.assert_called_with(ROUTER_NAMESPACE) self.delete_entries.assert_called_with([(4, 'icmp', 8, 0, '1.1.1.1', '2.2.2.2', '1234')], ROUTER_NAMESPACE) def test_delete_tcp_entry(self): """Testing delete a tcp entry The tcp entry can be deleted if there is a tcp conntrack entry matched with a tcp firewall rule. The information passing to nl_lib.kill_entry will include: (ipversion, protocol, src_port, dst_port, src_address, dst_addres) """ self.list_entries.return_value = [TCP_ENTRY] self.conntrack_driver.delete_entries(FW_RULES, ROUTER_NAMESPACE) self.list_entries.assert_called_with(ROUTER_NAMESPACE) self.delete_entries.assert_called_with( [(4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2')], ROUTER_NAMESPACE) def test_delete_udp_entry(self): """Testing delete an udp entry The udp entry can be deleted if there is an udp conntrack entry matched with an udp firewall rule. The information passing to nl_lib.kill_entry will include: (ipversion, protocol, src_port, dst_port, src_address, dst_addres) """ self.list_entries.return_value = [UDP_ENTRY] self.conntrack_driver.delete_entries(FW_RULES, ROUTER_NAMESPACE) self.list_entries.assert_called_with(ROUTER_NAMESPACE) self.delete_entries.assert_called_with( [(4, 'udp', 1, 2, '1.1.1.1', '2.2.2.2')], ROUTER_NAMESPACE) def test_delete_multiple_entries(self): self.list_entries.return_value = [ICMP_ENTRY, TCP_ENTRY, UDP_ENTRY] self.conntrack_driver.delete_entries(FW_RULES, ROUTER_NAMESPACE) self.list_entries.assert_called_with(ROUTER_NAMESPACE) self.delete_entries.assert_called_with( [(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'), (4, 'udp', 1, 2, '1.1.1.1', '2.2.2.2')], ROUTER_NAMESPACE) def _test_entry_to_delete(self, rule_filter, entry, expect_result): is_entry_to_delete = ( self.conntrack_driver._compare_entry_and_rule(rule_filter, entry)) self.assertEqual(expect_result, is_entry_to_delete) def test_icmp_entry_match_rule(self): entry = (4, 'icmp', 8, 0, '1.1.1.1', '2.2.2.2', '1234') rule_filter = (4, 'icmp', None, None) self._test_entry_to_delete(rule_filter, entry, 0) def test_tcp_entry_match_rule(self): entry = (4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2') rule_filters = [(4, 'tcp', None, None), (4, 'tcp', [1], None), (4, 'tcp', None, [2]), (4, 'tcp', [1], [2]), (4, 'tcp', ['0', '10'], ['0', '10']), ] for rule_filter in rule_filters: self._test_entry_to_delete(rule_filter, entry, 0) def test_udp_entry_match_rule(self): entry = (4, 'udp', 1, 2, '1.1.1.1', '2.2.2.2') rule_filters = [(4, 'udp', None, None), (4, 'udp', [1], None), (4, 'udp', None, [2]), (4, 'udp', [1], [2]), (4, 'udp', ['0', '10'], ['0', '10']), ] for rule_filter in rule_filters: self._test_entry_to_delete(rule_filter, entry, 0) def test_entry_unmatch_rule(self): wrong_ipv = [(4, 'tcp', '1', '2', '1.1.1.1', '2.2.2.2'), (6, 'tcp', None, None), -1] wrong_proto = [(4, 'tcp', '1', '2', '1.1.1.1', '2.2.2.2'), (4, 'udp', None, None), -1] not_in_sport_range = [(4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2'), (4, 'tcp', ['2', '100'], [2]), -1] not_in_dport_range = [(4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2'), (4, 'tcp', [1], ['3', '100']), -1] for entry, rule_filter, expect in [ wrong_ipv, wrong_proto, not_in_sport_range, not_in_dport_range]: self._test_entry_to_delete(rule_filter, entry, expect) def test_get_filter_from_rules(self): fw_rule_icmp = FW_RULES[0] fw_rule_port_range = FW_RULES[1] fw_rule_dest_port = FW_RULES[3] fw_rule_source_port = FW_RULES[4] # filter format: # ('ip_version', 'protocol', 'source_port', 'destination_port', # 'source_ip_address', 'destination_ip_address') expected_icmp_filter = (4, 'icmp', [], [], [], []) expected_port_range_filter = (4, 'tcp', ['0', '10'], ['0', '10'], [], []) expected_dest_port_filter = (4, 'tcp', [], ['0', '10'], [], []) expected_source_port_filter = (4, 'udp', ['0', '10'], [], [], []) actual_icmp_filter = self.conntrack_driver._get_filter_from_rule( fw_rule_icmp) actual_port_range_filter = \ self.conntrack_driver._get_filter_from_rule(fw_rule_port_range) actual_dest_port_filter = \ self.conntrack_driver._get_filter_from_rule(fw_rule_dest_port) actual_source_port_filter = \ self.conntrack_driver._get_filter_from_rule(fw_rule_source_port) self.assertEqual(expected_icmp_filter, actual_icmp_filter) self.assertEqual(expected_port_range_filter, actual_port_range_filter) self.assertEqual(expected_dest_port_filter, actual_dest_port_filter) self.assertEqual(expected_source_port_filter, actual_source_port_filter) def test_get_entries_to_delete(self): rule_filters = sorted( [(4, 'tcp', ['0', '10'], ['1', '10']), (4, 'udp', ['0', '10'], ['0', '10']), (4, 'icmp', None, None)]) TCP_ENTRY_IN_RANGE = (4, 'tcp', 2, 3, '1.1.1.1', '2.2.2.2') TCP_ENTRY_OUT_RANGE = (4, 'tcp', 22, 100, '1.1.1.1', '2.2.2.2') UDP_ENTRY_IN_RANGE = (4, 'udp', 3, 4, '1.1.1.1', '2.2.2.2') UDP_ENTRY_OUT_RANGE = (4, 'udp', 100, 200, '1.1.1.1', '2.2.2.2') self.list_entries.return_value = sorted( [ICMP_ENTRY, TCP_ENTRY, UDP_ENTRY, TCP_ENTRY_IN_RANGE, TCP_ENTRY_OUT_RANGE, UDP_ENTRY_IN_RANGE, UDP_ENTRY_OUT_RANGE]) expected_delete_entries = sorted( [ICMP_ENTRY, TCP_ENTRY, UDP_ENTRY, TCP_ENTRY_IN_RANGE, UDP_ENTRY_IN_RANGE]) actual_delete_entries = self.conntrack_driver._get_entries_to_delete( rule_filters, self.list_entries()) self.assertEqual(expected_delete_entries, actual_delete_entries) neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/drivers/__init__.py0000664000175000017500000000000013555577713030632 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/test_fwaas_plugin_v2.py0000664000175000017500000010467413555577713031570 0ustar zuulzuul00000000000000# Copyright 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 mock from neutron.tests import fake_notifier from neutron.tests.unit.extensions import test_l3 as test_l3_plugin from neutron_lib import constants as nl_constants from neutron_lib import context from neutron_lib.exceptions import firewall_v2 as f_exc from neutron_lib.plugins import constants as plugin_constants from neutron_lib.plugins import directory from oslo_config import cfg import six from neutron_fwaas.db.firewall.v2 import firewall_db_v2 import neutron_fwaas.extensions from neutron_fwaas.extensions import firewall_v2 from neutron_fwaas.services.firewall import fwaas_plugin_v2 from neutron_fwaas.tests import base from neutron_fwaas.tests.unit.db.firewall.v2 import ( test_firewall_db_v2 as test_db_firewall) extensions_path = neutron_fwaas.extensions.__path__[0] FW_PLUGIN_KLASS = ( "neutron_fwaas.services.firewall.fwaas_plugin_v2.FirewallPluginV2" ) class FirewallTestExtensionManager(test_l3_plugin.L3TestExtensionManager): def get_resources(self): res = super(FirewallTestExtensionManager, self).get_resources() res = res + firewall_v2.Firewall_v2.get_resources() return res def get_actions(self): return [] def get_request_extensions(self): return [] class TestFirewallAgentApi(base.BaseTestCase): def setUp(self): super(TestFirewallAgentApi, self).setUp() self.api = fwaas_plugin_v2.FirewallAgentApi('topic', 'host') def test_init(self): self.assertEqual('topic', self.api.client.target.topic) self.assertEqual('host', self.api.host) def _call_test_helper(self, method_name): with mock.patch.object(self.api.client, 'cast') as rpc_mock, \ mock.patch.object(self.api.client, 'prepare') as prepare_mock: prepare_mock.return_value = self.api.client getattr(self.api, method_name)(mock.sentinel.context, 'test') prepare_args = {'fanout': True} prepare_mock.assert_called_once_with(**prepare_args) rpc_mock.assert_called_once_with(mock.sentinel.context, method_name, firewall_group='test', host='host') def test_create_firewall_group(self): self._call_test_helper('create_firewall_group') def test_update_firewall_group(self): self._call_test_helper('update_firewall_group') def test_delete_firewall_group(self): self._call_test_helper('delete_firewall_group') class TestFirewallRouterPortBase( test_db_firewall.FirewallPluginV2DbTestCase): def setUp(self, core_plugin=None, fw_plugin=None, ext_mgr=None): self.agentapi_del_fw_p = mock.patch(test_db_firewall.DELETEFW_PATH, create=True, new=test_db_firewall.FakeAgentApi().delete_firewall_group) self.agentapi_del_fw_p.start() # the plugin without L3 support plugin = 'neutron.tests.unit.extensions.test_l3.TestNoL3NatPlugin' # the L3 service plugin l3_plugin = ('neutron.tests.unit.extensions.test_l3.' 'TestL3NatServicePlugin') cfg.CONF.set_override('api_extensions_path', extensions_path) if not fw_plugin: fw_plugin = FW_PLUGIN_KLASS service_plugins = {'l3_plugin_name': l3_plugin, 'fw_plugin_name': fw_plugin} if not ext_mgr: ext_mgr = FirewallTestExtensionManager() super(test_db_firewall.FirewallPluginV2DbTestCase, self).setUp( plugin=plugin, service_plugins=service_plugins, ext_mgr=ext_mgr) self.setup_notification_driver() self.l3_plugin = directory.get_plugin(plugin_constants.L3) self.plugin = directory.get_plugin('FIREWALL_V2') self.callbacks = self.plugin.endpoints[0] class TestFirewallCallbacks(TestFirewallRouterPortBase): def setUp(self): super(TestFirewallCallbacks, self).setUp(fw_plugin=FW_PLUGIN_KLASS) self.callbacks = self.plugin.endpoints[0] def test_set_firewall_group_status(self): ctx = context.get_admin_context() with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall_group( ingress_firewall_policy_id=fwp_id, admin_state_up=test_db_firewall.ADMIN_STATE_UP ) as fwg: fwg_id = fwg['firewall_group']['id'] res = self.callbacks.set_firewall_group_status(ctx, fwg_id, nl_constants.ACTIVE) fwg_db = self.plugin.get_firewall_group(ctx, fwg_id) self.assertEqual(nl_constants.ACTIVE, fwg_db['status']) self.assertTrue(res) res = self.callbacks.set_firewall_group_status(ctx, fwg_id, nl_constants.ERROR) fwg_db = self.plugin.get_firewall_group(ctx, fwg_id) self.assertEqual(nl_constants.ERROR, fwg_db['status']) self.assertFalse(res) def test_firewall_group_deleted(self): ctx = context.get_admin_context() with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall_group( ingress_firewall_policy_id=fwp_id, admin_state_up=test_db_firewall.ADMIN_STATE_UP, do_delete=False ) as fwg: fwg_id = fwg['firewall_group']['id'] with ctx.session.begin(subtransactions=True): fwg_db = self.plugin._get_firewall_group(ctx, fwg_id) fwg_db['status'] = nl_constants.PENDING_DELETE observed = self.callbacks.firewall_group_deleted(ctx, fwg_id) self.assertTrue(observed) self.assertRaises(f_exc.FirewallGroupNotFound, self.plugin.get_firewall_group, ctx, fwg_id) def test_firewall_group_deleted_concurrently(self): ctx = context.get_admin_context() alt_ctx = context.get_admin_context() _get_firewall_group = self.plugin._get_firewall_group def getdelete(context, fwg_id): fwg_db = _get_firewall_group(context, fwg_id) # NOTE(cby): Use a different session to simulate a concurrent del with mock.patch.object( firewall_db_v2.Firewall_db_mixin_v2, 'delete_firewall_group', return_value=None): self.plugin.delete_db_firewall_group_object(alt_ctx, fwg_id) return fwg_db with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall_group( firewall_policy_id=fwp_id, admin_state_up=test_db_firewall.ADMIN_STATE_UP, do_delete=False ) as fwg: fwg_id = fwg['firewall_group']['id'] with ctx.session.begin(subtransactions=True): fwg_db = self.plugin._get_firewall_group(ctx, fwg_id) fwg_db['status'] = nl_constants.PENDING_DELETE ctx.session.flush() with mock.patch.object( self.plugin, '_get_firewall_group', side_effect=getdelete ): observed = self.callbacks.firewall_group_deleted( ctx, fwg_id) self.assertTrue(observed) self.assertRaises(f_exc.FirewallGroupNotFound, self.plugin.get_firewall_group, ctx, fwg_id) def test_firewall_group_deleted_not_found(self): ctx = context.get_admin_context() observed = self.callbacks.firewall_group_deleted( ctx, 'notfound') self.assertTrue(observed) def test_firewall_group_deleted_error(self): ctx = context.get_admin_context() with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall_group( firewall_policy_id=fwp_id, admin_state_up=test_db_firewall.ADMIN_STATE_UP, ) as fwg: fwg_id = fwg['firewall_group']['id'] observed = self.callbacks.firewall_group_deleted( ctx, fwg_id) self.assertFalse(observed) fwg_db = self.plugin._get_firewall_group(ctx, fwg_id) self.assertEqual(nl_constants.ERROR, fwg_db['status']) class TestFirewallPluginBasev2(TestFirewallRouterPortBase, test_l3_plugin.L3NatTestCaseMixin): def setUp(self): super(TestFirewallPluginBasev2, self).setUp(fw_plugin=FW_PLUGIN_KLASS) fake_notifier.reset() mock.patch.object(self.plugin, '_is_supported_by_fw_l2_driver', return_value=True).start() @property def _self_context(self): return context.Context('', self._tenant_id) def test_create_firewall_group_ports_not_specified(self): """neutron firewall-create test-policy """ with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall_group( name='test', ingress_firewall_policy_id=fwp_id, egress_firewall_policy_id=fwp_id, admin_state_up=True) as fwg1: self.assertEqual(nl_constants.INACTIVE, fwg1['firewall_group']['status']) def test_create_firewall_group_with_ports(self): """neutron firewall_group create test-policy """ with self.router(name='router1', admin_state_up=True, tenant_id=self._tenant_id) as r, \ self.subnet() as s1, \ self.subnet(cidr='20.0.0.0/24') as s2: body = self._router_interface_action( 'add', r['router']['id'], s1['subnet']['id'], None) port_id1 = body['port_id'] body = self._router_interface_action( 'add', r['router']['id'], s2['subnet']['id'], None) port_id2 = body['port_id'] fwg_ports = [port_id1, port_id2] with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall_group( name='test', ingress_firewall_policy_id=fwp_id, egress_firewall_policy_id=fwp_id, ports=fwg_ports, admin_state_up=True) as fwg1: self.assertEqual(nl_constants.PENDING_CREATE, fwg1['firewall_group']['status']) self._router_interface_action('remove', r['router']['id'], s1['subnet']['id'], None) self._router_interface_action( 'remove', r['router']['id'], s2['subnet']['id'], None) def test_create_firewall_group_with_ports_on_diff_routers(self): """neutron firewall_group create test-policy """ with self.router(name='router1', admin_state_up=True, tenant_id=self._tenant_id) as r, \ self.subnet() as s1, \ self.subnet(cidr='20.0.0.0/24') as s2: body = self._router_interface_action( 'add', r['router']['id'], s1['subnet']['id'], None) port_id1 = body['port_id'] body = self._router_interface_action( 'add', r['router']['id'], s2['subnet']['id'], None) port_id2 = body['port_id'] with self.router(name='router1', admin_state_up=True, tenant_id=self._tenant_id) as r2, \ self.subnet() as s3: body = self._router_interface_action( 'add', r2['router']['id'], s3['subnet']['id'], None) port_id3 = body['port_id'] fwg_ports = [port_id1, port_id2, port_id3] with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall_group( name='test', ingress_firewall_policy_id=fwp_id, egress_firewall_policy_id=fwp_id, ports=fwg_ports, admin_state_up=True) as fwg1: self.assertEqual(nl_constants.PENDING_CREATE, fwg1['firewall_group']['status']) self._router_interface_action('remove', r2['router']['id'], s3['subnet']['id'], None) self._router_interface_action('remove', r['router']['id'], s1['subnet']['id'], None) self._router_interface_action( 'remove', r['router']['id'], s2['subnet']['id'], None) def test_create_firewall_group_with_ports_no_policy(self): """neutron firewall_group create test-policy """ with self.router(name='router1', admin_state_up=True, tenant_id=self._tenant_id) as r, \ self.subnet() as s1, \ self.subnet(cidr='20.0.0.0/24') as s2: body = self._router_interface_action( 'add', r['router']['id'], s1['subnet']['id'], None) port_id1 = body['port_id'] body = self._router_interface_action( 'add', r['router']['id'], s2['subnet']['id'], None) port_id2 = body['port_id'] fwg_ports = [port_id1, port_id2] with self.firewall_group( name='test', default_policy=False, ports=fwg_ports, admin_state_up=True) as fwg1: self.assertEqual(nl_constants.INACTIVE, fwg1['firewall_group']['status']) self._router_interface_action('remove', r['router']['id'], s1['subnet']['id'], None) self._router_interface_action( 'remove', r['router']['id'], s2['subnet']['id'], None) def test_update_firewall_group_with_new_ports_no_polcy(self): """neutron firewall_group create test-policy """ with self.router(name='router1', admin_state_up=True, tenant_id=self._tenant_id) as r, \ self.subnet() as s1, \ self.subnet(cidr='20.0.0.0/24') as s2, \ self.subnet(cidr='30.0.0.0/24') as s3: body = self._router_interface_action( 'add', r['router']['id'], s1['subnet']['id'], None) port_id1 = body['port_id'] body = self._router_interface_action( 'add', r['router']['id'], s2['subnet']['id'], None) port_id2 = body['port_id'] body = self._router_interface_action( 'add', r['router']['id'], s3['subnet']['id'], None) port_id3 = body['port_id'] fwg_ports = [port_id1, port_id2] with self.firewall_group( name='test', default_policy=False, ports=fwg_ports, admin_state_up=True) as fwg1: self.assertEqual(nl_constants.INACTIVE, fwg1['firewall_group']['status']) data = {'firewall_group': {'ports': [port_id2, port_id3]}} req = self.new_update_request('firewall_groups', data, fwg1['firewall_group']['id'], context=self._self_context) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) self.assertEqual(sorted([port_id2, port_id3]), sorted(res['firewall_group']['ports'])) self.assertEqual(nl_constants.INACTIVE, res['firewall_group']['status']) self._router_interface_action('remove', r['router']['id'], s1['subnet']['id'], None) self._router_interface_action( 'remove', r['router']['id'], s2['subnet']['id'], None) def test_update_firewall_group_with_new_ports_status_pending(self): """neutron firewall_group create test-policy """ with self.router(name='router1', admin_state_up=True, tenant_id=self._tenant_id) as r, \ self.subnet() as s1, \ self.subnet(cidr='20.0.0.0/24') as s2, \ self.subnet(cidr='30.0.0.0/24') as s3: body = self._router_interface_action( 'add', r['router']['id'], s1['subnet']['id'], None) port_id1 = body['port_id'] body = self._router_interface_action( 'add', r['router']['id'], s2['subnet']['id'], None) port_id2 = body['port_id'] fwg_ports = [port_id1, port_id2] body = self._router_interface_action( 'add', r['router']['id'], s3['subnet']['id'], None) port_id3 = body['port_id'] with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall_group( name='test', ingress_firewall_policy_id=fwp_id, egress_firewall_policy_id=fwp_id, ports=fwg_ports, admin_state_up=True) as fwg1: self.assertEqual(nl_constants.PENDING_CREATE, fwg1['firewall_group']['status']) data = {'firewall_group': {'ports': [port_id2, port_id3]}} req = self.new_update_request('firewall_groups', data, fwg1['firewall_group']['id']) res = req.get_response(self.ext_api) self.assertEqual(409, res.status_int) self._router_interface_action('remove', r['router']['id'], s1['subnet']['id'], None) self._router_interface_action( 'remove', r['router']['id'], s2['subnet']['id'], None) def test_update_firewall_group_with_new_ports_status_active(self): """neutron firewall_group create test-policy """ with self.router(name='router1', admin_state_up=True, tenant_id=self._tenant_id) as r, \ self.subnet() as s1, \ self.subnet(cidr='20.0.0.0/24') as s2, \ self.subnet(cidr='30.0.0.0/24') as s3: body = self._router_interface_action( 'add', r['router']['id'], s1['subnet']['id'], None) port_id1 = body['port_id'] body = self._router_interface_action( 'add', r['router']['id'], s2['subnet']['id'], None) port_id2 = body['port_id'] fwg_ports = [port_id1, port_id2] body = self._router_interface_action( 'add', r['router']['id'], s3['subnet']['id'], None) port_id3 = body['port_id'] with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall_group( name='test', ingress_firewall_policy_id=fwp_id, egress_firewall_policy_id=fwp_id, ports=fwg_ports, admin_state_up=True) as fwg1: self.assertEqual(nl_constants.PENDING_CREATE, fwg1['firewall_group']['status']) ctx = context.get_admin_context() self.callbacks.set_firewall_group_status(ctx, fwg1['firewall_group']['id'], nl_constants.ACTIVE) data = {'firewall_group': {'ports': [port_id2, port_id3]}} req = self.new_update_request('firewall_groups', data, fwg1['firewall_group']['id'], context=self._self_context) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) self.assertEqual(sorted([port_id2, port_id3]), sorted(res['firewall_group']['ports'])) self._router_interface_action('remove', r['router']['id'], s1['subnet']['id'], None) self._router_interface_action( 'remove', r['router']['id'], s2['subnet']['id'], None) def test_update_firewall_rule_on_active_fwg(self): name = "new_firewall_rule1" attrs = self._get_test_firewall_rule_attrs(name) with self.router(name='router1', admin_state_up=True, tenant_id=self._tenant_id) as r, \ self.subnet() as s1: body = self._router_interface_action( 'add', r['router']['id'], s1['subnet']['id'], None) port_id1 = body['port_id'] with self.firewall_rule() as fwr: with self.firewall_policy( firewall_rules=[fwr['firewall_rule']['id']]) as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall_group( name='test', ingress_firewall_policy_id=fwp_id, egress_firewall_policy_id=fwp_id, ports=[port_id1], admin_state_up=True) as fwg1: self.assertEqual(nl_constants.PENDING_CREATE, fwg1['firewall_group']['status']) ctx = context.get_admin_context() self.callbacks.set_firewall_group_status(ctx, fwg1['firewall_group']['id'], nl_constants.ACTIVE) data = {'firewall_rule': {'name': name}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id']) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) for k, v in six.iteritems(attrs): self.assertEqual(v, res['firewall_rule'][k]) self._router_interface_action('remove', r['router']['id'], s1['subnet']['id'], None) def test_update_firewall_rule_on_pending_create_fwg(self): """update should fail""" name = "new_firewall_rule1" with self.router(name='router1', admin_state_up=True, tenant_id=self._tenant_id) as r, \ self.subnet() as s1: body = self._router_interface_action( 'add', r['router']['id'], s1['subnet']['id'], None) port_id1 = body['port_id'] with self.firewall_rule() as fwr: with self.firewall_policy( firewall_rules=[fwr['firewall_rule']['id']]) as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall_group( name='test', ingress_firewall_policy_id=fwp_id, egress_firewall_policy_id=fwp_id, ports=[port_id1], admin_state_up=True) as fwg1: self.assertEqual(nl_constants.PENDING_CREATE, fwg1['firewall_group']['status']) data = {'firewall_rule': {'name': name}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id']) res = req.get_response(self.ext_api) self.assertEqual(409, res.status_int) self._router_interface_action('remove', r['router']['id'], s1['subnet']['id'], None) def test_update_firewall_group_with_non_exist_ports(self): """neutron firewall_group create test-policy """ with self.router(name='router1', admin_state_up=True, tenant_id=self._tenant_id) as r, \ self.subnet(cidr='30.0.0.0/24') as s: body = self._router_interface_action( 'add', r['router']['id'], s['subnet']['id'], None) port_id1 = body['port_id'] foo_port_id = 'caef152d-b118-4b9b-bc77-800661bf082d' fwg_ports = [port_id1] with self.firewall_group( name='test', default_policy=False, ports=fwg_ports, admin_state_up=True) as fwg1: self.assertEqual(nl_constants.INACTIVE, fwg1['firewall_group']['status']) data = {'firewall_group': {'ports': [foo_port_id]}} req = self.new_update_request('firewall_groups', data, fwg1['firewall_group']['id']) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) self.assertEqual('PortNotFound', res['NeutronError']['type']) self._router_interface_action('remove', r['router']['id'], s['subnet']['id'], None) def _get_user_context(self, user_id="a_user", tenant_id="some_tenant", is_admin=False): return context.Context(user_id=user_id, tenant_id=tenant_id, is_admin=is_admin) def test_update_firewall_group_with_invalid_project(self): with self.router(name='router1', admin_state_up=True, tenant_id=self._tenant_id) as r, \ self.subnet(cidr='30.0.0.0/24') as s: body = self._router_interface_action('add', r['router']['id'], s['subnet']['id'], None) port_id = body['port_id'] with self.firewall_group(name='test', default_policy=False, ports=[], admin_state_up=True, ctx=self._get_user_context()) as fwg1: data = {'firewall_group': {'ports': [port_id]}} # make sure that an exception is raised when admin tries to # update ports of another tenant req = self.new_update_request( 'firewall_groups', data, fwg1['firewall_group']['id'], context=context.get_admin_context()) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) self.assertEqual('FirewallGroupPortInvalidProject', res['NeutronError']['type']) self._router_interface_action('remove', r['router']['id'], s['subnet']['id'], None) def test_update_firewall_group_with_ports_and_polcy(self): """neutron firewall_group create test-policy """ with self.router(name='router1', admin_state_up=True, tenant_id=self._tenant_id) as r,\ self.subnet() as s1,\ self.subnet(cidr='20.0.0.0/24') as s2: body = self._router_interface_action( 'add', r['router']['id'], s1['subnet']['id'], None) port_id1 = body['port_id'] body = self._router_interface_action( 'add', r['router']['id'], s2['subnet']['id'], None) port_id2 = body['port_id'] fwg_ports = [port_id1, port_id2] with self.firewall_rule() as fwr: with self.firewall_policy( firewall_rules=[fwr['firewall_rule']['id']]) as fwp: with self.firewall_group( name='test', default_policy=False, ports=fwg_ports, admin_state_up=True) as fwg1: self.assertEqual(nl_constants.INACTIVE, fwg1['firewall_group']['status']) fwp_id = fwp["firewall_policy"]["id"] data = {'firewall_group': {'ports': fwg_ports}} req = (self. new_update_request('firewall_groups', data, fwg1['firewall_group']['id'], context=self._self_context)) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) self.assertEqual(nl_constants.INACTIVE, res['firewall_group']['status']) data = {'firewall_group': { 'ingress_firewall_policy_id': fwp_id}} req = (self. new_update_request('firewall_groups', data, fwg1['firewall_group']['id'], context=self._self_context)) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) self.assertEqual(nl_constants.PENDING_UPDATE, res['firewall_group']['status']) self._router_interface_action('remove', r['router']['id'], s1['subnet']['id'], None) self._router_interface_action( 'remove', r['router']['id'], s2['subnet']['id'], None) class TestAutomaticAssociation(TestFirewallPluginBasev2): def setUp(self): # TODO(yushiro): Replace constant value for this test class # Set auto association fwg super(TestAutomaticAssociation, self).setUp() self.agent_rpc = self.plugin.agent_rpc self.plugin.set_port_for_default_firewall_group = mock.Mock() self.plugin._get_fwg_port_details = mock.Mock() def test_vm_port(self): self.plugin._get_fwg_port_details = mock.Mock() kwargs = { "context": mock.ANY, "port": {"id": "fake_port", "device_owner": "compute:nova", "binding:vif_type": "ovs", "project_id": "fake_project"}, "original_port": {"binding:vif_type": "unbound"} } self.plugin.handle_update_port( "PORT", "after_update", "test_plugin", **kwargs) self.plugin.set_port_for_default_firewall_group.\ assert_called_once_with(mock.ANY, kwargs['port']['id'], kwargs['port']['project_id']) def test_vm_port_not_newly_created(self): # Just updated for VM port(name or description...etc.) kwargs = { "context": mock.ANY, "port": { "id": "fake_port", "device_owner": "compute:nova", "binding:vif_type": "ovs", "project_id": "fake_project" }, "original_port": { "device_owner": "compute:nova", "binding:vif_type": "ovs", "project_id": "fake_project" } } self.plugin.handle_update_port( "PORT", "after_update", "test_plugin", **kwargs) self.plugin.set_port_for_default_firewall_group.assert_not_called() def test_not_vm_port(self): for device_owner in ["network:router_interface", "network:router_gateway", "network:dhcp"]: kwargs = { "context": mock.ANY, "port": {"id": "fake_port", "device_owner": device_owner, "project_id": "fake_project"}, "original_port": {"device_owner": device_owner, "binding:vif_type": "unbound", "project_id": "fake_project"} } self.plugin.handle_update_port( "PORT", "after_update", "test_plugin", **kwargs) self.plugin.set_port_for_default_firewall_group.assert_not_called() neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/firewall/__init__.py0000664000175000017500000000000013555577713027154 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/services/__init__.py0000664000175000017500000000000013555577713025347 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/privileged/0000775000175000017500000000000013555600005023534 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/privileged/test_utils.py0000664000175000017500000000707213555577713026336 0ustar zuulzuul00000000000000# 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 mock import testtools from neutron_fwaas.privileged import utils from neutron_fwaas.tests import base class InNamespaceTest(base.BaseTestCase): ORG_NETNS_FD = 124 NEW_NETNS_FD = 421 NEW_NETNS = 'newns' def setUp(self): super(InNamespaceTest, self).setUp() # NOTE(cby): we should unmock os.open/close as early as possible # because there are used in cleanups open_patch = mock.patch('os.open', return_value=self.ORG_NETNS_FD) self.open_mock = open_patch.start() self.addCleanup(open_patch.stop) close_patch = mock.patch('os.close') self.close_mock = close_patch.start() self.addCleanup(close_patch.stop) self.setns_mock = mock.patch( 'pyroute2.netns.setns', side_effect=self.fake_setns ).start() def fake_setns(self, setns): if setns is self.ORG_NETNS_FD: return self.ORG_NETNS_FD elif setns is self.NEW_NETNS: return self.NEW_NETNS_FD else: self.fail('invalid netns name') def test_in_namespace(self): with utils.in_namespace(self.NEW_NETNS): self.setns_mock.assert_called_once_with(self.NEW_NETNS) setns_calls = [mock.call(self.NEW_NETNS), mock.call(self.ORG_NETNS_FD)] close_calls = [mock.call(self.NEW_NETNS_FD), mock.call(self.ORG_NETNS_FD)] self.setns_mock.assert_has_calls(setns_calls) self.close_mock.assert_has_calls(close_calls) def test_in_no_namespace(self): for namespace in ('', None): with utils.in_namespace(namespace): pass self.setns_mock.assert_not_called() self.close_mock.assert_not_called() def test_in_namespace_failed(self): with testtools.ExpectedException(ValueError): with utils.in_namespace(self.NEW_NETNS): self.setns_mock.assert_called_once_with(self.NEW_NETNS) raise ValueError setns_calls = [mock.call(self.NEW_NETNS), mock.call(self.ORG_NETNS_FD)] close_calls = [mock.call(self.NEW_NETNS_FD), mock.call(self.ORG_NETNS_FD)] self.setns_mock.assert_has_calls(setns_calls) self.close_mock.assert_has_calls(close_calls) def test_in_namespace_enter_failed(self): self.setns_mock.side_effect = ValueError with testtools.ExpectedException(ValueError): with utils.in_namespace(self.NEW_NETNS): self.fail('It should fail before we reach this code') self.setns_mock.assert_called_once_with(self.NEW_NETNS) self.close_mock.assert_called_once_with(self.ORG_NETNS_FD) def test_in_namespace_exit_failed(self): self.setns_mock.side_effect = [self.NEW_NETNS_FD, ValueError] with testtools.ExpectedException(utils.BackInNamespaceExit): with utils.in_namespace(self.NEW_NETNS): pass neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/privileged/__init__.py0000664000175000017500000000000013555577713025656 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/privileged/test_netlink_lib.py0000664000175000017500000003662313555577713027474 0ustar zuulzuul00000000000000# 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 mock import testtools from neutron_lib import constants from neutron_fwaas.privileged import netlink_constants as nl_constants from neutron_fwaas.privileged import netlink_lib as nl_lib from neutron_fwaas.tests import base FAKE_ENTRY = {'ipversion': 4, 'protocol': 'icmp', 'type': '8', 'code': '0', 'id': 1234, 'src': '1.1.1.1', 'dst': '2.2.2.2'} FAKE_TCP_ENTRY = {'ipversion': 4, 'protocol': 'tcp', 'sport': 1, 'dport': 2, 'src': '1.1.1.1', 'dst': '2.2.2.2'} FAKE_UDP_ENTRY = {'ipversion': 4, 'protocol': 'udp', 'sport': 1, 'dport': 2, 'src': '1.1.1.1', 'dst': '2.2.2.2'} FAKE_ICMPV6_ENTRY = {'ipversion': 6, 'protocol': 'ipv6-icmp', 'sport': 1, 'dport': 2, 'type': '8', 'code': '0', 'id': 3456, 'src': '10::10', 'dst': '20::20'} class NetlinkLibTestCase(base.BaseTestCase): def setUp(self): super(NetlinkLibTestCase, self).setUp() nl_lib.nfct = mock.Mock() nl_lib.libc = mock.Mock() def test_open_new_conntrack_handler_failed(self): nl_lib.nfct.nfct_open.return_value = None with testtools.ExpectedException(nl_lib.ConntrackOpenFailedExit): with nl_lib.ConntrackManager(): nl_lib.nfct.nfct_open.assert_called_once() nl_lib.nfct.nfct_close.assert_not_called() def test_open_new_conntrack_handler_pass(self): with nl_lib.ConntrackManager(): nl_lib.nfct.nfct_open.assert_called_once() nl_lib.nfct.nfct_close.assert_called_once() def test_conntrack_list_entries(self): with nl_lib.ConntrackManager() as conntrack: nl_lib.nfct.nfct_open.assert_called_once() conntrack.list_entries() nl_lib.nfct.nfct_callback_register.assert_called_once() nl_lib.nfct.nfct_query.assert_called_once() nl_lib.nfct.nfct_close.assert_called_once() def test_conntrack_flush_entries(self): with nl_lib.ConntrackManager() as conntrack: nl_lib.nfct.nfct_open.assert_called_once() conntrack.flush_entries() nl_lib.nfct.nfct_query.assert_called_once() nl_lib.nfct.nfct_close.assert_called_once() def test_conntrack_new_failed(self): nl_lib.nfct.nfct_new.return_value = None with nl_lib.ConntrackManager() as conntrack: nl_lib.nfct.nfct_open.assert_called_once() conntrack.delete_entries([FAKE_ENTRY]) nl_lib.nfct.nfct_new.assert_called_once() nl_lib.nfct.nfct_destroy.assert_called_once() nl_lib.nfct.nfct_close.assert_called_once() def test_conntrack_delete_icmp_entry(self): conntrack_filter = mock.Mock() nl_lib.nfct.nfct_new.return_value = conntrack_filter with nl_lib.ConntrackManager() as conntrack: nl_lib.nfct.nfct_open.assert_called_once() conntrack.delete_entries([FAKE_ENTRY]) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_L3PROTO, nl_constants.IPVERSION_SOCKET[4]), mock.call(conntrack_filter, nl_constants.ATTR_L4PROTO, constants.IP_PROTOCOL_MAP['icmp']), mock.call(conntrack_filter, nl_constants.ATTR_ICMP_CODE, int(FAKE_ENTRY['code'])), mock.call(conntrack_filter, nl_constants.ATTR_ICMP_TYPE, int(FAKE_ENTRY['type'])) ] nl_lib.nfct.nfct_set_attr_u8.assert_has_calls(calls, any_order=True) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_ICMP_ID, nl_lib.libc.htons(FAKE_ENTRY['id'])), ] nl_lib.nfct.nfct_set_attr_u16.assert_has_calls(calls) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_IPV4_SRC, str(conntrack._convert_text_to_binary( FAKE_ENTRY['src'], 4))), mock.call(conntrack_filter, nl_constants.ATTR_IPV4_DST, str(conntrack._convert_text_to_binary( FAKE_ENTRY['dst'], 4))), ] nl_lib.nfct.nfct_set_attr.assert_has_calls(calls, any_order=True) nl_lib.nfct.nfct_destroy.assert_called_once() nl_lib.nfct.nfct_close.assert_called_once() def test_conntrack_delete_icmpv6_entry(self): conntrack_filter = mock.Mock() nl_lib.nfct.nfct_new.return_value = conntrack_filter with nl_lib.ConntrackManager() as conntrack: nl_lib.nfct.nfct_open.assert_called_once() conntrack.delete_entries([FAKE_ICMPV6_ENTRY]) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_L3PROTO, nl_constants.IPVERSION_SOCKET[6]), mock.call(conntrack_filter, nl_constants.ATTR_L4PROTO, constants.IP_PROTOCOL_MAP['ipv6-icmp']), mock.call(conntrack_filter, nl_constants.ATTR_ICMP_CODE, int(FAKE_ICMPV6_ENTRY['code'])), mock.call(conntrack_filter, nl_constants.ATTR_ICMP_TYPE, int(FAKE_ICMPV6_ENTRY['type'])) ] nl_lib.nfct.nfct_set_attr_u8.assert_has_calls(calls, any_order=True) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_ICMP_ID, nl_lib.libc.htons(FAKE_ICMPV6_ENTRY['id'])), ] nl_lib.nfct.nfct_set_attr_u16.assert_has_calls(calls) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_IPV6_SRC, str(conntrack._convert_text_to_binary( FAKE_ENTRY['src'], 6))), mock.call(conntrack_filter, nl_constants.ATTR_IPV6_DST, str(conntrack._convert_text_to_binary( FAKE_ENTRY['dst'], 6))), ] nl_lib.nfct.nfct_set_attr.assert_has_calls(calls, any_order=True) nl_lib.nfct.nfct_destroy.assert_called_once() nl_lib.nfct.nfct_close.assert_called_once() def test_conntrack_delete_udp_entry(self): conntrack_filter = mock.Mock() nl_lib.nfct.nfct_new.return_value = conntrack_filter with nl_lib.ConntrackManager() as conntrack: nl_lib.nfct.nfct_open.assert_called_once() conntrack.delete_entries([FAKE_UDP_ENTRY]) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_L3PROTO, nl_constants.IPVERSION_SOCKET[4]), mock.call(conntrack_filter, nl_constants.ATTR_L4PROTO, constants.IP_PROTOCOL_MAP['udp']) ] nl_lib.nfct.nfct_set_attr_u8.assert_has_calls(calls, any_order=True) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_PORT_SRC, nl_lib.libc.htons(FAKE_UDP_ENTRY['sport'])), mock.call(conntrack_filter, nl_constants.ATTR_PORT_DST, nl_lib.libc.htons(FAKE_UDP_ENTRY['dport'])) ] nl_lib.nfct.nfct_set_attr_u16.assert_has_calls(calls, any_order=True) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_IPV4_SRC, str(conntrack._convert_text_to_binary( FAKE_UDP_ENTRY['src'], 4))), mock.call(conntrack_filter, nl_constants.ATTR_IPV4_DST, str(conntrack._convert_text_to_binary( FAKE_UDP_ENTRY['dst'], 4))), ] nl_lib.nfct.nfct_set_attr.assert_has_calls(calls, any_order=True) nl_lib.nfct.nfct_destroy.assert_called_once() nl_lib.nfct.nfct_close.assert_called_once() def test_conntrack_delete_tcp_entry(self): conntrack_filter = mock.Mock() nl_lib.nfct.nfct_new.return_value = conntrack_filter with nl_lib.ConntrackManager() as conntrack: nl_lib.nfct.nfct_open.assert_called_once() conntrack.delete_entries([FAKE_TCP_ENTRY]) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_L3PROTO, nl_constants.IPVERSION_SOCKET[4]), mock.call(conntrack_filter, nl_constants.ATTR_L4PROTO, constants.IP_PROTOCOL_MAP['tcp']) ] nl_lib.nfct.nfct_set_attr_u8.assert_has_calls(calls, any_order=True) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_PORT_SRC, nl_lib.libc.htons(FAKE_TCP_ENTRY['sport'])), mock.call(conntrack_filter, nl_constants.ATTR_PORT_DST, nl_lib.libc.htons(FAKE_TCP_ENTRY['dport'])) ] nl_lib.nfct.nfct_set_attr_u16.assert_has_calls(calls, any_order=True) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_IPV4_SRC, str(conntrack._convert_text_to_binary( FAKE_TCP_ENTRY['src'], 4))), mock.call(conntrack_filter, nl_constants.ATTR_IPV4_DST, str(conntrack._convert_text_to_binary( FAKE_TCP_ENTRY['dst'], 4))), ] nl_lib.nfct.nfct_set_attr.assert_has_calls(calls, any_order=True) nl_lib.nfct.nfct_destroy.assert_called_once() nl_lib.nfct.nfct_close.assert_called_once() def test_conntrack_delete_entries(self): conntrack_filter = mock.Mock() nl_lib.nfct.nfct_new.return_value = conntrack_filter with nl_lib.ConntrackManager() as conntrack: nl_lib.nfct.nfct_open.assert_called_once() conntrack.delete_entries([FAKE_ENTRY, FAKE_TCP_ENTRY, FAKE_UDP_ENTRY]) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_L3PROTO, nl_constants.IPVERSION_SOCKET[4]), mock.call(conntrack_filter, nl_constants.ATTR_L4PROTO, constants.IP_PROTOCOL_MAP['tcp']), mock.call(conntrack_filter, nl_constants.ATTR_L3PROTO, nl_constants.IPVERSION_SOCKET[4]), mock.call(conntrack_filter, nl_constants.ATTR_L4PROTO, constants.IP_PROTOCOL_MAP['udp']), mock.call(conntrack_filter, nl_constants.ATTR_L3PROTO, nl_constants.IPVERSION_SOCKET[4]), mock.call(conntrack_filter, nl_constants.ATTR_L4PROTO, constants.IP_PROTOCOL_MAP['icmp']), mock.call(conntrack_filter, nl_constants.ATTR_ICMP_CODE, int(FAKE_ENTRY['code'])), mock.call(conntrack_filter, nl_constants.ATTR_ICMP_TYPE, int(FAKE_ENTRY['type'])) ] nl_lib.nfct.nfct_set_attr_u8.assert_has_calls(calls, any_order=True) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_PORT_SRC, nl_lib.libc.htons(FAKE_TCP_ENTRY['sport'])), mock.call(conntrack_filter, nl_constants.ATTR_PORT_DST, nl_lib.libc.htons(FAKE_TCP_ENTRY['dport'])), mock.call(conntrack_filter, nl_constants.ATTR_PORT_SRC, nl_lib.libc.htons(FAKE_UDP_ENTRY['sport'])), mock.call(conntrack_filter, nl_constants.ATTR_PORT_DST, nl_lib.libc.htons(FAKE_UDP_ENTRY['dport'])), mock.call(conntrack_filter, nl_constants.ATTR_ICMP_ID, nl_lib.libc.htons(FAKE_ENTRY['id'])), ] nl_lib.nfct.nfct_set_attr_u16.assert_has_calls(calls, any_order=True) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_IPV4_SRC, str(conntrack._convert_text_to_binary( FAKE_TCP_ENTRY['src'], 4))), mock.call(conntrack_filter, nl_constants.ATTR_IPV4_DST, str(conntrack._convert_text_to_binary( FAKE_TCP_ENTRY['dst'], 4))), mock.call(conntrack_filter, nl_constants.ATTR_IPV4_SRC, str(conntrack._convert_text_to_binary( FAKE_UDP_ENTRY['src'], 4))), mock.call(conntrack_filter, nl_constants.ATTR_IPV4_DST, str(conntrack._convert_text_to_binary( FAKE_UDP_ENTRY['dst'], 4))), mock.call(conntrack_filter, nl_constants.ATTR_IPV4_SRC, str(conntrack._convert_text_to_binary( FAKE_ENTRY['src'], 4))), mock.call(conntrack_filter, nl_constants.ATTR_IPV4_DST, str(conntrack._convert_text_to_binary( FAKE_UDP_ENTRY['dst'], 4))), ] nl_lib.nfct.nfct_set_attr.assert_has_calls(calls, any_order=True) nl_lib.nfct.nfct_destroy.assert_called_once() nl_lib.nfct.nfct_close.assert_called_once() neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/__init__.py0000664000175000017500000000000013555577713023524 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/db/0000775000175000017500000000000013555600005021767 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/db/firewall/0000775000175000017500000000000013555600005023574 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/db/firewall/test_firewall_db.py0000664000175000017500000022406013555577713027506 0ustar zuulzuul00000000000000# 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 contextlib import mock from neutron.api import extensions as api_ext from neutron.common import config from neutron_lib.api.definitions import firewall from neutron_lib import constants as nl_constants from neutron_lib import context 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_utils import importutils from oslo_utils import uuidutils import six import webob.exc from neutron_fwaas.db.firewall import firewall_db as fdb from neutron_fwaas import extensions from neutron_fwaas.services.firewall import fwaas_plugin from neutron_fwaas.tests import base DB_FW_PLUGIN_KLASS = ( "neutron_fwaas.db.firewall.firewall_db.Firewall_db_mixin" ) FWAAS_PLUGIN = 'neutron_fwaas.services.firewall.fwaas_plugin' DELETEFW_PATH = FWAAS_PLUGIN + '.FirewallAgentApi.delete_firewall' extensions_path = ':'.join(extensions.__path__) DESCRIPTION = 'default description' SHARED = True PROTOCOL = 'tcp' IP_VERSION = 4 SOURCE_IP_ADDRESS_RAW = '1.1.1.1' DESTINATION_IP_ADDRESS_RAW = '2.2.2.2' SOURCE_PORT = '55000:56000' DESTINATION_PORT = '56000:57000' ACTION = 'allow' AUDITED = True ENABLED = True ADMIN_STATE_UP = True class FakeAgentApi(fwaas_plugin.FirewallCallbacks): """ This class used to mock the AgentAPI delete method inherits from FirewallCallbacks because it needs access to the firewall_deleted method. The delete_firewall method belongs to the FirewallAgentApi, which has no access to the firewall_deleted method normally because it's not responsible for deleting the firewall from the DB. However, it needs to in the unit tests since there is no agent to call back. """ def __init__(self): pass def delete_firewall(self, context, firewall, **kwargs): self.plugin = directory.get_plugin('FIREWALL') self.firewall_deleted(context, firewall['id'], **kwargs) class FirewallPluginDbTestCase(base.NeutronDbPluginV2TestCase): resource_prefix_map = dict( (k, firewall.API_PREFIX) for k in firewall.RESOURCE_ATTRIBUTE_MAP.keys() ) def setUp(self, core_plugin=None, fw_plugin=None, ext_mgr=None): self.agentapi_delf_p = mock.patch(DELETEFW_PATH, create=True, new=FakeAgentApi().delete_firewall) self.agentapi_delf_p.start() if not fw_plugin: fw_plugin = DB_FW_PLUGIN_KLASS service_plugins = {'fw_plugin_name': fw_plugin} fdb.Firewall_db_mixin.supported_extension_aliases = ["fwaas"] fdb.Firewall_db_mixin.path_prefix = firewall.API_PREFIX super(FirewallPluginDbTestCase, self).setUp( ext_mgr=ext_mgr, service_plugins=service_plugins ) if not ext_mgr: self.plugin = importutils.import_object(fw_plugin) ext_mgr = api_ext.PluginAwareExtensionManager( extensions_path, {'FIREWALL': self.plugin} ) app = config.load_paste_app('extensions_test_app') self.ext_api = api_ext.ExtensionMiddleware(app, ext_mgr=ext_mgr) def _test_list_resources(self, resource, items, neutron_context=None, query_params=None): if resource.endswith('y'): resource_plural = resource.replace('y', 'ies') else: resource_plural = resource + 's' res = self._list(resource_plural, neutron_context=neutron_context, query_params=query_params) resource = resource.replace('-', '_') if query_params is None or ('fields' not in query_params): self.assertEqual( sorted([i[resource]['id'] for i in items]), sorted([i['id'] for i in res[resource_plural]])) else: self.assertEqual( sorted([i for i in items]), sorted([i for i in res[resource_plural]])) def _get_test_firewall_rule_attrs(self, name='firewall_rule1'): attrs = {'name': name, 'tenant_id': self._tenant_id, 'project_id': self._tenant_id, 'shared': SHARED, 'protocol': PROTOCOL, 'ip_version': IP_VERSION, 'source_ip_address': SOURCE_IP_ADDRESS_RAW, 'destination_ip_address': DESTINATION_IP_ADDRESS_RAW, 'source_port': SOURCE_PORT, 'destination_port': DESTINATION_PORT, 'action': ACTION, 'enabled': ENABLED} return attrs def _get_test_firewall_policy_attrs(self, name='firewall_policy1', audited=AUDITED): attrs = {'name': name, 'description': DESCRIPTION, 'tenant_id': self._tenant_id, 'project_id': self._tenant_id, 'shared': SHARED, 'firewall_rules': [], 'audited': audited} return attrs def _get_test_firewall_attrs(self, name='firewall_1', status='PENDING_CREATE'): attrs = {'name': name, 'tenant_id': self._tenant_id, 'project_id': self._tenant_id, 'admin_state_up': ADMIN_STATE_UP, 'status': status} return attrs def _create_firewall_policy(self, fmt, name, description, shared, firewall_rules, audited, expected_res_status=None, **kwargs): tenant_id = kwargs.get('tenant_id', self._tenant_id) data = {'firewall_policy': {'name': name, 'description': description, 'tenant_id': tenant_id, 'project_id': tenant_id, 'shared': shared, 'firewall_rules': firewall_rules, 'audited': audited}} fw_policy_req = self.new_create_request('firewall_policies', data, fmt) fw_policy_res = fw_policy_req.get_response(self.ext_api) if expected_res_status: self.assertEqual(expected_res_status, fw_policy_res.status_int) return fw_policy_res def _replace_firewall_status(self, attrs, old_status, new_status): if attrs['status'] is old_status: attrs['status'] = new_status return attrs @contextlib.contextmanager def firewall_policy(self, fmt=None, name='firewall_policy1', description=DESCRIPTION, shared=True, firewall_rules=None, audited=True, do_delete=True, **kwargs): if firewall_rules is None: firewall_rules = [] if not fmt: fmt = self.fmt res = self._create_firewall_policy(fmt, name, description, shared, firewall_rules, audited, **kwargs) if res.status_int >= 400: raise webob.exc.HTTPClientError(code=res.status_int) firewall_policy = self.deserialize(fmt or self.fmt, res) yield firewall_policy if do_delete: self._delete('firewall_policies', firewall_policy['firewall_policy']['id']) def _create_firewall_rule(self, fmt, name, shared, protocol, ip_version, source_ip_address, destination_ip_address, source_port, destination_port, action, enabled, expected_res_status=None, **kwargs): tenant_id = kwargs.get('tenant_id', self._tenant_id) data = {'firewall_rule': {'name': name, 'tenant_id': tenant_id, 'project_id': tenant_id, 'shared': shared, 'protocol': protocol, 'ip_version': ip_version, 'source_ip_address': source_ip_address, 'destination_ip_address': destination_ip_address, 'source_port': source_port, 'destination_port': destination_port, 'action': action, 'enabled': enabled}} fw_rule_req = self.new_create_request('firewall_rules', data, fmt) fw_rule_res = fw_rule_req.get_response(self.ext_api) if expected_res_status: self.assertEqual(expected_res_status, fw_rule_res.status_int) return fw_rule_res @contextlib.contextmanager def firewall_rule(self, fmt=None, name='firewall_rule1', shared=SHARED, protocol=PROTOCOL, ip_version=IP_VERSION, source_ip_address=SOURCE_IP_ADDRESS_RAW, destination_ip_address=DESTINATION_IP_ADDRESS_RAW, source_port=SOURCE_PORT, destination_port=DESTINATION_PORT, action=ACTION, enabled=ENABLED, do_delete=True, **kwargs): if not fmt: fmt = self.fmt res = self._create_firewall_rule(fmt, name, shared, protocol, ip_version, source_ip_address, destination_ip_address, source_port, destination_port, action, enabled, **kwargs) if res.status_int >= 400: raise webob.exc.HTTPClientError(code=res.status_int) firewall_rule = self.deserialize(fmt or self.fmt, res) yield firewall_rule if do_delete: self._delete('firewall_rules', firewall_rule['firewall_rule']['id']) def _create_firewall(self, fmt, name, description, firewall_policy_id, admin_state_up=True, expected_res_status=None, **kwargs): tenant_id = kwargs.get('tenant_id', self._tenant_id) if firewall_policy_id is None: res = self._create_firewall_policy(fmt, 'fwp', description=DESCRIPTION, shared=True, firewall_rules=[], tenant_id=tenant_id, audited=AUDITED) firewall_policy = self.deserialize(fmt or self.fmt, res) firewall_policy_id = firewall_policy["firewall_policy"]["id"] data = {'firewall': {'name': name, 'description': description, 'firewall_policy_id': firewall_policy_id, 'admin_state_up': admin_state_up}} ctx = kwargs.get('context', None) if ctx is None or ctx.is_admin: data['firewall'].update({'tenant_id': tenant_id}) data['firewall'].update({'project_id': tenant_id}) router_ids = kwargs.get('router_ids', None) if router_ids: data['firewall'].update({'router_ids': router_ids}) firewall_req = self.new_create_request('firewalls', data, fmt, context=ctx) firewall_res = firewall_req.get_response(self.ext_api) if expected_res_status: self.assertEqual(expected_res_status, firewall_res.status_int) return firewall_res @contextlib.contextmanager def firewall(self, fmt=None, name='firewall_1', description=DESCRIPTION, firewall_policy_id=None, admin_state_up=True, do_delete=True, **kwargs): if not fmt: fmt = self.fmt res = self._create_firewall(fmt, name, description, firewall_policy_id, admin_state_up, **kwargs) if res.status_int >= 400: raise webob.exc.HTTPClientError(code=res.status_int) firewall = self.deserialize(fmt or self.fmt, res) yield firewall if do_delete: self._delete('firewalls', firewall['firewall']['id']) def _rule_action(self, action, id, firewall_rule_id, insert_before=None, insert_after=None, expected_code=webob.exc.HTTPOk.code, expected_body=None, body_data=None): # We intentionally do this check for None since we want to distinguish # from empty dictionary if body_data is None: if action == 'insert': body_data = {'firewall_rule_id': firewall_rule_id, 'insert_before': insert_before, 'insert_after': insert_after} else: body_data = {'firewall_rule_id': firewall_rule_id} req = self.new_action_request('firewall_policies', body_data, id, "%s_rule" % action) res = req.get_response(self.ext_api) self.assertEqual(expected_code, res.status_int) response = self.deserialize(self.fmt, res) if expected_body: self.assertEqual(expected_body, response) return response def _compare_firewall_rule_lists(self, firewall_policy_id, observed_list, expected_list): position = 0 for r1, r2 in zip(observed_list, expected_list): rule = r1['firewall_rule'] rule['firewall_policy_id'] = firewall_policy_id position += 1 rule['position'] = position for k in rule: self.assertEqual(r2[k], rule[k]) class TestFirewallDBPlugin(FirewallPluginDbTestCase): def test_create_firewall_policy(self): name = "firewall_policy1" attrs = self._get_test_firewall_policy_attrs(name) with self.firewall_policy(name=name, shared=SHARED, firewall_rules=None, audited=AUDITED) as firewall_policy: for k, v in six.iteritems(attrs): self.assertEqual(v, firewall_policy['firewall_policy'][k]) def test_create_firewall_policy_with_rules(self): name = "firewall_policy1" attrs = self._get_test_firewall_policy_attrs(name) with self.firewall_rule(name='fwr1') as fwr1, \ self.firewall_rule(name='fwr2') as fwr2, \ self.firewall_rule(name='fwr3') as fwr3: fr = [fwr1, fwr2, fwr3] fw_rule_ids = [r['firewall_rule']['id'] for r in fr] attrs['firewall_rules'] = fw_rule_ids with self.firewall_policy(name=name, shared=SHARED, firewall_rules=fw_rule_ids, audited=AUDITED) as fwp: for k, v in six.iteritems(attrs): self.assertEqual(v, fwp['firewall_policy'][k]) def test_create_admin_firewall_policy_with_other_tenant_rules(self): with self.firewall_rule(shared=False) as fr: fw_rule_ids = [fr['firewall_rule']['id']] res = self._create_firewall_policy(None, 'firewall_policy1', description=DESCRIPTION, shared=SHARED, firewall_rules=fw_rule_ids, audited=AUDITED, tenant_id='admin-tenant') self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_create_firewall_policy_with_previously_associated_rule(self): with self.firewall_rule() as fwr: fw_rule_ids = [fwr['firewall_rule']['id']] with self.firewall_policy(firewall_rules=fw_rule_ids): res = self._create_firewall_policy( None, 'firewall_policy2', description=DESCRIPTION, shared=SHARED, firewall_rules=fw_rule_ids, audited=AUDITED) self.assertEqual(409, res.status_int) def test_create_shared_firewall_policy_with_unshared_rule(self): with self.firewall_rule(shared=False) as fwr: fw_rule_ids = [fwr['firewall_rule']['id']] res = self._create_firewall_policy( None, 'firewall_policy1', description=DESCRIPTION, shared=True, firewall_rules=fw_rule_ids, audited=AUDITED) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_show_firewall_policy(self): name = "firewall_policy1" attrs = self._get_test_firewall_policy_attrs(name) with self.firewall_policy(name=name, shared=SHARED, firewall_rules=None, audited=AUDITED) as fwp: req = self.new_show_request('firewall_policies', fwp['firewall_policy']['id'], fmt=self.fmt) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) for k, v in six.iteritems(attrs): self.assertEqual(v, res['firewall_policy'][k]) def test_list_firewall_policies(self): with self.firewall_policy(name='fwp1', description='fwp') as fwp1, \ self.firewall_policy(name='fwp2', description='fwp') as fwp2, \ self.firewall_policy(name='fwp3', description='fwp') as fwp3: fw_policies = [fwp1, fwp2, fwp3] self._test_list_resources('firewall_policy', fw_policies, query_params='description=fwp') def test_update_firewall_policy(self): name = "new_firewall_policy1" attrs = self._get_test_firewall_policy_attrs(name, audited=False) with self.firewall_policy(shared=SHARED, firewall_rules=None, audited=AUDITED) as fwp: data = {'firewall_policy': {'name': name}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) for k, v in six.iteritems(attrs): self.assertEqual(v, res['firewall_policy'][k]) def _test_update_firewall_policy(self, with_audited): with self.firewall_policy(name='firewall_policy1', description='fwp', audited=AUDITED) as fwp: attrs = self._get_test_firewall_policy_attrs(audited=with_audited) data = {'firewall_policy': {'description': 'fw_p1'}} if with_audited: data['firewall_policy']['audited'] = 'True' req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) attrs['description'] = 'fw_p1' for k, v in six.iteritems(attrs): self.assertEqual(v, res['firewall_policy'][k]) def test_update_firewall_policy_set_audited_false(self): self._test_update_firewall_policy(with_audited=False) def test_update_firewall_policy_with_audited_set_true(self): self._test_update_firewall_policy(with_audited=True) def test_update_firewall_policy_with_rules(self): attrs = self._get_test_firewall_policy_attrs() with self.firewall_rule(name='fwr1') as fwr1, \ self.firewall_rule(name='fwr2') as fwr2, \ self.firewall_rule(name='fwr3') as fwr3: with self.firewall_policy() as fwp: fr = [fwr1, fwr2, fwr3] fw_rule_ids = [r['firewall_rule']['id'] for r in fr] attrs['firewall_rules'] = fw_rule_ids data = {'firewall_policy': {'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) attrs['audited'] = False for k, v in six.iteritems(attrs): self.assertEqual(v, res['firewall_policy'][k]) def test_update_firewall_policy_replace_rules(self): attrs = self._get_test_firewall_policy_attrs() with self.firewall_rule(name='fwr1') as fwr1, \ self.firewall_rule(name='fwr2') as fwr2, \ self.firewall_rule(name='fwr3') as fwr3, \ self.firewall_rule(name='fwr4') as fwr4: frs = [fwr1, fwr2, fwr3, fwr4] fr1 = frs[0:2] fr2 = frs[2:4] with self.firewall_policy() as fwp: fw_rule_ids = [r['firewall_rule']['id'] for r in fr1] data = {'firewall_policy': {'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) req.get_response(self.ext_api) fw_rule_ids = [r['firewall_rule']['id'] for r in fr2] attrs['firewall_rules'] = fw_rule_ids new_data = {'firewall_policy': {'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', new_data, fwp['firewall_policy']['id']) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) attrs['audited'] = False for k, v in six.iteritems(attrs): self.assertEqual(v, res['firewall_policy'][k]) def test_update_firewall_policy_reorder_rules(self): attrs = self._get_test_firewall_policy_attrs() with self.firewall_rule(name='fwr1') as fwr1, \ self.firewall_rule(name='fwr2') as fwr2, \ self.firewall_rule(name='fwr3') as fwr3, \ self.firewall_rule(name='fwr4') as fwr4: fr = [fwr1, fwr2, fwr3, fwr4] with self.firewall_policy() as fwp: fw_rule_ids = [fr[2]['firewall_rule']['id'], fr[3]['firewall_rule']['id']] data = {'firewall_policy': {'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) req.get_response(self.ext_api) # shuffle the rules, add more rules fw_rule_ids = [fr[1]['firewall_rule']['id'], fr[3]['firewall_rule']['id'], fr[2]['firewall_rule']['id'], fr[0]['firewall_rule']['id']] attrs['firewall_rules'] = fw_rule_ids data = {'firewall_policy': {'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) rules = [] for rule_id in fw_rule_ids: req = self.new_show_request('firewall_rules', rule_id, fmt=self.fmt) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) rules.append(res['firewall_rule']) self.assertEqual(1, rules[0]['position']) self.assertEqual(fr[1]['firewall_rule']['id'], rules[0]['id']) self.assertEqual(2, rules[1]['position']) self.assertEqual(fr[3]['firewall_rule']['id'], rules[1]['id']) self.assertEqual(3, rules[2]['position']) self.assertEqual(fr[2]['firewall_rule']['id'], rules[2]['id']) self.assertEqual(4, rules[3]['position']) self.assertEqual(fr[0]['firewall_rule']['id'], rules[3]['id']) def test_update_firewall_policy_with_non_existing_rule(self): attrs = self._get_test_firewall_policy_attrs() with self.firewall_rule(name='fwr1') as fwr1, \ self.firewall_rule(name='fwr2') as fwr2: fr = [fwr1, fwr2] with self.firewall_policy() as fwp: fw_rule_ids = [r['firewall_rule']['id'] for r in fr] # appending non-existent rule fw_rule_ids.append(uuidutils.generate_uuid()) data = {'firewall_policy': {'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) res = req.get_response(self.ext_api) # check that the firewall_rule was not found self.assertEqual(404, res.status_int) # check if none of the rules got added to the policy req = self.new_show_request('firewall_policies', fwp['firewall_policy']['id'], fmt=self.fmt) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) for k, v in six.iteritems(attrs): self.assertEqual(v, res['firewall_policy'][k]) def test_update_shared_firewall_policy_with_unshared_rule(self): with self.firewall_rule(name='fwr1', shared=False) as fr: with self.firewall_policy() as fwp: fw_rule_ids = [fr['firewall_rule']['id']] # update shared policy with unshared rule data = {'firewall_policy': {'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) res = req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_update_firewall_policy_with_shared_attr_unshared_rule(self): with self.firewall_rule(name='fwr1', shared=False) as fr: with self.firewall_policy(shared=False) as fwp: fw_rule_ids = [fr['firewall_rule']['id']] # update shared policy with shared attr and unshared rule data = {'firewall_policy': {'shared': True, 'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) res = req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_update_firewall_policy_with_shared_attr_exist_unshare_rule(self): with self.firewall_rule(name='fwr1', shared=False) as fr: fw_rule_ids = [fr['firewall_rule']['id']] with self.firewall_policy(shared=False, firewall_rules=fw_rule_ids) as fwp: # update policy with shared attr data = {'firewall_policy': {'shared': True}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) res = req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_update_firewall_policy_assoc_with_other_tenant_firewall(self): with self.firewall_policy(shared=True, tenant_id='tenant1') as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall(firewall_policy_id=fwp_id): data = {'firewall_policy': {'shared': False}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) res = req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_delete_firewall_policy(self): ctx = context.get_admin_context() with self.firewall_policy(do_delete=False) as fwp: fwp_id = fwp['firewall_policy']['id'] req = self.new_delete_request('firewall_policies', fwp_id) res = req.get_response(self.ext_api) self.assertEqual(204, res.status_int) self.assertRaises(f_exc.FirewallPolicyNotFound, self.plugin.get_firewall_policy, ctx, fwp_id) def test_delete_firewall_policy_with_rule(self): ctx = context.get_admin_context() attrs = self._get_test_firewall_policy_attrs() with self.firewall_policy(do_delete=False) as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall_rule(name='fwr1') as fr: fr_id = fr['firewall_rule']['id'] fw_rule_ids = [fr_id] attrs['firewall_rules'] = fw_rule_ids data = {'firewall_policy': {'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) req.get_response(self.ext_api) fw_rule = self.plugin.get_firewall_rule(ctx, fr_id) self.assertEqual(fwp_id, fw_rule['firewall_policy_id']) req = self.new_delete_request('firewall_policies', fwp_id) res = req.get_response(self.ext_api) self.assertEqual(204, res.status_int) self.assertRaises(f_exc.FirewallPolicyNotFound, self.plugin.get_firewall_policy, ctx, fwp_id) fw_rule = self.plugin.get_firewall_rule(ctx, fr_id) self.assertIsNone(fw_rule['firewall_policy_id']) def test_delete_firewall_policy_with_firewall_association(self): attrs = self._get_test_firewall_attrs() with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] attrs['firewall_policy_id'] = fwp_id with self.firewall( firewall_policy_id=fwp_id, admin_state_up=ADMIN_STATE_UP): req = self.new_delete_request('firewall_policies', fwp_id) res = req.get_response(self.ext_api) self.assertEqual(409, res.status_int) def test_create_firewall_rule(self): attrs = self._get_test_firewall_rule_attrs() with self.firewall_rule() as firewall_rule: for k, v in six.iteritems(attrs): self.assertEqual(v, firewall_rule['firewall_rule'][k]) attrs['source_port'] = None attrs['destination_port'] = None with self.firewall_rule(source_port=None, destination_port=None) as firewall_rule: for k, v in six.iteritems(attrs): self.assertEqual(v, firewall_rule['firewall_rule'][k]) attrs['source_port'] = '10000' attrs['destination_port'] = '80' with self.firewall_rule(source_port=10000, destination_port=80) as firewall_rule: for k, v in six.iteritems(attrs): self.assertEqual(v, firewall_rule['firewall_rule'][k]) attrs['source_port'] = '10000' attrs['destination_port'] = '80' with self.firewall_rule(source_port='10000', destination_port='80') as firewall_rule: for k, v in six.iteritems(attrs): self.assertEqual(v, firewall_rule['firewall_rule'][k]) def test_create_firewall_src_port_illegal_range(self): attrs = self._get_test_firewall_rule_attrs() attrs['source_port'] = '65535:1024' res = self._create_firewall_rule(self.fmt, **attrs) self.assertEqual(400, res.status_int) def test_create_firewall_dest_port_illegal_range(self): attrs = self._get_test_firewall_rule_attrs() attrs['destination_port'] = '65535:1024' res = self._create_firewall_rule(self.fmt, **attrs) self.assertEqual(400, res.status_int) def test_create_firewall_rule_icmp_with_port(self): attrs = self._get_test_firewall_rule_attrs() attrs['protocol'] = 'icmp' res = self._create_firewall_rule(self.fmt, **attrs) self.assertEqual(400, res.status_int) def test_create_firewall_rule_icmp_without_port(self): attrs = self._get_test_firewall_rule_attrs() attrs['protocol'] = 'icmp' attrs['source_port'] = None attrs['destination_port'] = None with self.firewall_rule(source_port=None, destination_port=None, protocol='icmp') as firewall_rule: for k, v in six.iteritems(attrs): self.assertEqual(v, firewall_rule['firewall_rule'][k]) def test_create_firewall_without_source(self): attrs = self._get_test_firewall_rule_attrs() attrs['source_ip_address'] = None res = self._create_firewall_rule(self.fmt, **attrs) self.assertEqual(201, res.status_int) def test_create_firewall_rule_without_destination(self): attrs = self._get_test_firewall_rule_attrs() attrs['destination_ip_address'] = None res = self._create_firewall_rule(self.fmt, **attrs) self.assertEqual(201, res.status_int) def test_create_firewall_rule_without_protocol_with_dport(self): attrs = self._get_test_firewall_rule_attrs() attrs['protocol'] = None attrs['source_port'] = None res = self._create_firewall_rule(self.fmt, **attrs) self.assertEqual(400, res.status_int) def test_create_firewall_rule_without_protocol_with_sport(self): attrs = self._get_test_firewall_rule_attrs() attrs['protocol'] = None attrs['destination_port'] = None res = self._create_firewall_rule(self.fmt, **attrs) self.assertEqual(400, res.status_int) def test_show_firewall_rule_with_fw_policy_not_associated(self): attrs = self._get_test_firewall_rule_attrs() with self.firewall_rule() as fw_rule: req = self.new_show_request('firewall_rules', fw_rule['firewall_rule']['id'], fmt=self.fmt) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) for k, v in six.iteritems(attrs): self.assertEqual(v, res['firewall_rule'][k]) def test_show_firewall_rule_with_fw_policy_associated(self): attrs = self._get_test_firewall_rule_attrs() with self.firewall_rule() as fw_rule: with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] attrs['firewall_policy_id'] = fwp_id data = {'firewall_policy': {'firewall_rules': [fw_rule['firewall_rule']['id']]}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) req.get_response(self.ext_api) req = self.new_show_request('firewall_rules', fw_rule['firewall_rule']['id'], fmt=self.fmt) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) for k, v in six.iteritems(attrs): self.assertEqual(v, res['firewall_rule'][k]) def test_create_firewall_rule_with_ipv6_addrs_and_wrong_ip_version(self): attrs = self._get_test_firewall_rule_attrs() attrs['source_ip_address'] = '::/0' attrs['destination_ip_address'] = '2001:db8:3::/64' attrs['ip_version'] = 4 res = self._create_firewall_rule(self.fmt, **attrs) self.assertEqual(400, res.status_int) attrs = self._get_test_firewall_rule_attrs() attrs['source_ip_address'] = None attrs['destination_ip_address'] = '2001:db8:3::/64' attrs['ip_version'] = 4 res = self._create_firewall_rule(self.fmt, **attrs) self.assertEqual(400, res.status_int) attrs = self._get_test_firewall_rule_attrs() attrs['source_ip_address'] = '::/0' attrs['destination_ip_address'] = None attrs['ip_version'] = 4 res = self._create_firewall_rule(self.fmt, **attrs) self.assertEqual(400, res.status_int) def test_list_firewall_rules(self): with self.firewall_rule(name='fwr1') as fwr1, \ self.firewall_rule(name='fwr2') as fwr2, \ self.firewall_rule(name='fwr3') as fwr3: fr = [fwr1, fwr2, fwr3] query_params = 'protocol=tcp' self._test_list_resources('firewall_rule', fr, query_params=query_params) def test_update_firewall_rule(self): name = "new_firewall_rule1" attrs = self._get_test_firewall_rule_attrs(name) attrs['source_port'] = '10:20' attrs['destination_port'] = '30:40' with self.firewall_rule() as fwr: data = {'firewall_rule': {'name': name, 'protocol': PROTOCOL, 'source_port': '10:20', 'destination_port': '30:40'}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id']) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) for k, v in six.iteritems(attrs): self.assertEqual(v, res['firewall_rule'][k]) attrs['source_port'] = '10000' attrs['destination_port'] = '80' with self.firewall_rule() as fwr: data = {'firewall_rule': {'name': name, 'protocol': PROTOCOL, 'source_port': 10000, 'destination_port': 80}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id']) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) for k, v in six.iteritems(attrs): self.assertEqual(v, res['firewall_rule'][k]) attrs['source_port'] = '10000' attrs['destination_port'] = '80' with self.firewall_rule() as fwr: data = {'firewall_rule': {'name': name, 'protocol': PROTOCOL, 'source_port': '10000', 'destination_port': '80'}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id']) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) for k, v in six.iteritems(attrs): self.assertEqual(v, res['firewall_rule'][k]) attrs['source_port'] = None attrs['destination_port'] = None with self.firewall_rule() as fwr: data = {'firewall_rule': {'name': name, 'source_port': None, 'destination_port': None}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id']) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) for k, v in six.iteritems(attrs): self.assertEqual(v, res['firewall_rule'][k]) def test_update_firewall_rule_with_port_and_no_proto(self): with self.firewall_rule() as fwr: data = {'firewall_rule': {'protocol': None, 'destination_port': 80}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id']) res = req.get_response(self.ext_api) self.assertEqual(400, res.status_int) def test_update_firewall_rule_without_ports_and_no_proto(self): with self.firewall_rule() as fwr: data = {'firewall_rule': {'protocol': None, 'destination_port': None, 'source_port': None}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id']) res = req.get_response(self.ext_api) self.assertEqual(200, res.status_int) def test_update_firewall_rule_with_port(self): with self.firewall_rule(source_port=None, destination_port=None, protocol=None) as fwr: data = {'firewall_rule': {'destination_port': 80}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id']) res = req.get_response(self.ext_api) self.assertEqual(400, res.status_int) def test_update_firewall_rule_with_port_illegal_range(self): with self.firewall_rule() as fwr: data = {'firewall_rule': {'destination_port': '65535:1024'}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id']) res = req.get_response(self.ext_api) self.assertEqual(400, res.status_int) def test_update_firewall_rule_with_port_and_protocol(self): with self.firewall_rule(source_port=None, destination_port=None, protocol=None) as fwr: data = {'firewall_rule': {'destination_port': 80, 'protocol': 'tcp'}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id']) res = req.get_response(self.ext_api) self.assertEqual(200, res.status_int) def test_update_firewall_rule_icmp_with_port(self): with self.firewall_rule(source_port=None, destination_port=None, protocol=None) as fwr: data = {'firewall_rule': {'destination_port': 80, 'protocol': 'icmp'}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id']) res = req.get_response(self.ext_api) self.assertEqual(400, res.status_int) def test_update_firewall_rule_with_policy_associated(self): name = "new_firewall_rule1" attrs = self._get_test_firewall_rule_attrs(name) with self.firewall_rule() as fwr: with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] attrs['firewall_policy_id'] = fwp_id fwr_id = fwr['firewall_rule']['id'] data = {'firewall_policy': {'firewall_rules': [fwr_id]}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) req.get_response(self.ext_api) data = {'firewall_rule': {'name': name}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id']) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) attrs['firewall_policy_id'] = fwp_id for k, v in six.iteritems(attrs): self.assertEqual(v, res['firewall_rule'][k]) req = self.new_show_request('firewall_policies', fwp['firewall_policy']['id'], fmt=self.fmt) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) self.assertEqual( [fwr_id], res['firewall_policy']['firewall_rules']) self.assertFalse(res['firewall_policy']['audited']) def test_update_firewall_rule_associated_with_other_tenant_policy(self): with self.firewall_rule(shared=True, tenant_id='tenant1') as fwr: fwr_id = [fwr['firewall_rule']['id']] with self.firewall_policy(shared=False, firewall_rules=fwr_id): data = {'firewall_rule': {'shared': False}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id']) res = req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_delete_firewall_rule(self): ctx = context.get_admin_context() with self.firewall_rule(do_delete=False) as fwr: fwr_id = fwr['firewall_rule']['id'] req = self.new_delete_request('firewall_rules', fwr_id) res = req.get_response(self.ext_api) self.assertEqual(204, res.status_int) self.assertRaises(f_exc.FirewallRuleNotFound, self.plugin.get_firewall_rule, ctx, fwr_id) def test_delete_firewall_rule_with_policy_associated(self): attrs = self._get_test_firewall_rule_attrs() with self.firewall_rule() as fwr: with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] attrs['firewall_policy_id'] = fwp_id fwr_id = fwr['firewall_rule']['id'] data = {'firewall_policy': {'firewall_rules': [fwr_id]}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) req.get_response(self.ext_api) req = self.new_delete_request('firewall_rules', fwr_id) res = req.get_response(self.ext_api) self.assertEqual(409, res.status_int) def _test_create_firewall(self, attrs): with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] attrs['firewall_policy_id'] = fwp_id with self.firewall( name=attrs['name'], firewall_policy_id=fwp_id, admin_state_up=ADMIN_STATE_UP ) as firewall: for k, v in six.iteritems(attrs): self.assertEqual(v, firewall['firewall'][k]) def test_create_firewall(self): attrs = self._get_test_firewall_attrs("firewall1") self._test_create_firewall(attrs) def test_create_firewall_with_dvr(self): cfg.CONF.set_override('router_distributed', True) attrs = self._get_test_firewall_attrs("firewall1", "CREATED") self._test_create_firewall(attrs) def test_create_firewall_with_fwp_does_not_exist(self): fmt = self.fmt fw_name = "firewall1" description = "my_firewall1" not_found_fwp_id = uuidutils.generate_uuid() self._create_firewall(fmt, fw_name, description, not_found_fwp_id, ADMIN_STATE_UP, expected_res_status=404) def test_create_firewall_with_fwp_not_found_on_different_tenant(self): fmt = self.fmt fw_name = "firewall1" description = "my_firewall1" with self.firewall_policy(shared=False, tenant_id='tenant2') as fwp: fwp_id = fwp['firewall_policy']['id'] ctx = context.Context('not_admin', 'tenant1') self._create_firewall(fmt, fw_name, description, fwp_id, context=ctx, expected_res_status=404) def test_create_firewall_with_admin_and_fwp_different_tenant(self): fmt = self.fmt fw_name = "firewall1" description = "my_firewall1" with self.firewall_policy(shared=False, tenant_id='tenant2') as fwp: fwp_id = fwp['firewall_policy']['id'] ctx = context.get_admin_context() self._create_firewall(fmt, fw_name, description, fwp_id, tenant_id="admin-tenant", context=ctx, expected_res_status=409) def test_create_firewall_with_admin_and_fwp_is_shared(self): fw_name = "fw_with_shared_fwp" with self.firewall_policy(tenant_id="tenantX") as fwp: fwp_id = fwp['firewall_policy']['id'] ctx = context.get_admin_context() target_tenant = 'tenant1' with self.firewall(name=fw_name, firewall_policy_id=fwp_id, tenant_id=target_tenant, context=ctx, admin_state_up=ADMIN_STATE_UP) as fw: self.assertEqual(target_tenant, fw['firewall']['tenant_id']) def test_show_firewall(self): name = "firewall1" attrs = self._get_test_firewall_attrs(name) with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] attrs['firewall_policy_id'] = fwp_id with self.firewall( name=name, firewall_policy_id=fwp_id, admin_state_up=ADMIN_STATE_UP) as firewall: req = self.new_show_request('firewalls', firewall['firewall']['id'], fmt=self.fmt) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) for k, v in six.iteritems(attrs): self.assertEqual(v, res['firewall'][k]) def test_list_firewalls(self): with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall(name='fw1', tenant_id='tenant1', firewall_policy_id=fwp_id, description='fw') as fw1, \ self.firewall(name='fw2', tenant_id='tenant2', firewall_policy_id=fwp_id, description='fw') as fw2, \ self.firewall(name='fw3', tenant_id='tenant3', firewall_policy_id=fwp_id, description='fw') as fw3: fwalls = [fw1, fw2, fw3] self._test_list_resources('firewall', fwalls, query_params='description=fw') def test_update_firewall(self): name = "new_firewall1" attrs = self._get_test_firewall_attrs(name) with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] attrs['firewall_policy_id'] = fwp_id with self.firewall( firewall_policy_id=fwp_id, admin_state_up=ADMIN_STATE_UP) as firewall: data = {'firewall': {'name': name}} req = self.new_update_request('firewalls', data, firewall['firewall']['id']) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) for k, v in six.iteritems(attrs): self.assertEqual(v, res['firewall'][k]) def test_update_firewall_with_fwp(self): ctx = context.Context('not_admin', 'tenant1') with self.firewall_policy() as fwp1, \ self.firewall_policy( tenant_id='tenant1', shared=False) as fwp2, \ self.firewall(firewall_policy_id=fwp1['firewall_policy']['id'], context=ctx) as fw: fw_id = fw['firewall']['id'] fwp2_id = fwp2['firewall_policy']['id'] data = {'firewall': {'firewall_policy_id': fwp2_id}} req = self.new_update_request('firewalls', data, fw_id, context=ctx) res = req.get_response(self.ext_api) self.assertEqual(200, res.status_int) def test_update_firewall_with_shared_fwp(self): ctx = context.Context('not_admin', 'tenant1') with self.firewall_policy() as fwp1, \ self.firewall_policy(tenant_id='tenant2') as fwp2, \ self.firewall(firewall_policy_id=fwp1['firewall_policy']['id'], context=ctx) as fw: fw_id = fw['firewall']['id'] fwp2_id = fwp2['firewall_policy']['id'] data = {'firewall': {'firewall_policy_id': fwp2_id}} req = self.new_update_request('firewalls', data, fw_id, context=ctx) res = req.get_response(self.ext_api) self.assertEqual(200, res.status_int) def test_update_firewall_with_admin_and_fwp_different_tenant(self): ctx = context.get_admin_context() with self.firewall_policy() as fwp1, \ self.firewall_policy( tenant_id='tenant2', shared=False) as fwp2, \ self.firewall(firewall_policy_id=fwp1['firewall_policy']['id'], context=ctx) as fw: fw_id = fw['firewall']['id'] fwp2_id = fwp2['firewall_policy']['id'] data = {'firewall': {'firewall_policy_id': fwp2_id}} req = self.new_update_request('firewalls', data, fw_id, context=ctx) res = req.get_response(self.ext_api) self.assertEqual(409, res.status_int) def test_update_firewall_fwp_not_found_on_different_tenant(self): with self.firewall_policy(name='fwp1', tenant_id='tenant1', do_delete=False) as fwp1, \ self.firewall_policy(name='fwp2', shared=False, tenant_id='tenant2') as fwp2: fwps = [fwp1, fwp2] # create firewall using fwp1 exists the same tenant. fwp1_id = fwps[0]['firewall_policy']['id'] fwp2_id = fwps[1]['firewall_policy']['id'] ctx = context.Context('not_admin', 'tenant1') with self.firewall(firewall_policy_id=fwp1_id, context=ctx) as firewall: fw_id = firewall['firewall']['id'] fw_db = self.plugin._get_firewall(ctx, fw_id) fw_db['status'] = nl_constants.ACTIVE # update firewall from fwp1 to fwp2(different tenant) data = {'firewall': {'firewall_policy_id': fwp2_id}} req = self.new_update_request('firewalls', data, fw_id, context=ctx) res = req.get_response(self.ext_api) self.assertEqual(404, res.status_int) def test_delete_firewall(self): ctx = context.get_admin_context() with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall(firewall_policy_id=fwp_id, do_delete=False) as fw: fw_id = fw['firewall']['id'] req = self.new_delete_request('firewalls', fw_id) res = req.get_response(self.ext_api) self.assertEqual(204, res.status_int) self.assertRaises(f_exc.FirewallNotFound, self.plugin.get_firewall, ctx, fw_id) def test_insert_rule_in_policy_with_prior_rules_added_via_update(self): attrs = self._get_test_firewall_policy_attrs() attrs['audited'] = False attrs['firewall_list'] = [] with self.firewall_rule(name='fwr1') as fwr1, \ self.firewall_rule(name='fwr2') as fwr2, \ self.firewall_rule(name='fwr3') as fwr3: frs = [fwr1, fwr2, fwr3] fr1 = frs[0:2] fwr3 = frs[2] with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] attrs['id'] = fwp_id fw_rule_ids = [r['firewall_rule']['id'] for r in fr1] attrs['firewall_rules'] = fw_rule_ids[:] data = {'firewall_policy': {'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp_id) req.get_response(self.ext_api) self._rule_action('insert', fwp_id, fw_rule_ids[0], insert_before=fw_rule_ids[0], insert_after=None, expected_code=webob.exc.HTTPConflict.code, expected_body=None) fwr3_id = fwr3['firewall_rule']['id'] attrs['firewall_rules'].insert(0, fwr3_id) self._rule_action('insert', fwp_id, fwr3_id, insert_before=fw_rule_ids[0], insert_after=None, expected_code=webob.exc.HTTPOk.code, expected_body=attrs) def test_insert_rule_in_policy_failures(self): with self.firewall_rule(name='fwr1') as fr1: with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] fr1_id = fr1['firewall_rule']['id'] fw_rule_ids = [fr1_id] data = {'firewall_policy': {'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp_id) req.get_response(self.ext_api) # test inserting with empty request body self._rule_action('insert', fwp_id, '123', expected_code=webob.exc.HTTPBadRequest.code, expected_body=None, body_data={}) # test inserting when firewall_rule_id is missing in # request body insert_data = {'insert_before': '123', 'insert_after': '456'} self._rule_action('insert', fwp_id, '123', expected_code=webob.exc.HTTPBadRequest.code, expected_body=None, body_data=insert_data) # test inserting when firewall_rule_id is None insert_data = {'firewall_rule_id': None, 'insert_before': '123', 'insert_after': '456'} self._rule_action('insert', fwp_id, '123', expected_code=webob.exc.HTTPNotFound.code, expected_body=None, body_data=insert_data) # test inserting when firewall_policy_id is incorrect self._rule_action('insert', '123', fr1_id, expected_code=webob.exc.HTTPNotFound.code, expected_body=None) # test inserting when firewall_policy_id is None self._rule_action('insert', None, fr1_id, expected_code=webob.exc.HTTPBadRequest.code, expected_body=None) def test_insert_rule_for_previously_associated_rule(self): with self.firewall_rule() as fwr: fwr_id = fwr['firewall_rule']['id'] fw_rule_ids = [fwr_id] with self.firewall_policy(firewall_rules=fw_rule_ids): with self.firewall_policy(name='firewall_policy2') as fwp: fwp_id = fwp['firewall_policy']['id'] insert_data = {'firewall_rule_id': fwr_id} self._rule_action( 'insert', fwp_id, fwr_id, insert_before=None, insert_after=None, expected_code=webob.exc.HTTPConflict.code, expected_body=None, body_data=insert_data) def test_insert_rule_for_prev_associated_ref_rule(self): with self.firewall_rule(name='fwr0') as fwr0, \ self.firewall_rule(name='fwr1') as fwr1: fwr = [fwr0, fwr1] fwr0_id = fwr[0]['firewall_rule']['id'] fwr1_id = fwr[1]['firewall_rule']['id'] with self.firewall_policy(name='fwp0') as fwp0, \ self.firewall_policy(name='fwp1', firewall_rules=[fwr1_id]) as fwp1: fwp = [fwp0, fwp1] fwp0_id = fwp[0]['firewall_policy']['id'] # test inserting before a rule which # is associated with different policy self._rule_action('insert', fwp0_id, fwr0_id, insert_before=fwr1_id, expected_code=webob.exc.HTTPBadRequest.code, expected_body=None) # test inserting after a rule which # is associated with different policy self._rule_action('insert', fwp0_id, fwr0_id, insert_after=fwr1_id, expected_code=webob.exc.HTTPBadRequest.code, expected_body=None) def test_insert_rule_for_policy_of_other_tenant(self): with self.firewall_rule(tenant_id='tenant-2', shared=False) as fwr: fwr_id = fwr['firewall_rule']['id'] with self.firewall_policy(name='firewall_policy') as fwp: fwp_id = fwp['firewall_policy']['id'] insert_data = {'firewall_rule_id': fwr_id} self._rule_action( 'insert', fwp_id, fwr_id, insert_before=None, insert_after=None, expected_code=webob.exc.HTTPConflict.code, expected_body=None, body_data=insert_data) def test_insert_rule_in_policy(self): attrs = self._get_test_firewall_policy_attrs() attrs['audited'] = False attrs['firewall_list'] = [] with self.firewall_rule(name='fwr0') as fwr0, \ self.firewall_rule(name='fwr1') as fwr1, \ self.firewall_rule(name='fwr2') as fwr2, \ self.firewall_rule(name='fwr3') as fwr3, \ self.firewall_rule(name='fwr4') as fwr4, \ self.firewall_rule(name='fwr5') as fwr5, \ self.firewall_rule(name='fwr6') as fwr6: fwr = [fwr0, fwr1, fwr2, fwr3, fwr4, fwr5, fwr6] with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] attrs['id'] = fwp_id # test insert when rule list is empty fwr0_id = fwr[0]['firewall_rule']['id'] attrs['firewall_rules'].insert(0, fwr0_id) self._rule_action('insert', fwp_id, fwr0_id, insert_before=None, insert_after=None, expected_code=webob.exc.HTTPOk.code, expected_body=attrs) # test insert at top of rule list, insert_before and # insert_after not provided fwr1_id = fwr[1]['firewall_rule']['id'] attrs['firewall_rules'].insert(0, fwr1_id) insert_data = {'firewall_rule_id': fwr1_id} self._rule_action('insert', fwp_id, fwr0_id, expected_code=webob.exc.HTTPOk.code, expected_body=attrs, body_data=insert_data) # test insert at top of list above existing rule fwr2_id = fwr[2]['firewall_rule']['id'] attrs['firewall_rules'].insert(0, fwr2_id) self._rule_action('insert', fwp_id, fwr2_id, insert_before=fwr1_id, insert_after=None, expected_code=webob.exc.HTTPOk.code, expected_body=attrs) # test insert at bottom of list fwr3_id = fwr[3]['firewall_rule']['id'] attrs['firewall_rules'].append(fwr3_id) self._rule_action('insert', fwp_id, fwr3_id, insert_before=None, insert_after=fwr0_id, expected_code=webob.exc.HTTPOk.code, expected_body=attrs) # test insert in the middle of the list using # insert_before fwr4_id = fwr[4]['firewall_rule']['id'] attrs['firewall_rules'].insert(1, fwr4_id) self._rule_action('insert', fwp_id, fwr4_id, insert_before=fwr1_id, insert_after=None, expected_code=webob.exc.HTTPOk.code, expected_body=attrs) # test insert in the middle of the list using # insert_after fwr5_id = fwr[5]['firewall_rule']['id'] attrs['firewall_rules'].insert(1, fwr5_id) self._rule_action('insert', fwp_id, fwr5_id, insert_before=None, insert_after=fwr2_id, expected_code=webob.exc.HTTPOk.code, expected_body=attrs) # test insert when both insert_before and # insert_after are set fwr6_id = fwr[6]['firewall_rule']['id'] attrs['firewall_rules'].insert(1, fwr6_id) self._rule_action('insert', fwp_id, fwr6_id, insert_before=fwr5_id, insert_after=fwr5_id, expected_code=webob.exc.HTTPOk.code, expected_body=attrs) def test_remove_rule_and_not_associated(self): with self.firewall_rule(name='fwr0') as fwr1, \ self.firewall_rule(name='fwr2') as fwr2: associated = fwr1['firewall_rule']['id'] with self.firewall_policy( name='firewall_policy2', firewall_rules=[associated]) as fwp: fwp_id = fwp['firewall_policy']['id'] not_associated = fwr2['firewall_rule']['id'] msg = "Firewall rule {0} is not associated with " \ "firewall policy {1}.".format(not_associated, fwp_id) result = self._rule_action( 'remove', fwp_id, not_associated, insert_before=None, insert_after=None, expected_code=webob.exc.HTTPBadRequest.code, body_data={'firewall_rule_id': not_associated}) self.assertEqual(msg, result['NeutronError']['message']) def test_remove_rule_from_policy(self): attrs = self._get_test_firewall_policy_attrs() attrs['audited'] = False attrs['firewall_list'] = [] with self.firewall_rule(name='fwr1') as fwr1, \ self.firewall_rule(name='fwr2') as fwr2, \ self.firewall_rule(name='fwr3') as fwr3: fr1 = [fwr1, fwr2, fwr3] with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] attrs['id'] = fwp_id fw_rule_ids = [r['firewall_rule']['id'] for r in fr1] attrs['firewall_rules'] = fw_rule_ids[:] data = {'firewall_policy': {'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp_id) req.get_response(self.ext_api) # test removing a rule from a policy that does not exist self._rule_action('remove', '123', fw_rule_ids[1], expected_code=webob.exc.HTTPNotFound.code, expected_body=None) # test removing a rule in the middle of the list attrs['firewall_rules'].remove(fw_rule_ids[1]) self._rule_action('remove', fwp_id, fw_rule_ids[1], expected_body=attrs) # test removing a rule at the top of the list attrs['firewall_rules'].remove(fw_rule_ids[0]) self._rule_action('remove', fwp_id, fw_rule_ids[0], expected_body=attrs) # test removing remaining rule in the list attrs['firewall_rules'].remove(fw_rule_ids[2]) self._rule_action('remove', fwp_id, fw_rule_ids[2], expected_body=attrs) # test removing rule that is not associated with the policy self._rule_action('remove', fwp_id, fw_rule_ids[2], expected_code=webob.exc.HTTPBadRequest.code, expected_body=None) def test_remove_rule_from_policy_failures(self): with self.firewall_rule(name='fwr1') as fr1: with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] fw_rule_ids = [fr1['firewall_rule']['id']] data = {'firewall_policy': {'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp_id) req.get_response(self.ext_api) # test removing rule that does not exist self._rule_action('remove', fwp_id, '123', expected_code=webob.exc.HTTPNotFound.code, expected_body=None) # test removing rule with bad request self._rule_action('remove', fwp_id, '123', expected_code=webob.exc.HTTPBadRequest.code, expected_body=None, body_data={}) # test removing rule with firewall_rule_id set to None self._rule_action('remove', fwp_id, '123', expected_code=webob.exc.HTTPNotFound.code, expected_body=None, body_data={'firewall_rule_id': None}) def test_check_router_has_no_firewall_raises(self): fw_plugin = mock.Mock() directory.add_plugin('FIREWALL', fw_plugin) fw_plugin.get_firewalls.return_value = [mock.ANY] kwargs = { 'context': mock.ANY, 'router': {'id': 'foo_id', 'tenant_id': 'foo_tenant'} } self.assertRaises( l3.RouterInUse, fdb.migration_callback, 'router', 'before_event', mock.ANY, **kwargs) def test_check_router_has_no_firewall_passes(self): with mock.patch.object(directory, 'get_plugin', return_value=None): kwargs = {'context': mock.ANY, 'router': mock.ANY} self.assertIsNone(fdb.migration_callback( mock.ANY, mock.ANY, mock.ANY, **kwargs)) def test_show_firewall_rule_by_name(self): with self.firewall_rule(name='firewall_Rule1') as fw_rule: res = self._show('firewall_rules', fw_rule['firewall_rule']['id']) self.assertEqual('firewall_Rule1', res['firewall_rule']['name']) def test_show_firewall_policy_by_name(self): with self.firewall_policy( name='firewall_Policy1') as fw_policy: res = self._show('firewall_policies', fw_policy['firewall_policy']['id']) self.assertEqual( 'firewall_Policy1', res['firewall_policy']['name']) def test_show_firewall_by_name(self): with self.firewall(name='fireWall1') as fw: res = self._show('firewalls', fw['firewall']['id']) self.assertEqual('fireWall1', res['firewall']['name']) def test_create_firewall_rule_with_invalid_action_type(self): attrs = self._get_test_firewall_rule_attrs() attrs['action'] = 123 res = self._create_firewall_rule(self.fmt, **attrs) self.assertEqual(400, res.status_int) neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/db/firewall/__init__.py0000664000175000017500000000000013555577713025716 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/db/firewall/v2/0000775000175000017500000000000013555600005024123 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/db/firewall/v2/__init__.py0000664000175000017500000000000013555577713026245 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/db/firewall/v2/test_firewall_db_v2.py0000664000175000017500000030517413555577713030452 0ustar zuulzuul00000000000000# Copyright (c) 2016 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 contextlib import mock from neutron.api import extensions as api_ext from neutron.common import config from neutron_lib.api.definitions import firewall_v2 from neutron_lib import constants as nl_constants from neutron_lib import context from neutron_lib.exceptions import firewall_v2 as f_exc from neutron_lib.plugins import directory from oslo_config import cfg from oslo_utils import importutils from oslo_utils import uuidutils import six import testtools import webob.exc from neutron_fwaas.common import fwaas_constants as constants from neutron_fwaas.db.firewall.v2 import firewall_db_v2 as fdb from neutron_fwaas import extensions from neutron_fwaas.services.firewall import fwaas_plugin_v2 from neutron_fwaas.tests import base DB_FW_PLUGIN_KLASS = ( "neutron_fwaas.db.firewall.v2.firewall_db_v2.Firewall_db_mixin_v2" ) FWAAS_PLUGIN = 'neutron_fwaas.services.firewall.fwaas_plugin_v2' DELETEFW_PATH = FWAAS_PLUGIN + '.FirewallAgentApi.delete_firewall_group' extensions_path = ':'.join(extensions.__path__) DESCRIPTION = 'default description' PROTOCOL = 'tcp' IP_VERSION = 4 SOURCE_IP_ADDRESS_RAW = '1.1.1.1' DESTINATION_IP_ADDRESS_RAW = '2.2.2.2' SOURCE_PORT = '55000:56000' DESTINATION_PORT = '56000:57000' ACTION = 'allow' AUDITED = True ENABLED = True ADMIN_STATE_UP = True SHARED = True class FakeAgentApi(fwaas_plugin_v2.FirewallCallbacks): """ This class used to mock the AgentAPI delete method inherits from FirewallCallbacks because it needs access to the firewall_deleted method. The delete_firewall method belongs to the FirewallAgentApi, which has no access to the firewall_deleted method normally because it's not responsible for deleting the firewall from the DB. However, it needs to in the unit tests since there is no agent to call back. """ def __init__(self): pass def delete_firewall_group(self, context, firewall_group, **kwargs): self.plugin = directory.get_plugin('FIREWALL_V2') self.firewall_group_deleted(context, firewall_group['id'], **kwargs) class FirewallPluginV2DbTestCase(base.NeutronDbPluginV2TestCase): resource_prefix_map = dict( (k, firewall_v2.API_PREFIX) for k in firewall_v2.RESOURCE_ATTRIBUTE_MAP.keys() ) def setUp(self, core_plugin=None, fw_plugin=None, ext_mgr=None): self.agentapi_delf_p = mock.patch( DELETEFW_PATH, create=True, new=FakeAgentApi().delete_firewall_group) self.agentapi_delf_p.start() if not fw_plugin: fw_plugin = DB_FW_PLUGIN_KLASS service_plugins = {'fw_plugin_name': fw_plugin} fdb.Firewall_db_mixin_v2.supported_extension_aliases = ["fwaas_v2"] fdb.Firewall_db_mixin_v2.path_prefix = firewall_v2.API_PREFIX super(FirewallPluginV2DbTestCase, self).setUp( ext_mgr=ext_mgr, service_plugins=service_plugins ) if not ext_mgr: self.plugin = importutils.import_object(fw_plugin) ext_mgr = api_ext.PluginAwareExtensionManager( extensions_path, {'FIREWALL': self.plugin} ) app = config.load_paste_app('extensions_test_app') self.ext_api = api_ext.ExtensionMiddleware(app, ext_mgr=ext_mgr) router_distributed_opts = [ cfg.BoolOpt( 'router_distributed', default=False, help=("System-wide flag to determine the type of router " "that tenants can create. Only admin can override.")), ] cfg.CONF.register_opts(router_distributed_opts) def _get_admin_context(self): # FIXME NOTE(ivasilevskaya) seems that test framework treats context # with user_id=None/tenant_id=None (return value of # context._get_admin_context() method) in a somewhat special way. # So as a workaround to have the framework behave properly right now # let's implement our own _get_admin_context method and look into the # matter some other time. return context.Context(user_id='admin', tenant_id='admin-tenant', is_admin=True) def _get_nonadmin_context(self, user_id=None, tenant_id=None): return context.Context(user_id=user_id or 'non-admin', tenant_id=tenant_id or 'tenant1') def _test_list_resources(self, resource, items, neutron_context=None, query_params=None): if resource.endswith('y'): resource_plural = resource.replace('y', 'ies') else: resource_plural = resource + 's' res = self._list(resource_plural, neutron_context=neutron_context, query_params=query_params) resource = resource.replace('-', '_') self.assertEqual( sorted([i[resource]['id'] for i in items]), sorted([i['id'] for i in res[resource_plural]])) def _list_req(self, resource_plural, ctx=None): if not ctx: ctx = self._get_admin_context() req = self.new_list_request(resource_plural) req.environ['neutron.context'] = ctx return self.deserialize( self.fmt, req.get_response(self.ext_api))[resource_plural] def _show_req(self, resource_plural, obj_id, ctx=None): req = self.new_show_request(resource_plural, obj_id, fmt=self.fmt) if not ctx: ctx = self._get_admin_context() req.environ['neutron.context'] = ctx res = self.deserialize( self.fmt, req.get_response(self.ext_api)) return res def _build_default_fwg(self, ctx=None, is_one=True): res = self._list_req('firewall_groups', ctx=ctx) if is_one: self.assertEqual(1, len(res)) return res[0] return res def _get_test_firewall_rule_attrs(self, name='firewall_rule1'): attrs = {'name': name, 'tenant_id': self._tenant_id, 'project_id': self._tenant_id, 'protocol': PROTOCOL, 'ip_version': IP_VERSION, 'source_ip_address': SOURCE_IP_ADDRESS_RAW, 'destination_ip_address': DESTINATION_IP_ADDRESS_RAW, 'source_port': SOURCE_PORT, 'destination_port': DESTINATION_PORT, 'action': ACTION, 'enabled': ENABLED, 'shared': SHARED} return attrs def _get_test_firewall_policy_attrs(self, name='firewall_policy1', audited=AUDITED): attrs = {'name': name, 'description': DESCRIPTION, 'tenant_id': self._tenant_id, 'project_id': self._tenant_id, 'firewall_rules': [], 'audited': audited, 'shared': SHARED} return attrs def _get_test_firewall_group_attrs(self, name='firewall_1', status='PENDING_CREATE'): attrs = {'name': name, 'tenant_id': self._tenant_id, 'project_id': self._tenant_id, 'admin_state_up': ADMIN_STATE_UP, 'status': status} return attrs def _create_firewall_policy(self, fmt, name, description, shared, firewall_rules, audited, expected_res_status=None, **kwargs): tenant_id = kwargs.get('tenant_id', self._tenant_id) data = {'firewall_policy': {'name': name, 'description': description, 'tenant_id': tenant_id, 'project_id': tenant_id, 'firewall_rules': firewall_rules, 'audited': audited, 'shared': shared}} fw_policy_req = self.new_create_request('firewall_policies', data, fmt) fw_policy_res = fw_policy_req.get_response(self.ext_api) if expected_res_status: self.assertEqual(expected_res_status, fw_policy_res.status_int) return fw_policy_res def _replace_firewall_status(self, attrs, old_status, new_status): if attrs['status'] is old_status: attrs['status'] = new_status return attrs @contextlib.contextmanager def firewall_policy(self, fmt=None, name='firewall_policy1', description=DESCRIPTION, shared=SHARED, firewall_rules=None, audited=True, do_delete=True, **kwargs): if firewall_rules is None: firewall_rules = [] if not fmt: fmt = self.fmt res = self._create_firewall_policy(fmt, name, description, shared, firewall_rules, audited, **kwargs) if res.status_int >= 400: raise webob.exc.HTTPClientError(code=res.status_int) firewall_policy = self.deserialize(fmt or self.fmt, res) yield firewall_policy if do_delete: self._delete('firewall_policies', firewall_policy['firewall_policy']['id']) def _create_firewall_rule(self, fmt, name, shared, protocol, ip_version, source_ip_address, destination_ip_address, source_port, destination_port, action, enabled, expected_res_status=None, **kwargs): tenant_id = kwargs.get('tenant_id', self._tenant_id) data = {'firewall_rule': {'name': name, 'tenant_id': tenant_id, 'project_id': tenant_id, 'protocol': protocol, 'ip_version': ip_version, 'source_ip_address': source_ip_address, 'destination_ip_address': destination_ip_address, 'source_port': source_port, 'destination_port': destination_port, 'action': action, 'enabled': enabled, 'shared': shared}} fw_rule_req = self.new_create_request('firewall_rules', data, fmt) fw_rule_res = fw_rule_req.get_response(self.ext_api) if expected_res_status: self.assertEqual(expected_res_status, fw_rule_res.status_int) return fw_rule_res @contextlib.contextmanager def firewall_rule(self, fmt=None, name='firewall_rule1', shared=SHARED, protocol=PROTOCOL, ip_version=IP_VERSION, source_ip_address=SOURCE_IP_ADDRESS_RAW, destination_ip_address=DESTINATION_IP_ADDRESS_RAW, source_port=SOURCE_PORT, destination_port=DESTINATION_PORT, action=ACTION, enabled=ENABLED, do_delete=True, **kwargs): if not fmt: fmt = self.fmt res = self._create_firewall_rule(fmt, name, shared, protocol, ip_version, source_ip_address, destination_ip_address, source_port, destination_port, action, enabled, **kwargs) if res.status_int >= 400: raise webob.exc.HTTPClientError(code=res.status_int) firewall_rule = self.deserialize(fmt or self.fmt, res) yield firewall_rule if do_delete: self._delete('firewall_rules', firewall_rule['firewall_rule']['id']) def _create_firewall_group(self, fmt, name, description, ingress_firewall_policy_id, egress_firewall_policy_id, ports=None, admin_state_up=True, expected_res_status=None, **kwargs): tenant_id = kwargs.get('tenant_id', self._tenant_id) if ingress_firewall_policy_id is None: default_policy = kwargs.get('default_policy', True) if default_policy: res = self._create_firewall_policy(fmt, 'fwp', description=DESCRIPTION, shared=SHARED, firewall_rules=[], audited=AUDITED) firewall_policy = self.deserialize(fmt or self.fmt, res) fwp_id = firewall_policy["firewall_policy"]["id"] ingress_firewall_policy_id = fwp_id data = {'firewall_group': {'name': name, 'description': description, 'ingress_firewall_policy_id': ingress_firewall_policy_id, 'egress_firewall_policy_id': egress_firewall_policy_id, 'admin_state_up': admin_state_up}} ctx = kwargs.get('context', None) if ctx is None or ctx.is_admin: data['firewall_group'].update({'tenant_id': tenant_id}) data['firewall_group'].update({'project_id': tenant_id}) if ports is not None: data['firewall_group'].update({'ports': ports}) firewall_req = self.new_create_request('firewall_groups', data, fmt, context=ctx) firewall_res = firewall_req.get_response(self.ext_api) if expected_res_status: self.assertEqual(expected_res_status, firewall_res.status_int) return firewall_res @contextlib.contextmanager def firewall_group(self, fmt=None, name='firewall_1', description=DESCRIPTION, ingress_firewall_policy_id=None, egress_firewall_policy_id=None, ports=None, admin_state_up=True, do_delete=True, **kwargs): if not fmt: fmt = self.fmt res = self._create_firewall_group(fmt, name, description, ingress_firewall_policy_id, egress_firewall_policy_id, ports=ports, admin_state_up=admin_state_up, **kwargs) if res.status_int >= 400: raise webob.exc.HTTPClientError(code=res.status_int) firewall_group = self.deserialize(fmt or self.fmt, res) yield firewall_group if do_delete: self._delete('firewall_groups', firewall_group['firewall_group']['id']) def _rule_action(self, action, id, firewall_rule_id, insert_before=None, insert_after=None, expected_code=webob.exc.HTTPOk.code, expected_body=None, body_data=None): # We intentionally do this check for None since we want to distinguish # from empty dictionary if body_data is None: if action == 'insert': body_data = {'firewall_rule_id': firewall_rule_id, 'insert_before': insert_before, 'insert_after': insert_after} else: body_data = {'firewall_rule_id': firewall_rule_id} req = self.new_action_request('firewall_policies', body_data, id, "%s_rule" % action) res = req.get_response(self.ext_api) self.assertEqual(expected_code, res.status_int) response = self.deserialize(self.fmt, res) if expected_body: self.assertEqual(expected_body, response) return response def _compare_firewall_rule_lists(self, firewall_policy_id, observed_list, expected_list): position = 0 for r1, r2 in zip(observed_list, expected_list): rule = r1['firewall_rule'] rule['firewall_policy_id'] = firewall_policy_id position += 1 rule['position'] = position for k in rule: self.assertEqual(r2[k], rule[k]) class TestFirewallDBPluginV2(FirewallPluginV2DbTestCase): def test_get_policy_ordered_rules(self): with self.firewall_rule(name='alone'), \ self.firewall_rule(name='fwr1') as fwr1, \ self.firewall_rule(name='fwr3') as fwr3, \ self.firewall_rule(name='fwr2') as fwr2: fwrs = fwr1, fwr2, fwr3 expected_ids = [fwr['firewall_rule']['id'] for fwr in fwrs] with self.firewall_policy(firewall_rules=expected_ids) as fwp: ctx = self._get_admin_context() fwp_id = fwp['firewall_policy']['id'] observeds = self.plugin._get_policy_ordered_rules(ctx, fwp_id) observed_ids = [r['id'] for r in observeds] self.assertEqual(expected_ids, observed_ids) def test_create_firewall_policy(self): name = "firewall_policy1" attrs = self._get_test_firewall_policy_attrs(name) with self.firewall_policy(name=name, shared=SHARED, firewall_rules=None, audited=AUDITED ) as firewall_policy: for k, v in six.iteritems(attrs): self.assertEqual(v, firewall_policy['firewall_policy'][k]) def test_create_firewall_policy_with_rules(self): name = "firewall_policy1" attrs = self._get_test_firewall_policy_attrs(name) with self.firewall_rule(name='fwr1') as fwr1, \ self.firewall_rule(name='fwr2') as fwr2, \ self.firewall_rule(name='fwr3') as fwr3: fr = [fwr1, fwr2, fwr3] fw_rule_ids = [r['firewall_rule']['id'] for r in fr] attrs['firewall_rules'] = fw_rule_ids with self.firewall_policy(name=name, shared=SHARED, firewall_rules=fw_rule_ids, audited=AUDITED) as fwp: for k, v in six.iteritems(attrs): self.assertEqual(v, fwp['firewall_policy'][k]) def test_create_admin_firewall_policy_with_other_tenant_rules(self): with self.firewall_rule(shared=False) as fr: fw_rule_ids = [fr['firewall_rule']['id']] res = self._create_firewall_policy(None, 'firewall_policy1', description=DESCRIPTION, shared=SHARED, firewall_rules=fw_rule_ids, audited=AUDITED, tenant_id='admin-tenant') self.assertEqual(webob.exc.HTTPNotFound.code, res.status_int) def test_create_firewall_policy_with_previously_associated_rule(self): with self.firewall_rule() as fwr: fw_rule_ids = [fwr['firewall_rule']['id']] with self.firewall_policy(firewall_rules=fw_rule_ids): with self.firewall_policy(shared=SHARED, firewall_rules=fw_rule_ids) as fwp2: self.assertEqual( fwr['firewall_rule']['id'], fwp2['firewall_policy']['firewall_rules'][0]) def test_create_shared_firewall_policy_with_nonshared_rule(self): with self.firewall_rule(shared=False) as fwr: fw_rule_ids = [fwr['firewall_rule']['id']] res = self._create_firewall_policy(None, 'firewall_policy1', description=DESCRIPTION, shared=SHARED, firewall_rules=fw_rule_ids, audited=AUDITED) self.assertEqual(webob.exc.HTTPNotFound.code, res.status_int) def test_show_firewall_policy(self): name = "firewall_policy1" attrs = self._get_test_firewall_policy_attrs(name) with self.firewall_policy(name=name, shared=SHARED, firewall_rules=None, audited=AUDITED) as fwp: res = self._show_req('firewall_policies', fwp['firewall_policy']['id']) for k, v in six.iteritems(attrs): self.assertEqual(v, res['firewall_policy'][k]) def test_list_firewall_policies(self): with self.firewall_policy(name='fwp1', description='fwp') as fwp1, \ self.firewall_policy(name='fwp2', description='fwp') as fwp2, \ self.firewall_policy(name='fwp3', description='fwp') as fwp3: fw_policies = [fwp1, fwp2, fwp3] self._test_list_resources('firewall_policy', fw_policies, query_params='description=fwp') def test_update_firewall_policy(self): name = "new_firewall_policy1" attrs = self._get_test_firewall_policy_attrs(name, audited=False) with self.firewall_policy(shared=SHARED, firewall_rules=None, audited=AUDITED) as fwp: data = {'firewall_policy': {'name': name}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) for k, v in six.iteritems(attrs): self.assertEqual(v, res['firewall_policy'][k]) def _test_update_firewall_policy(self, with_audited): with self.firewall_policy(name='firewall_policy1', description='fwp', audited=AUDITED) as fwp: attrs = self._get_test_firewall_policy_attrs(audited=with_audited) data = {'firewall_policy': {'description': 'fw_p1'}} if with_audited: data['firewall_policy']['audited'] = 'True' req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) attrs['description'] = 'fw_p1' for k, v in six.iteritems(attrs): self.assertEqual(v, res['firewall_policy'][k]) def test_update_firewall_policy_set_audited_false(self): self._test_update_firewall_policy(with_audited=False) def test_update_firewall_policy_with_audited_set_true(self): self._test_update_firewall_policy(with_audited=True) def test_update_firewall_policy_with_rules(self): attrs = self._get_test_firewall_policy_attrs() with self.firewall_rule(name='fwr1') as fwr1, \ self.firewall_rule(name='fwr2') as fwr2, \ self.firewall_rule(name='fwr3') as fwr3: with self.firewall_policy() as fwp: fr = [fwr1, fwr2, fwr3] fw_rule_ids = [r['firewall_rule']['id'] for r in fr] attrs['firewall_rules'] = fw_rule_ids data = {'firewall_policy': {'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) attrs['audited'] = False attrs['firewall_rules'] = sorted(attrs['firewall_rules']) # TODO(sridar): set it so that the ordering is maintained res['firewall_policy']['firewall_rules'] = sorted( res['firewall_policy']['firewall_rules']) for k, v in six.iteritems(attrs): self.assertEqual(v, res['firewall_policy'][k]) def test_update_firewall_policy_replace_rules(self): attrs = self._get_test_firewall_policy_attrs() with self.firewall_rule(name='fwr1') as fwr1, \ self.firewall_rule(name='fwr2') as fwr2, \ self.firewall_rule(name='fwr3') as fwr3, \ self.firewall_rule(name='fwr4') as fwr4: frs = [fwr1, fwr2, fwr3, fwr4] fr1 = frs[0:2] fr2 = frs[2:4] with self.firewall_policy() as fwp: fw_rule_ids = [r['firewall_rule']['id'] for r in fr1] data = {'firewall_policy': {'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) req.get_response(self.ext_api) fw_rule_ids = [r['firewall_rule']['id'] for r in fr2] attrs['firewall_rules'] = fw_rule_ids new_data = {'firewall_policy': {'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', new_data, fwp['firewall_policy']['id']) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) attrs['audited'] = False for k, v in six.iteritems(attrs): self.assertEqual(v, res['firewall_policy'][k]) @testtools.skip('bug/1614673') def test_update_firewall_policy_reorder_rules(self): attrs = self._get_test_firewall_policy_attrs() with self.firewall_rule(name='fwr1') as fwr1, \ self.firewall_rule(name='fwr2') as fwr2, \ self.firewall_rule(name='fwr3') as fwr3, \ self.firewall_rule(name='fwr4') as fwr4: fr = [fwr1, fwr2, fwr3, fwr4] with self.firewall_policy() as fwp: fw_rule_ids = [fr[2]['firewall_rule']['id'], fr[3]['firewall_rule']['id']] data = {'firewall_policy': {'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) req.get_response(self.ext_api) # shuffle the rules, add more rules fw_rule_ids = [fr[1]['firewall_rule']['id'], fr[3]['firewall_rule']['id'], fr[2]['firewall_rule']['id'], fr[0]['firewall_rule']['id']] attrs['firewall_rules'] = fw_rule_ids data = {'firewall_policy': {'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) rules = [] for rule_id in fw_rule_ids: res = self._show_req('firewall_rules', rule_id) rules.append(res['firewall_rule']) self.assertEqual(1, rules[0]['position']) self.assertEqual(fr[1]['firewall_rule']['id'], rules[0]['id']) self.assertEqual(2, rules[1]['position']) self.assertEqual(fr[3]['firewall_rule']['id'], rules[1]['id']) self.assertEqual(3, rules[2]['position']) self.assertEqual(fr[2]['firewall_rule']['id'], rules[2]['id']) self.assertEqual(4, rules[3]['position']) self.assertEqual(fr[0]['firewall_rule']['id'], rules[3]['id']) def test_update_firewall_policy_with_non_existing_rule(self): attrs = self._get_test_firewall_policy_attrs() with self.firewall_rule(name='fwr1') as fwr1, \ self.firewall_rule(name='fwr2') as fwr2: fr = [fwr1, fwr2] with self.firewall_policy() as fwp: fw_rule_ids = [r['firewall_rule']['id'] for r in fr] # appending non-existent rule fw_rule_ids.append(uuidutils.generate_uuid()) data = {'firewall_policy': {'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) res = req.get_response(self.ext_api) # check that the firewall_rule was not found self.assertEqual(404, res.status_int) # check if none of the rules got added to the policy res = self._show_req('firewall_policies', fwp['firewall_policy']['id']) for k, v in six.iteritems(attrs): self.assertEqual(v, res['firewall_policy'][k]) def test_update_shared_firewall_policy_with_nonshared_rule(self): with self.firewall_rule(name='fwr1', shared=False) as fr: with self.firewall_policy() as fwp: fw_rule_ids = [fr['firewall_rule']['id']] # update shared policy with nonshared rule data = {'firewall_policy': {'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) res = req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPNotFound.code, res.status_int) def test_update_firewall_policy_with_shared_attr_nonshared_rule(self): with self.firewall_rule(name='fwr1', shared=False) as fr: with self.firewall_policy(shared=False) as fwp: fw_rule_ids = [fr['firewall_rule']['id']] # update shared policy with shared attr and nonshared rule data = {'firewall_policy': {'shared': SHARED, 'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) res = req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPNotFound.code, res.status_int) def test_update_firewall_policy_with_shared_attr_exist_unshare_rule(self): with self.firewall_rule(name='fwr1', shared=False) as fr: fw_rule_ids = [fr['firewall_rule']['id']] with self.firewall_policy(shared=False, firewall_rules=fw_rule_ids) as fwp: # update policy with shared attr data = {'firewall_policy': {'shared': SHARED}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) res = req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_update_firewall_policy_assoc_with_other_tenant_firewall(self): with self.firewall_policy(shared=SHARED, tenant_id='tenant1') as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall_group(ingress_firewall_policy_id=fwp_id, egress_firewall_policy_id=fwp_id): data = {'firewall_policy': {'shared': False}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) res = req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_update_firewall_policy_from_shared_to_unshared(self): with self.firewall_policy(shared=True) as fwp: # update policy with public attr data = {'firewall_policy': {'shared': False}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) res = req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPOk.code, res.status_int) def test_update_from_shared_to_unshared_associated_as_ingress_fwp(self): with self.firewall_policy(shared=True, tenant_id='here') as fwp: # update policy with public attr fwp_id = fwp['firewall_policy']['id'] with self.firewall_group(tenant_id='another', ingress_firewall_policy_id=fwp_id): data = {'firewall_policy': {'shared': False}} req = self.new_update_request('firewall_policies', data, fwp_id) res = req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_update_from_shared_to_unshared_associated_as_egress_fwp(self): with self.firewall_policy(shared=True, tenant_id='here') as fwp: # update policy with public attr fwp_id = fwp['firewall_policy']['id'] with self.firewall_group(tenant_id='another', egress_firewall_policy_id=fwp_id): data = {'firewall_policy': {'shared': False}} req = self.new_update_request('firewall_policies', data, fwp_id) res = req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_update_from_shared_to_unshared_associated_as_ingress_egress(self): with self.firewall_policy(shared=True, tenant_id='here') as fwp: # update policy with public attr fwp_id = fwp['firewall_policy']['id'] with self.firewall_group(tenant_id='another', egress_firewall_policy_id=fwp_id, ingress_firewall_policy_id=fwp_id): data = {'firewall_policy': {'shared': False}} req = self.new_update_request('firewall_policies', data, fwp_id) res = req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_update_default_fwg_policy(self): """ Make sure that neither admin nor non-admin can update policy associated with default firewall group """ ctx_admin = self._get_admin_context() ctx_nonadmin = self._get_nonadmin_context() for ctx in [ctx_admin, ctx_nonadmin]: self._build_default_fwg(ctx=ctx) policies = self._list_req('firewall_policies') for p in policies: data = {'firewall_policy': {'firewall_rules': []}} req = self.new_update_request('firewall_policies', data, p['id']) res = req.get_response(self.ext_api) self.assertEqual(409, res.status_int) def test_delete_firewall_policy(self): ctx = self._get_admin_context() with self.firewall_policy(do_delete=False) as fwp: fwp_id = fwp['firewall_policy']['id'] req = self.new_delete_request('firewall_policies', fwp_id) res = req.get_response(self.ext_api) self.assertEqual(204, res.status_int) self.assertRaises(f_exc.FirewallPolicyNotFound, self.plugin.get_firewall_policy, ctx, fwp_id) @testtools.skip('bug/1614673') def test_delete_firewall_policy_with_rule(self): ctx = self._get_admin_context() attrs = self._get_test_firewall_policy_attrs() with self.firewall_policy(do_delete=False) as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall_rule(name='fwr1') as fr: fr_id = fr['firewall_rule']['id'] fw_rule_ids = [fr_id] attrs['firewall_rules'] = fw_rule_ids data = {'firewall_policy': {'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) req.get_response(self.ext_api) fw_rule = self.plugin.get_firewall_rule(ctx, fr_id) self.assertEqual(fwp_id, fw_rule['ingress_firewall_policy_id']) req = self.new_delete_request('firewall_policies', fwp_id) res = req.get_response(self.ext_api) self.assertEqual(204, res.status_int) self.assertRaises(f_exc.FirewallPolicyNotFound, self.plugin.get_firewall_policy, ctx, fwp_id) fw_rule = self.plugin.get_firewall_rule(ctx, fr_id) self.assertIsNone(fw_rule['ingress_firewall_policy_id']) def test_delete_firewall_policy_with_firewall_group_association(self): attrs = self._get_test_firewall_group_attrs() with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] attrs['firewall_policy_id'] = fwp_id with self.firewall_group( ingress_firewall_policy_id=fwp_id, admin_state_up=ADMIN_STATE_UP): req = self.new_delete_request('firewall_policies', fwp_id) res = req.get_response(self.ext_api) self.assertEqual(409, res.status_int) def test_create_firewall_rule(self): attrs = self._get_test_firewall_rule_attrs() with self.firewall_rule() as firewall_rule: for k, v in six.iteritems(attrs): self.assertEqual(v, firewall_rule['firewall_rule'][k]) attrs['source_port'] = None attrs['destination_port'] = None with self.firewall_rule(source_port=None, destination_port=None) as firewall_rule: for k, v in six.iteritems(attrs): self.assertEqual(v, firewall_rule['firewall_rule'][k]) attrs['source_port'] = '10000' attrs['destination_port'] = '80' with self.firewall_rule(source_port=10000, destination_port=80) as firewall_rule: for k, v in six.iteritems(attrs): self.assertEqual(v, firewall_rule['firewall_rule'][k]) attrs['source_port'] = '10000' attrs['destination_port'] = '80' with self.firewall_rule(source_port='10000', destination_port='80') as firewall_rule: for k, v in six.iteritems(attrs): self.assertEqual(v, firewall_rule['firewall_rule'][k]) def test_create_firewall_src_port_illegal_range(self): attrs = self._get_test_firewall_rule_attrs() attrs['source_port'] = '65535:1024' res = self._create_firewall_rule(self.fmt, **attrs) self.assertEqual(400, res.status_int) def test_create_firewall_dest_port_illegal_range(self): attrs = self._get_test_firewall_rule_attrs() attrs['destination_port'] = '65535:1024' res = self._create_firewall_rule(self.fmt, **attrs) self.assertEqual(400, res.status_int) def test_create_firewall_rule_icmp_with_port(self): attrs = self._get_test_firewall_rule_attrs() attrs['protocol'] = 'icmp' res = self._create_firewall_rule(self.fmt, **attrs) self.assertEqual(400, res.status_int) def test_create_firewall_rule_icmp_without_port(self): attrs = self._get_test_firewall_rule_attrs() attrs['protocol'] = 'icmp' attrs['source_port'] = None attrs['destination_port'] = None with self.firewall_rule(source_port=None, destination_port=None, protocol='icmp') as firewall_rule: for k, v in six.iteritems(attrs): self.assertEqual(v, firewall_rule['firewall_rule'][k]) def test_create_firewall_without_source(self): attrs = self._get_test_firewall_rule_attrs() attrs['source_ip_address'] = None res = self._create_firewall_rule(self.fmt, **attrs) self.assertEqual(201, res.status_int) def test_create_firewall_rule_without_destination(self): attrs = self._get_test_firewall_rule_attrs() attrs['destination_ip_address'] = None res = self._create_firewall_rule(self.fmt, **attrs) self.assertEqual(201, res.status_int) def test_create_firewall_rule_without_protocol_with_dport(self): attrs = self._get_test_firewall_rule_attrs() attrs['protocol'] = None attrs['source_port'] = None res = self._create_firewall_rule(self.fmt, **attrs) self.assertEqual(400, res.status_int) def test_create_firewall_rule_without_protocol_with_sport(self): attrs = self._get_test_firewall_rule_attrs() attrs['protocol'] = None attrs['destination_port'] = None res = self._create_firewall_rule(self.fmt, **attrs) self.assertEqual(400, res.status_int) def test_show_firewall_rule_with_fw_policy_not_associated(self): attrs = self._get_test_firewall_rule_attrs() with self.firewall_rule() as fw_rule: res = self._show_req('firewall_rules', fw_rule['firewall_rule']['id']) for k, v in six.iteritems(attrs): self.assertEqual(v, res['firewall_rule'][k]) @testtools.skip('bug/1614673') def test_show_firewall_rule_with_fw_policy_associated(self): attrs = self._get_test_firewall_rule_attrs() with self.firewall_rule() as fw_rule: with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] attrs['ingress_firewall_policy_id'] = fwp_id data = {'firewall_policy': {'firewall_rules': [fw_rule['firewall_rule']['id']]}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) req.get_response(self.ext_api) res = self._show_req('firewall_rules', fw_rule['firewall_rule']['id']) for k, v in six.iteritems(attrs): self.assertEqual(v, res['firewall_rule'][k]) def test_create_firewall_rule_with_ipv6_addrs_and_wrong_ip_version(self): attrs = self._get_test_firewall_rule_attrs() attrs['source_ip_address'] = '::/0' attrs['destination_ip_address'] = '2001:db8:3::/64' attrs['ip_version'] = 4 res = self._create_firewall_rule(self.fmt, **attrs) self.assertEqual(400, res.status_int) attrs = self._get_test_firewall_rule_attrs() attrs['source_ip_address'] = None attrs['destination_ip_address'] = '2001:db8:3::/64' attrs['ip_version'] = 4 res = self._create_firewall_rule(self.fmt, **attrs) self.assertEqual(400, res.status_int) attrs = self._get_test_firewall_rule_attrs() attrs['source_ip_address'] = '::/0' attrs['destination_ip_address'] = None attrs['ip_version'] = 4 res = self._create_firewall_rule(self.fmt, **attrs) self.assertEqual(400, res.status_int) def test_list_firewall_rules(self): with self.firewall_rule(name='fwr1') as fwr1, \ self.firewall_rule(name='fwr2') as fwr2, \ self.firewall_rule(name='fwr3') as fwr3: fr = [fwr1, fwr2, fwr3] query_params = 'protocol=tcp' self._test_list_resources('firewall_rule', fr, query_params=query_params) def test_update_firewall_rule(self): name = "new_firewall_rule1" attrs = self._get_test_firewall_rule_attrs(name) attrs['source_port'] = '10:20' attrs['destination_port'] = '30:40' with self.firewall_rule() as fwr: data = {'firewall_rule': {'name': name, 'protocol': PROTOCOL, 'source_port': '10:20', 'destination_port': '30:40'}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id']) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) for k, v in six.iteritems(attrs): self.assertEqual(v, res['firewall_rule'][k]) attrs['source_port'] = '10000' attrs['destination_port'] = '80' with self.firewall_rule() as fwr: data = {'firewall_rule': {'name': name, 'protocol': PROTOCOL, 'source_port': 10000, 'destination_port': 80}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id']) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) for k, v in six.iteritems(attrs): self.assertEqual(v, res['firewall_rule'][k]) attrs['source_port'] = '10000' attrs['destination_port'] = '80' with self.firewall_rule() as fwr: data = {'firewall_rule': {'name': name, 'protocol': PROTOCOL, 'source_port': '10000', 'destination_port': '80'}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id']) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) for k, v in six.iteritems(attrs): self.assertEqual(v, res['firewall_rule'][k]) attrs['source_port'] = None attrs['destination_port'] = None with self.firewall_rule() as fwr: data = {'firewall_rule': {'name': name, 'source_port': None, 'destination_port': None}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id']) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) for k, v in six.iteritems(attrs): self.assertEqual(v, res['firewall_rule'][k]) def test_update_firewall_rule_with_port_and_no_proto(self): with self.firewall_rule() as fwr: data = {'firewall_rule': {'protocol': None, 'destination_port': 80}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id']) res = req.get_response(self.ext_api) self.assertEqual(400, res.status_int) def test_update_firewall_rule_without_ports_and_no_proto(self): with self.firewall_rule() as fwr: data = {'firewall_rule': {'protocol': None, 'destination_port': None, 'source_port': None}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id']) res = req.get_response(self.ext_api) self.assertEqual(200, res.status_int) def test_update_firewall_rule_with_port(self): with self.firewall_rule(source_port=None, destination_port=None, protocol=None) as fwr: data = {'firewall_rule': {'destination_port': 80}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id']) res = req.get_response(self.ext_api) self.assertEqual(400, res.status_int) def test_update_firewall_rule_with_port_illegal_range(self): with self.firewall_rule() as fwr: data = {'firewall_rule': {'destination_port': '65535:1024'}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id']) res = req.get_response(self.ext_api) self.assertEqual(400, res.status_int) def test_update_firewall_rule_with_port_and_protocol(self): with self.firewall_rule(source_port=None, destination_port=None, protocol=None) as fwr: data = {'firewall_rule': {'destination_port': 80, 'protocol': 'tcp'}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id']) res = req.get_response(self.ext_api) self.assertEqual(200, res.status_int) def test_update_firewall_rule_icmp_with_port(self): with self.firewall_rule(source_port=None, destination_port=None, protocol=None) as fwr: data = {'firewall_rule': {'destination_port': 80, 'protocol': 'icmp'}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id']) res = req.get_response(self.ext_api) self.assertEqual(400, res.status_int) with self.firewall_rule(source_port=None, destination_port=None, protocol='icmp') as fwr: data = {'firewall_rule': {'destination_port': 80}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id']) res = req.get_response(self.ext_api) self.assertEqual(400, res.status_int) def test_update_firewall_rule_with_policy_associated(self): name = "new_firewall_rule1" attrs = self._get_test_firewall_rule_attrs(name) with self.firewall_rule() as fwr: with self.firewall_policy() as fwp: fwr_id = fwr['firewall_rule']['id'] data = {'firewall_policy': {'firewall_rules': [fwr_id]}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) req.get_response(self.ext_api) data = {'firewall_rule': {'name': name}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id']) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) for k, v in six.iteritems(attrs): self.assertEqual(v, res['firewall_rule'][k]) res = self._show_req('firewall_policies', fwp['firewall_policy']['id']) self.assertEqual( [fwr_id], res['firewall_policy']['firewall_rules']) self.assertFalse(res['firewall_policy']['audited']) @testtools.skip('bug/1614680') def test_update_firewall_rule_associated_with_other_tenant_policy(self): with self.firewall_rule(shared=SHARED, tenant_id='tenant1') as fwr: fwr_id = [fwr['firewall_rule']['id']] with self.firewall_policy(shared=False, firewall_rules=fwr_id): data = {'firewall_rule': {'shared': False}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id']) res = req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_update_firewall_rule_with_ipv6_ipaddr(self): with self.firewall_rule(source_ip_address="1::10", destination_ip_address=None, ip_version=6) as fwr_v6: data = {'firewall_rule': { 'destination_ip_address': "2::20"}} req = self.new_update_request('firewall_rules', data, fwr_v6['firewall_rule']['id']) res = req.get_response(self.ext_api) self.assertEqual(200, res.status_int) def test_delete_firewall_rule(self): ctx = self._get_admin_context() with self.firewall_rule(do_delete=False) as fwr: fwr_id = fwr['firewall_rule']['id'] req = self.new_delete_request('firewall_rules', fwr_id) res = req.get_response(self.ext_api) self.assertEqual(204, res.status_int) self.assertRaises(f_exc.FirewallRuleNotFound, self.plugin.get_firewall_rule, ctx, fwr_id) def test_delete_firewall_rule_with_policy_associated(self): with self.firewall_rule() as fwr: with self.firewall_policy() as fwp: fwr_id = fwr['firewall_rule']['id'] data = {'firewall_policy': {'firewall_rules': [fwr_id]}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) res = req.get_response(self.ext_api) req = self.new_delete_request('firewall_rules', fwr_id) res = req.get_response(self.ext_api) self.assertEqual(409, res.status_int) def _test_create_firewall_group(self, attrs): with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] attrs['ingress_firewall_policy_id'] = fwp_id attrs['egress_firewall_policy_id'] = fwp_id with self.firewall_group( name=attrs['name'], ingress_firewall_policy_id=fwp_id, egress_firewall_policy_id=fwp_id, admin_state_up=ADMIN_STATE_UP, ports=attrs['ports'] if 'ports' in attrs else None, ) as firewall_group: for k, v in six.iteritems(attrs): self.assertEqual(v, firewall_group['firewall_group'][k]) def test_create_firewall_group(self): attrs = self._get_test_firewall_group_attrs("firewall1") self._test_create_firewall_group(attrs) def test_create_firewall_group_with_ports(self): with self.port( device_owner=nl_constants.DEVICE_OWNER_ROUTER_INTF) as dummy_port: attrs = self._get_test_firewall_group_attrs("fwg1") attrs['ports'] = [dummy_port['port']['id']] self._test_create_firewall_group(attrs) def test_create_firewall_group_with_empty_ports(self): attrs = self._get_test_firewall_group_attrs("fwg1") attrs['ports'] = [] self._test_create_firewall_group(attrs) def test_create_default_firewall_group_multiple_times_diff_tenants(self): ctx_admin = self._get_admin_context() fwg_admin = self._build_default_fwg(ctx=ctx_admin) res = self._build_default_fwg(ctx=ctx_admin, is_one=False) # check that only 1 group has been created self.assertEqual(1, len(res)) ctx = self._get_nonadmin_context() fwg_na = self._build_default_fwg(ctx=ctx) res = self._build_default_fwg(ctx=ctx, is_one=False) # check that only 1 group has been created self.assertEqual(1, len(res)) # make sure that admin default_fwg and non_admin don't match self.assertNotEqual(fwg_na['id'], fwg_admin['id']) # make sure that admin can see default groups for admin and non-admin res = self._list_req('firewall_groups', ctx=ctx_admin) self.assertEqual(2, len(res)) self.assertEqual(set([ctx_admin.tenant_id, ctx.tenant_id]), set([r['tenant_id'] for r in res])) def test_create_default_firewall_group(self): self._build_default_fwg() result_map = { 'firewall_groups': {"keys": ["description", "name"], "data": [("Default firewall group", constants.DEFAULT_FWG)] }, 'firewall_policies': { "keys": ["description", "name"], "data": [("Ingress firewall policy", constants.DEFAULT_FWP_INGRESS), ("Egress firewall policy", constants.DEFAULT_FWP_EGRESS)]}, 'firewall_rules': { "keys": ["description", "action", "protocol", "enabled", "ip_version", "name"], "data": [ ("default ingress rule for IPv4", "deny", None, True, 4, "default ingress ipv4 (deny all)"), ("default egress rule for IPv4", "allow", None, True, 4, "default egress ipv4 (allow all)"), ("default ingress rule for IPv6", "deny", None, True, 6, "default ingress ipv6 (deny all)"), ("default egress rule for IPv6", "allow", None, True, 6, "default egress ipv6 (allow all)")] } } def _check_rules_match_policies(policy, direction): if direction in policy["description"].lower(): for rule_id in policy['firewall_rules']: rule = self._show_req( 'firewall_rules', rule_id)['firewall_rule'] self.assertTrue(direction in rule["description"]) for obj in result_map: res = self._list_req(obj) check_keys = result_map[obj]["keys"] expected = result_map[obj]["data"] self.assertEqual(len(expected), len(res)) # an attempt to check that rules match policies if obj == 'firewall_policies': for p in res: _check_rules_match_policies(p, "ingress") _check_rules_match_policies(p, "egress") # check that a rule with given params is present in actual # data by comparing expected/actual tuples actual = [] for r in res: actual.append(tuple(r[key] for key in check_keys)) self.assertEqual(set(expected), set(actual)) def test_create_firewall_group_exists_default(self): self._build_default_fwg()['id'] attrs = self._get_test_firewall_group_attrs("firewall1") self._test_create_firewall_group(attrs) def test_create_firewall_group_with_dvr(self): cfg.CONF.set_override('router_distributed', True) attrs = self._get_test_firewall_group_attrs("firewall1", "CREATED") self._test_create_firewall_group(attrs) def test_create_firewall_group_with_fwp_does_not_exist(self): fmt = self.fmt fwg_name = "firewall1" description = "my_firewall1" not_found_fwp_id = uuidutils.generate_uuid() self._create_firewall_group(fmt, fwg_name, description, not_found_fwp_id, not_found_fwp_id, ports=None, admin_state_up=ADMIN_STATE_UP, expected_res_status=404) def test_create_firewall_group_with_fwp_on_different_tenant(self): fmt = self.fmt fwg_name = "firewall1" description = "my_firewall1" with self.firewall_policy(shared=False, tenant_id='tenant2') as fwp: fwp_id = fwp['firewall_policy']['id'] ctx = self._get_nonadmin_context() self._create_firewall_group(fmt, fwg_name, description, ingress_firewall_policy_id=fwp_id, egress_firewall_policy_id=fwp_id, context=ctx, expected_res_status=404) def test_create_firewall_group_with_admin_and_fwp_different_tenant(self): fmt = self.fmt fwg_name = "firewall1" description = "my_firewall1" with self.firewall_policy(shared=False, tenant_id='tenant2') as fwp: fwp_id = fwp['firewall_policy']['id'] ctx = self._get_admin_context() self._create_firewall_group(fmt, fwg_name, description, fwp_id, fwp_id, tenant_id="admin-tenant", context=ctx, expected_res_status=404) def test_create_firewall_group_with_admin_and_fwp_is_shared(self): fwg_name = "fw_with_shared_fwp" with self.firewall_policy(tenant_id="tenantX") as fwp: fwp_id = fwp['firewall_policy']['id'] ctx = self._get_admin_context() target_tenant = 'tenant1' with self.firewall_group(name=fwg_name, ingress_firewall_policy_id=fwp_id, tenant_id=target_tenant, context=ctx, admin_state_up=ADMIN_STATE_UP) as fwg: self.assertEqual(target_tenant, fwg['firewall_group']['tenant_id']) def _test_show_firewall_group(self, attrs): with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] attrs['ingress_firewall_policy_id'] = fwp_id attrs['egress_firewall_policy_id'] = fwp_id attrs['status'] = 'PENDING_CREATE' with self.firewall_group( name=attrs['name'], ports=attrs['ports'] if 'ports' in attrs else None, ingress_firewall_policy_id=fwp_id, egress_firewall_policy_id=fwp_id, admin_state_up=ADMIN_STATE_UP) as firewall_group: res = self._show_req('firewall_groups', firewall_group['firewall_group']['id']) for k, v in six.iteritems(attrs): self.assertEqual(v, res['firewall_group'][k]) def test_show_firewall_group(self): attrs = self._get_test_firewall_group_attrs('fwg1') self._test_show_firewall_group(attrs) def test_show_firewall_group_with_ports(self): attrs = self._get_test_firewall_group_attrs('fwg1') with self.port( device_owner=nl_constants.DEVICE_OWNER_ROUTER_INTF) as dummy_port: attrs['ports'] = [dummy_port['port']['id']] self._test_show_firewall_group(attrs) def test_show_firewall_group_with_empty_ports(self): attrs = self._get_test_firewall_group_attrs('fwg1') attrs['ports'] = [] self._test_show_firewall_group(attrs) def test_list_firewall_groups(self): with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall_group(name='fwg1', tenant_id='tenant1', ingress_firewall_policy_id=fwp_id, description='fwg') as fwg1, \ self.firewall_group(name='fwg2', tenant_id='tenant2', ingress_firewall_policy_id=fwp_id, egress_firewall_policy_id=fwp_id, description='fwg') as fwg2, \ self.firewall_group(name='fwg3', tenant_id='tenant3', ingress_firewall_policy_id=fwp_id, egress_firewall_policy_id=fwp_id, description='fwg') as fwg3: fwgrps = [fwg1, fwg2, fwg3] self._test_list_resources('firewall_group', fwgrps, query_params='description=fwg') def test_update_firewall_group(self): name = "new_firewall1" attrs = self._get_test_firewall_group_attrs(name) with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall_group( ingress_firewall_policy_id=fwp_id, admin_state_up=ADMIN_STATE_UP) as firewall: data = {'firewall_group': {'name': name}} req = self.new_update_request('firewall_groups', data, firewall['firewall_group']['id']) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) for k, v in six.iteritems(attrs): self.assertEqual(v, res['firewall_group'][k]) def test_existing_default_create_default_firewall_group(self): self._build_default_fwg() self._create_firewall_group(fmt=None, name=constants.DEFAULT_FWG, description="", ingress_firewall_policy_id=None, egress_firewall_policy_id=None, expected_res_status=409) def test_update_default_firewall_group_with_non_admin_success(self): ctx = self._get_nonadmin_context() def_fwg_id = self._build_default_fwg(ctx=ctx)['id'] with self.port( device_owner=nl_constants.DEVICE_OWNER_ROUTER_INTF, ctx=ctx) as dummy_port: port_id = dummy_port['port']['id'] success_cases = [ {'ports': [port_id]}, {'ports': []}, {'ports': None}, {}, ] for attr in success_cases: data = {'firewall_group': attr} req = self.new_update_request( 'firewall_groups', data, def_fwg_id) req.environ['neutron.context'] = ctx res = req.get_response(self.ext_api) self.assertEqual(200, res.status_int) def test_update_default_firewall_group_with_non_admin_failure(self): ctx = self._get_nonadmin_context() def_fwg_id = self._build_default_fwg(ctx=ctx)['id'] with self.port( device_owner=nl_constants.DEVICE_OWNER_ROUTER_INTF, ctx=ctx) as dummy_port: port_id = dummy_port['port']['id'] conflict_cases = [ {'name': ''}, {'name': 'default'}, {'name': 'non-default'}, {'ingress_firewall_policy_id': None}, {'egress_firewall_policy_id': None}, {'description': 'try to modify'}, {'admin_state_up': True}, {'ports': [port_id], 'name': ''}, {'ports': [], 'name': 'default'}, {'ports': None, 'name': 'non-default'}, ] for attr in conflict_cases: data = {'firewall_group': attr} req = self.new_update_request( 'firewall_groups', data, def_fwg_id) req.environ['neutron.context'] = ctx res = req.get_response(self.ext_api) self.assertEqual(409, res.status_int) def test_update_default_firewall_group_with_admin_success(self): ctx = self._get_admin_context() with self.port( device_owner=nl_constants.DEVICE_OWNER_ROUTER_INTF, ctx=ctx) as dummy_port: port_id = dummy_port['port']['id'] def_fwg_id = self._build_default_fwg(ctx=ctx)['id'] success_cases = [ {'ports': [port_id]}, {'ports': []}, {'ports': None}, {'ingress_firewall_policy_id': None}, {'egress_firewall_policy_id': None}, {'description': 'try to modify'}, {'admin_state_up': True}, {}, ] for attr in success_cases: data = {'firewall_group': attr} req = self.new_update_request( 'firewall_groups', data, def_fwg_id) req.environ['neutron.context'] = ctx res = req.get_response(self.ext_api) self.assertEqual(200, res.status_int) def test_update_default_firewall_group_with_admin_failure(self): ctx = self._get_admin_context() with self.port( device_owner=nl_constants.DEVICE_OWNER_ROUTER_INTF, ctx=ctx) as dummy_port: port_id = dummy_port['port']['id'] def_fwg_id = self._build_default_fwg(ctx=ctx)['id'] conflict_cases = [ {'name': 'default'}, {'name': 'non-default'}, {'name': ''}, {'ports': [port_id], 'name': ''}, {'ports': [], 'name': 'default'}, {'ports': None, 'name': 'non-default'}, ] for attr in conflict_cases: data = {'firewall_group': attr} req = self.new_update_request( 'firewall_groups', data, def_fwg_id) req.environ['neutron.context'] = ctx res = req.get_response(self.ext_api) self.assertEqual(409, res.status_int) def test_update_firewall_group_with_fwp(self): ctx = self._get_nonadmin_context() with self.firewall_policy(name='p1', tenant_id=ctx.tenant_id, shared=False) as fwp1, \ self.firewall_policy(name='p2', tenant_id=ctx.tenant_id, shared=False) as fwp2, \ self.firewall_group( ingress_firewall_policy_id=fwp1['firewall_policy']['id'], egress_firewall_policy_id=fwp2['firewall_policy']['id'], context=ctx) as fw: fw_id = fw['firewall_group']['id'] fwp2_id = fwp2['firewall_policy']['id'] data = {'firewall_group': {'ingress_firewall_policy_id': fwp2_id}} req = self.new_update_request('firewall_groups', data, fw_id, context=ctx) res = req.get_response(self.ext_api) self.assertEqual(200, res.status_int) def test_change_fwg_name_to_default(self): """ Make sure that neither admin nor non-admin can change name of existing firewall group to default """ admin_ctx = self._get_admin_context() nonadmin_ctx = self._get_nonadmin_context() with self.firewall_group(context=nonadmin_ctx) as fwg: data = {'firewall_group': {'name': constants.DEFAULT_FWG}} fwg_id = fwg['firewall_group']['id'] for ctx in [admin_ctx, nonadmin_ctx]: req = self.new_update_request('firewall_groups', data, fwg_id, context=ctx) res = req.get_response(self.ext_api) self.assertEqual(409, res.status_int) @testtools.skip('bug/1614680') def test_update_firewall_group_with_shared_fwp(self): ctx = self._get_nonadmin_context() with self.firewall_policy(name='p1', tenant_id=ctx.tenant_id, shared=True) as fwp1, \ self.firewall_policy(name='p2', tenant_id='tenant2', shared=True) as fwp2, \ self.firewall_group( ingress_firewall_policy_id=fwp1['firewall_policy']['id'], egress_firewall_policy_id=fwp1['firewall_policy']['id'], context=ctx) as fw: fw_id = fw['firewall_group']['id'] fwp2_id = fwp2['firewall_policy']['id'] data = {'firewall_group': {'ingress_firewall_policy_id': fwp2_id}} req = self.new_update_request('firewall_groups', data, fw_id, context=ctx) res = req.get_response(self.ext_api) self.assertEqual(200, res.status_int) def test_update_firewall_group_with_admin_and_fwp_different_tenant(self): ctx = self._get_admin_context() with self.firewall_policy() as fwp1, \ self.firewall_policy(tenant_id='tenant2', shared=False) as fwp2, \ self.firewall_group( ingress_firewall_policy_id=fwp1['firewall_policy']['id'], egress_firewall_policy_id=fwp1['firewall_policy']['id'], context=ctx) as fw: fw_id = fw['firewall_group']['id'] fwp2_id = fwp2['firewall_policy']['id'] data = {'firewall_group': {'egress_firewall_policy_id': fwp2_id}} req = self.new_update_request('firewall_groups', data, fw_id, context=ctx) res = req.get_response(self.ext_api) self.assertEqual(404, res.status_int) def test_update_firewall_group_fwp_not_found_on_different_tenant(self): with self.firewall_policy(name='fwp1', tenant_id='tenant1', do_delete=False) as fwp1, \ self.firewall_policy(name='fwp2', shared=False, tenant_id='tenant2') as fwp2: fwps = [fwp1, fwp2] # create firewall using fwp1 exists the same tenant. fwp1_id = fwps[0]['firewall_policy']['id'] fwp2_id = fwps[1]['firewall_policy']['id'] ctx = self._get_nonadmin_context() with self.firewall_group(ingress_firewall_policy_id=fwp1_id, context=ctx) as firewall: fw_id = firewall['firewall_group']['id'] fw_db = self.plugin._get_firewall_group(ctx, fw_id) fw_db['status'] = nl_constants.ACTIVE # update firewall from fwp1 to fwp2(different tenant) data = {'firewall_group': {'ingress_firewall_policy_id': fwp2_id}} req = self.new_update_request('firewall_groups', data, fw_id, context=ctx) res = req.get_response(self.ext_api) self.assertEqual(404, res.status_int) def test_delete_firewall_group(self): ctx = self._get_admin_context() with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall_group(ingress_firewall_policy_id=fwp_id, do_delete=False) as fw: fw_id = fw['firewall_group']['id'] req = self.new_delete_request('firewall_groups', fw_id) res = req.get_response(self.ext_api) self.assertEqual(204, res.status_int) self.assertRaises(f_exc.FirewallGroupNotFound, self.plugin.get_firewall_group, ctx, fw_id) def test_delete_firewall_group_already_deleted(self): ctx = self._get_admin_context() deleted_id = uuidutils.generate_uuid() with self.firewall_group(do_delete=False) as fwg: fwg_id = fwg['firewall_group']['id'] self.assertIsNone(self.plugin.delete_firewall_group(ctx, fwg_id)) self.assertIsNone(self.plugin.delete_firewall_group(ctx, deleted_id)) def test_delete_default_firewall_group_with_admin(self): ctx_a = self._get_admin_context() ctx_na = self._get_nonadmin_context() def_fwg_id = None for ctx in [ctx_na, ctx_a]: def_fwg_id = self._build_default_fwg(ctx=ctx)['id'] req = self.new_delete_request('firewall_groups', def_fwg_id) req.environ['neutron.context'] = ctx_a self.assertEqual(204, req.get_response(self.ext_api).status_int) # check that policy has been deleted by listing as admin and getting 1 # default fwg with a differnt id res = self._list_req('firewall_groups', ctx=ctx_a) self.assertEqual(1, len(res)) self.assertNotEqual(def_fwg_id, res[0]['id']) def test_delete_default_firewall_group_with_non_admin(self): ctx = self._get_nonadmin_context() def_fwg_id = self._build_default_fwg(ctx=ctx)['id'] req = self.new_delete_request('firewall_groups', def_fwg_id) req.environ['neutron.context'] = ctx self.assertEqual(409, req.get_response(self.ext_api).status_int) def test_insert_rule_in_policy_with_prior_rules_added_via_update(self): attrs = self._get_test_firewall_policy_attrs() attrs['audited'] = False with self.firewall_rule(name='fwr1') as fwr1, \ self.firewall_rule(name='fwr2') as fwr2, \ self.firewall_rule(name='fwr3') as fwr3: frs = [fwr1, fwr2, fwr3] fr1 = frs[0:2] fwr3 = frs[2] with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] attrs['id'] = fwp_id fw_rule_ids = [r['firewall_rule']['id'] for r in fr1] attrs['firewall_rules'] = fw_rule_ids[:] data = {'firewall_policy': {'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp_id) req.get_response(self.ext_api) self._rule_action('insert', fwp_id, fw_rule_ids[0], insert_before=fw_rule_ids[0], insert_after=None, expected_code=webob.exc.HTTPConflict.code, expected_body=None) fwr3_id = fwr3['firewall_rule']['id'] attrs['firewall_rules'].insert(0, fwr3_id) self._rule_action('insert', fwp_id, fwr3_id, insert_before=fw_rule_ids[0], insert_after=None, expected_code=webob.exc.HTTPOk.code, expected_body=attrs) def test_insert_rule_in_policy_failures(self): with self.firewall_rule(name='fwr1') as fr1: with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] fr1_id = fr1['firewall_rule']['id'] fw_rule_ids = [fr1_id] data = {'firewall_policy': {'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp_id) req.get_response(self.ext_api) # test inserting with empty request body self._rule_action('insert', fwp_id, '123', expected_code=webob.exc.HTTPBadRequest.code, expected_body=None, body_data={}) # test inserting when firewall_rule_id is missing in # request body insert_data = {'insert_before': '123', 'insert_after': '456'} self._rule_action('insert', fwp_id, '123', expected_code=webob.exc.HTTPBadRequest.code, expected_body=None, body_data=insert_data) # test inserting when firewall_rule_id is None insert_data = {'firewall_rule_id': None, 'insert_before': '123', 'insert_after': '456'} self._rule_action('insert', fwp_id, None, expected_code=webob.exc.HTTPNotFound.code, expected_body=None, body_data=insert_data) # test inserting when firewall_policy_id is incorrect self._rule_action('insert', '123', fr1_id, expected_code=webob.exc.HTTPNotFound.code, expected_body=None) # test inserting when firewall_policy_id is None self._rule_action('insert', None, fr1_id, expected_code=webob.exc.HTTPBadRequest.code, expected_body=None) def test_insert_rule_and_already_associated(self): with self.firewall_rule() as fwr: fwr_id = fwr['firewall_rule']['id'] with self.firewall_policy(firewall_rules=[fwr_id]) as fwp: fwp_id = fwp['firewall_policy']['id'] self._rule_action( 'insert', fwp_id, fwr_id, insert_before=None, insert_after=None, expected_code=webob.exc.HTTPConflict.code, body_data={'firewall_rule_id': fwr_id}) def test_insert_rule_for_previously_associated_rule(self): with self.firewall_rule() as fwr: fwr_id = fwr['firewall_rule']['id'] fw_rule_ids = [fwr_id] with self.firewall_policy(firewall_rules=fw_rule_ids): with self.firewall_policy(name='firewall_policy2') as fwp: fwp_id = fwp['firewall_policy']['id'] insert_data = {'firewall_rule_id': fwr_id} self._rule_action( 'insert', fwp_id, fwr_id, insert_before=None, insert_after=None, expected_code=webob.exc.HTTPOk.code, expected_body=None, body_data=insert_data) def test_insert_rule_for_previously_associated_rule_other_tenant(self): with self.firewall_rule(tenant_id='tenant-2') as fwr: fwr_id = fwr['firewall_rule']['id'] fw_rule_ids = [fwr_id] with self.firewall_policy(tenant_id='tenant-2', firewall_rules=fw_rule_ids): with self.firewall_policy(name='firewall_policy2') as fwp: fwp_id = fwp['firewall_policy']['id'] insert_data = {'firewall_rule_id': fwr_id} self._rule_action( 'insert', fwp_id, fwr_id, insert_before=None, insert_after=None, expected_code=webob.exc.HTTPOk.code, expected_body=None, body_data=insert_data) def test_insert_rule_for_prev_associated_ref_rule(self): with self.firewall_rule(name='fwr0') as fwr0, \ self.firewall_rule(name='fwr1') as fwr1: fwr = [fwr0, fwr1] fwr0_id = fwr[0]['firewall_rule']['id'] fwr1_id = fwr[1]['firewall_rule']['id'] with self.firewall_policy(name='fwp0') as fwp0, \ self.firewall_policy(name='fwp1', firewall_rules=[fwr1_id]) as fwp1: fwp = [fwp0, fwp1] fwp0_id = fwp[0]['firewall_policy']['id'] # test inserting before a rule which # is associated with different policy self._rule_action('insert', fwp0_id, fwr0_id, insert_before=fwr1_id, expected_code=webob.exc.HTTPBadRequest.code, expected_body=None) # test inserting after a rule which # is associated with different policy self._rule_action('insert', fwp0_id, fwr0_id, insert_after=fwr1_id, expected_code=webob.exc.HTTPBadRequest.code, expected_body=None) def test_insert_rule_for_policy_of_other_tenant(self): with self.firewall_rule(tenant_id='tenant-2', shared=False) as fwr: fwr_id = fwr['firewall_rule']['id'] with self.firewall_policy(name='firewall_policy') as fwp: fwp_id = fwp['firewall_policy']['id'] insert_data = {'firewall_rule_id': fwr_id} self._rule_action( 'insert', fwp_id, fwr_id, insert_before=None, insert_after=None, expected_code=webob.exc.HTTPConflict.code, expected_body=None, body_data=insert_data) def test_insert_rule_missing_rule_id(self): with self.firewall_rule(tenant_id='tenant-2', shared=False): with self.firewall_policy(name='firewall_policy') as fwp: fwp_id = fwp['firewall_policy']['id'] insert_data = {} self._rule_action( 'insert', fwp_id, None, insert_before=None, insert_after=None, expected_code=webob.exc.HTTPBadRequest.code, expected_body=None, body_data=insert_data) def test_insert_rule_empty_rule_id(self): with self.firewall_rule(tenant_id='tenant-2', shared=False): with self.firewall_policy(name='firewall_policy') as fwp: fwp_id = fwp['firewall_policy']['id'] insert_data = {'firewall_rule_id': None} self._rule_action( 'insert', fwp_id, None, insert_before=None, insert_after=None, expected_code=webob.exc.HTTPNotFound.code, expected_body=None, body_data=insert_data) def test_insert_rule_invalid_rule_id(self): with self.firewall_rule(tenant_id='tenant-2', shared=False): with self.firewall_policy(name='firewall_policy') as fwp: fwp_id = fwp['firewall_policy']['id'] fwr_id_fake = 'foo' insert_data = {'firewall_rule_id': fwr_id_fake} self._rule_action( 'insert', fwp_id, fwr_id_fake, insert_before=None, insert_after=None, expected_code=webob.exc.HTTPNotFound.code, expected_body=None, body_data=insert_data) def test_insert_rule_nonexistent_rule_id(self): with self.firewall_rule(tenant_id='tenant-2', shared=False): with self.firewall_policy(name='firewall_policy') as fwp: fwp_id = fwp['firewall_policy']['id'] fwr_id_fake = uuidutils.generate_uuid() insert_data = {'firewall_rule_id': fwr_id_fake} self._rule_action( 'insert', fwp_id, fwr_id_fake, insert_before=None, insert_after=None, expected_code=webob.exc.HTTPNotFound.code, expected_body=None, body_data=insert_data) def test_insert_rule_in_policy(self): attrs = self._get_test_firewall_policy_attrs() attrs['audited'] = False with self.firewall_rule(name='fwr0') as fwr0, \ self.firewall_rule(name='fwr1') as fwr1, \ self.firewall_rule(name='fwr2') as fwr2, \ self.firewall_rule(name='fwr3') as fwr3, \ self.firewall_rule(name='fwr4') as fwr4, \ self.firewall_rule(name='fwr5') as fwr5, \ self.firewall_rule(name='fwr6') as fwr6: fwr = [fwr0, fwr1, fwr2, fwr3, fwr4, fwr5, fwr6] with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] attrs['id'] = fwp_id # test insert when rule list is empty fwr0_id = fwr[0]['firewall_rule']['id'] attrs['firewall_rules'].insert(0, fwr0_id) self._rule_action('insert', fwp_id, fwr0_id, insert_before=None, insert_after=None, expected_code=webob.exc.HTTPOk.code, expected_body=attrs) # test insert at top of rule list, insert_before and # insert_after not provided fwr1_id = fwr[1]['firewall_rule']['id'] attrs['firewall_rules'].insert(0, fwr1_id) insert_data = {'firewall_rule_id': fwr1_id} self._rule_action('insert', fwp_id, fwr0_id, expected_code=webob.exc.HTTPOk.code, expected_body=attrs, body_data=insert_data) # test insert at top of list above existing rule fwr2_id = fwr[2]['firewall_rule']['id'] attrs['firewall_rules'].insert(0, fwr2_id) self._rule_action('insert', fwp_id, fwr2_id, insert_before=fwr1_id, insert_after=None, expected_code=webob.exc.HTTPOk.code, expected_body=attrs) # test insert at bottom of list fwr3_id = fwr[3]['firewall_rule']['id'] attrs['firewall_rules'].append(fwr3_id) self._rule_action('insert', fwp_id, fwr3_id, insert_before=None, insert_after=fwr0_id, expected_code=webob.exc.HTTPOk.code, expected_body=attrs) # test insert in the middle of the list using # insert_before fwr4_id = fwr[4]['firewall_rule']['id'] attrs['firewall_rules'].insert(1, fwr4_id) self._rule_action('insert', fwp_id, fwr4_id, insert_before=fwr1_id, insert_after=None, expected_code=webob.exc.HTTPOk.code, expected_body=attrs) # test insert in the middle of the list using # insert_after fwr5_id = fwr[5]['firewall_rule']['id'] attrs['firewall_rules'].insert(1, fwr5_id) self._rule_action('insert', fwp_id, fwr5_id, insert_before=None, insert_after=fwr2_id, expected_code=webob.exc.HTTPOk.code, expected_body=attrs) # test insert when both insert_before and # insert_after are set fwr6_id = fwr[6]['firewall_rule']['id'] attrs['firewall_rules'].insert(1, fwr6_id) self._rule_action('insert', fwp_id, fwr6_id, insert_before=fwr5_id, insert_after=fwr5_id, expected_code=webob.exc.HTTPOk.code, expected_body=attrs) def test_remove_rule_and_not_associated(self): with self.firewall_rule(name='fwr0') as fwr: with self.firewall_policy(name='firewall_policy2') as fwp: fwp_id = fwp['firewall_policy']['id'] fwr_id = fwr['firewall_rule']['id'] msg = "Firewall rule {0} is not associated with " \ "firewall policy {1}.".format(fwr_id, fwp_id) result = self._rule_action( 'remove', fwp_id, fwr_id, insert_before=None, insert_after=None, expected_code=webob.exc.HTTPBadRequest.code, body_data={'firewall_rule_id': fwr_id}) self.assertEqual(msg, result['NeutronError']['message']) def test_remove_rule_from_policy(self): attrs = self._get_test_firewall_policy_attrs() attrs['audited'] = False with self.firewall_rule(name='fwr1') as fwr1, \ self.firewall_rule(name='fwr2') as fwr2, \ self.firewall_rule(name='fwr3') as fwr3: fr1 = [fwr1, fwr2, fwr3] with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] attrs['id'] = fwp_id fw_rule_ids = [r['firewall_rule']['id'] for r in fr1] attrs['firewall_rules'] = fw_rule_ids[:] data = {'firewall_policy': {'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp_id) req.get_response(self.ext_api) # test removing a rule from a policy that does not exist self._rule_action('remove', '123', fw_rule_ids[1], expected_code=webob.exc.HTTPNotFound.code, expected_body=None) # test removing a rule in the middle of the list attrs['firewall_rules'].remove(fw_rule_ids[1]) self._rule_action('remove', fwp_id, fw_rule_ids[1], expected_body=attrs) # test removing a rule at the top of the list attrs['firewall_rules'].remove(fw_rule_ids[0]) self._rule_action('remove', fwp_id, fw_rule_ids[0], expected_body=attrs) # test removing remaining rule in the list attrs['firewall_rules'].remove(fw_rule_ids[2]) self._rule_action('remove', fwp_id, fw_rule_ids[2], expected_body=attrs) # test removing rule that is not associated with the policy self._rule_action('remove', fwp_id, fw_rule_ids[2], expected_code=webob.exc.HTTPBadRequest.code, expected_body=None) def test_remove_rule_from_policy_failures(self): with self.firewall_rule(name='fwr1') as fr1: with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] fw_rule_ids = [fr1['firewall_rule']['id']] data = {'firewall_policy': {'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp_id) req.get_response(self.ext_api) # test removing rule that does not exist self._rule_action('remove', fwp_id, '123', expected_code=webob.exc.HTTPNotFound.code, expected_body=None) # test removing rule with bad request self._rule_action('remove', fwp_id, '123', expected_code=webob.exc.HTTPBadRequest.code, expected_body=None, body_data={}) # test removing rule with firewall_rule_id set to None self._rule_action('remove', fwp_id, '123', expected_code=webob.exc.HTTPNotFound.code, expected_body=None, body_data={'firewall_rule_id': None}) def test_show_firewall_rule_by_name(self): with self.firewall_rule(name='firewall_Rule1') as fw_rule: res = self._show('firewall_rules', fw_rule['firewall_rule']['id']) self.assertEqual('firewall_Rule1', res['firewall_rule']['name']) def test_show_firewall_policy_by_name(self): with self.firewall_policy(name='firewall_Policy1') as fw_policy: res = self._show('firewall_policies', fw_policy['firewall_policy']['id']) self.assertEqual( 'firewall_Policy1', res['firewall_policy']['name']) def test_show_firewall_group_by_name(self): with self.firewall_group(name='fireWall1') as fw: res = self._show('firewall_groups', fw['firewall_group']['id']) self.assertEqual('fireWall1', res['firewall_group']['name']) def test_set_port_in_use_for_firewall_group(self): fwg_db = {'id': 'fake_id'} new_ports = {'ports': ['fake_port1', 'fake_port2']} m_context = context.get_admin_context() with mock.patch.object(m_context.session, 'add', side_effect=[None, f_exc.FirewallGroupPortInUse( port_ids=['fake_port2'])]): self.assertRaises(f_exc.FirewallGroupPortInUse, self.plugin._set_ports_for_firewall_group, m_context, fwg_db, new_ports) def test_set_port_for_default_firewall_group(self): ctx = self._get_nonadmin_context() self._build_default_fwg(ctx=ctx) with self.port(project_id=ctx.tenant_id) as port1, \ self.port(project_id=ctx.tenant_id) as port2: port1_id = port1['port']['id'] port2_id = port2['port']['id'] port_ids = [port1_id, port2_id] project_id = ctx.tenant_id self.plugin.set_port_for_default_firewall_group( ctx, port1_id, project_id) self.plugin.set_port_for_default_firewall_group( ctx, port2_id, project_id) def_fwg_db = self.plugin._get_default_fwg(ctx, project_id) self.assertEqual('PENDING_UPDATE', def_fwg_db['status']) self.assertEqual(sorted(port_ids), sorted(def_fwg_db['ports'])) def test_set_port_for_default_firewall_group_raised_port_in_use(self): ctx = self._get_nonadmin_context() self._build_default_fwg(ctx=ctx) self.plugin.update_firewall_group_status = mock.Mock() with self.port(project_id=ctx.tenant_id) as port1: port1_id = port1['port']['id'] port_ids = [port1_id] self.plugin._set_ports_for_firewall_group = mock.Mock( side_effect=f_exc.FirewallGroupPortInUse(port_ids=port_ids)) project_id = ctx.tenant_id self.plugin.set_port_for_default_firewall_group( ctx, port1_id, project_id) self.plugin.update_firewall_group_status.assert_not_called() neutron-fwaas-12.0.2/neutron_fwaas/tests/unit/db/__init__.py0000664000175000017500000000000013555577713024111 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/tempest_plugin/0000775000175000017500000000000013555600005023462 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/tempest_plugin/services/0000775000175000017500000000000013555600005025305 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/tempest_plugin/services/v2_client.py0000664000175000017500000001111113555577713027562 0ustar zuulzuul00000000000000# 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 tempest.lib import exceptions as lib_exc from tempest.lib.services.network import base class FirewallGroupsClient(base.BaseNetworkClient): def create_firewall_group(self, **kwargs): uri = '/fwaas/firewall_groups' post_data = {'firewall_group': kwargs} return self.create_resource(uri, post_data) def update_firewall_group(self, firewall_group_id, **kwargs): uri = '/fwaas/firewall_groups/%s' % firewall_group_id post_data = {'firewall_group': kwargs} return self.update_resource(uri, post_data) def show_firewall_group(self, firewall_group_id, **fields): uri = '/fwaas/firewall_groups/%s' % firewall_group_id return self.show_resource(uri, **fields) def delete_firewall_group(self, firewall_group_id): uri = '/fwaas/firewall_groups/%s' % firewall_group_id return self.delete_resource(uri) def list_firewall_groups(self, **filters): uri = '/fwaas/firewall_groups' return self.list_resources(uri, **filters) def is_resource_deleted(self, id): try: self.show_firewall_group(id) except lib_exc.NotFound: return True return False @property def resource_type(self): """Returns the primary type of resource this client works with.""" return 'firewall_group' class FirewallRulesClient(base.BaseNetworkClient): def create_firewall_rule(self, **kwargs): uri = '/fwaas/firewall_rules' post_data = {'firewall_rule': kwargs} return self.create_resource(uri, post_data) def update_firewall_rule(self, firewall_rule_id, **kwargs): uri = '/fwaas/firewall_rules/%s' % firewall_rule_id post_data = {'firewall_rule': kwargs} return self.update_resource(uri, post_data) def show_firewall_rule(self, firewall_rule_id, **fields): uri = '/fwaas/firewall_rules/%s' % firewall_rule_id return self.show_resource(uri, **fields) def delete_firewall_rule(self, firewall_rule_id): uri = '/fwaas/firewall_rules/%s' % firewall_rule_id return self.delete_resource(uri) def list_firewall_rules(self, **filters): uri = '/fwaas/firewall_rules' return self.list_resources(uri, **filters) class FirewallPoliciesClient(base.BaseNetworkClient): def create_firewall_policy(self, **kwargs): uri = '/fwaas/firewall_policies' post_data = {'firewall_policy': kwargs} return self.create_resource(uri, post_data) def update_firewall_policy(self, firewall_policy_id, **kwargs): uri = '/fwaas/firewall_policies/%s' % firewall_policy_id post_data = {'firewall_policy': kwargs} return self.update_resource(uri, post_data) def show_firewall_policy(self, firewall_policy_id, **fields): uri = '/fwaas/firewall_policies/%s' % firewall_policy_id return self.show_resource(uri, **fields) def delete_firewall_policy(self, firewall_policy_id): uri = '/fwaas/firewall_policies/%s' % firewall_policy_id return self.delete_resource(uri) def list_firewall_policies(self, **filters): uri = '/fwaas/firewall_policies' return self.list_resources(uri, **filters) def insert_firewall_rule_in_policy(self, firewall_policy_id, firewall_rule_id, insert_after='', insert_before=''): uri = '/fwaas/firewall_policies/%s/insert_rule' % firewall_policy_id data = { 'firewall_rule_id': firewall_rule_id, 'insert_after': insert_after, 'insert_before': insert_before, } return self.update_resource(uri, data) def remove_firewall_rule_from_policy(self, firewall_policy_id, firewall_rule_id): uri = '/fwaas/firewall_policies/%s/remove_rule' % firewall_policy_id data = { 'firewall_rule_id': firewall_rule_id, } return self.update_resource(uri, data) neutron-fwaas-12.0.2/neutron_fwaas/tests/tempest_plugin/services/__init__.py0000664000175000017500000000000013555577713027427 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/tempest_plugin/services/client.py0000664000175000017500000001064713555577713027170 0ustar zuulzuul00000000000000# Copyright (c) 2015 Midokura SARL # 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 tempest.lib import exceptions as lib_exc from tempest.lib.services.network import base class FirewallsClient(base.BaseNetworkClient): def create_firewall(self, **kwargs): uri = '/fw/firewalls' post_data = {'firewall': kwargs} return self.create_resource(uri, post_data) def update_firewall(self, firewall_id, **kwargs): uri = '/fw/firewalls/%s' % firewall_id post_data = {'firewall': kwargs} return self.update_resource(uri, post_data) def show_firewall(self, firewall_id, **fields): uri = '/fw/firewalls/%s' % firewall_id return self.show_resource(uri, **fields) def delete_firewall(self, firewall_id): uri = '/fw/firewalls/%s' % firewall_id return self.delete_resource(uri) def list_firewalls(self, **filters): uri = '/fw/firewalls' return self.list_resources(uri, **filters) def is_resource_deleted(self, id): try: self.show_firewall(id) except lib_exc.NotFound: return True return False @property def resource_type(self): """Returns the primary type of resource this client works with.""" return 'firewall' class FirewallRulesClient(base.BaseNetworkClient): def create_firewall_rule(self, **kwargs): uri = '/fw/firewall_rules' post_data = {'firewall_rule': kwargs} return self.create_resource(uri, post_data) def update_firewall_rule(self, firewall_rule_id, **kwargs): uri = '/fw/firewall_rules/%s' % firewall_rule_id post_data = {'firewall_rule': kwargs} return self.update_resource(uri, post_data) def show_firewall_rule(self, firewall_rule_id, **fields): uri = '/fw/firewall_rules/%s' % firewall_rule_id return self.show_resource(uri, **fields) def delete_firewall_rule(self, firewall_rule_id): uri = '/fw/firewall_rules/%s' % firewall_rule_id return self.delete_resource(uri) def list_firewall_rules(self, **filters): uri = '/fw/firewall_rules' return self.list_resources(uri, **filters) class FirewallPoliciesClient(base.BaseNetworkClient): def create_firewall_policy(self, **kwargs): uri = '/fw/firewall_policies' post_data = {'firewall_policy': kwargs} return self.create_resource(uri, post_data) def update_firewall_policy(self, firewall_policy_id, **kwargs): uri = '/fw/firewall_policies/%s' % firewall_policy_id post_data = {'firewall_policy': kwargs} return self.update_resource(uri, post_data) def show_firewall_policy(self, firewall_policy_id, **fields): uri = '/fw/firewall_policies/%s' % firewall_policy_id return self.show_resource(uri, **fields) def delete_firewall_policy(self, firewall_policy_id): uri = '/fw/firewall_policies/%s' % firewall_policy_id return self.delete_resource(uri) def list_firewall_policies(self, **filters): uri = '/fw/firewall_policies' return self.list_resources(uri, **filters) def insert_firewall_rule_in_policy(self, firewall_policy_id, firewall_rule_id, insert_after='', insert_before=''): uri = '/fw/firewall_policies/%s/insert_rule' % firewall_policy_id data = { 'firewall_rule_id': firewall_rule_id, 'insert_after': insert_after, 'insert_before': insert_before, } return self.update_resource(uri, data) def remove_firewall_rule_from_policy(self, firewall_policy_id, firewall_rule_id): uri = '/fw/firewall_policies/%s/remove_rule' % firewall_policy_id data = { 'firewall_rule_id': firewall_rule_id, } return self.update_resource(uri, data) neutron-fwaas-12.0.2/neutron_fwaas/tests/tempest_plugin/plugin.py0000664000175000017500000000231613555577713025357 0ustar zuulzuul00000000000000# Copyright (c) 2015 Midokura SARL # 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 tempest.test_discover import plugins class NeutronFWaaSPlugin(plugins.TempestPlugin): def get_opt_lists(self): return [] def load_tests(self): this_dir = os.path.dirname(os.path.abspath(__file__)) # top_level_dir = $(this_dir)/../../.. d = os.path.split(this_dir)[0] d = os.path.split(d)[0] top_level_dir = os.path.split(d)[0] test_dir = os.path.join(top_level_dir, 'neutron_fwaas/tests/tempest_plugin/tests') return (test_dir, top_level_dir) def register_opts(self, conf): return neutron-fwaas-12.0.2/neutron_fwaas/tests/tempest_plugin/__init__.py0000664000175000017500000000000013555577713025604 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/tempest_plugin/tests/0000775000175000017500000000000013555600005024624 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/tempest_plugin/tests/fwaas_client.py0000664000175000017500000001152713555577713027666 0ustar zuulzuul00000000000000# Copyright (c) 2015 Midokura SARL # 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 time from tempest import config from tempest.lib.common.utils import data_utils from tempest.lib.common.utils import test_utils from tempest.lib import exceptions as lib_exc from neutron_fwaas.tests.tempest_plugin.services import client from neutron_lib import constants as nl_constants CONF = config.CONF class FWaaSClientMixin(object): @classmethod def resource_setup(cls): super(FWaaSClientMixin, cls).resource_setup() manager = cls.os_primary default_params = config.service_client_config() cls.firewalls_client = client.FirewallsClient( manager.auth_provider, CONF.network.catalog_type, CONF.network.region or CONF.identity.region, endpoint_type=CONF.network.endpoint_type, build_interval=CONF.network.build_interval, build_timeout=CONF.network.build_timeout, **default_params) cls.firewall_policies_client = client.FirewallPoliciesClient( manager.auth_provider, CONF.network.catalog_type, CONF.network.region or CONF.identity.region, endpoint_type=CONF.network.endpoint_type, build_interval=CONF.network.build_interval, build_timeout=CONF.network.build_timeout, **default_params) cls.firewall_rules_client = client.FirewallRulesClient( manager.auth_provider, CONF.network.catalog_type, CONF.network.region or CONF.identity.region, endpoint_type=CONF.network.endpoint_type, build_interval=CONF.network.build_interval, build_timeout=CONF.network.build_timeout, **default_params) def create_firewall_rule(self, **kwargs): body = self.firewall_rules_client.create_firewall_rule( name=data_utils.rand_name("fw-rule"), **kwargs) fw_rule = body['firewall_rule'] self.addCleanup(test_utils.call_and_ignore_notfound_exc, self.firewall_rules_client.delete_firewall_rule, fw_rule['id']) return fw_rule def create_firewall_policy(self, **kwargs): body = self.firewall_policies_client.create_firewall_policy( name=data_utils.rand_name("fw-policy"), **kwargs) fw_policy = body['firewall_policy'] self.addCleanup(test_utils.call_and_ignore_notfound_exc, self.firewall_policies_client.delete_firewall_policy, fw_policy['id']) return fw_policy def create_firewall(self, **kwargs): body = self.firewalls_client.create_firewall( name=data_utils.rand_name("fw"), **kwargs) fw = body['firewall'] self.addCleanup(test_utils.call_and_ignore_notfound_exc, self.delete_firewall_and_wait, fw['id']) return fw def delete_firewall_and_wait(self, firewall_id): self.firewalls_client.delete_firewall(firewall_id) self._wait_firewall_while(firewall_id, [nl_constants.PENDING_DELETE], not_found_ok=True) def _wait_firewall_ready(self, firewall_id): self._wait_firewall_while(firewall_id, [nl_constants.PENDING_CREATE, nl_constants.PENDING_UPDATE]) def _wait_firewall_while(self, firewall_id, statuses, not_found_ok=False): start = int(time.time()) if not_found_ok: expected_exceptions = (lib_exc.NotFound) else: expected_exceptions = () while True: try: fw = self.firewalls_client.show_firewall(firewall_id) except expected_exceptions: break status = fw['firewall']['status'] if status not in statuses: break if int(time.time()) - start >= self.firewalls_client.build_timeout: msg = ("Firewall %(firewall)s failed to reach " "non PENDING status (current %(status)s)") % { "firewall": firewall_id, "status": status, } raise lib_exc.TimeoutException(msg) time.sleep(1) neutron-fwaas-12.0.2/neutron_fwaas/tests/tempest_plugin/tests/scenario/0000775000175000017500000000000013555600005026427 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/tempest_plugin/tests/scenario/test_fwaas_v2.py0000664000175000017500000003151513555577713031600 0ustar zuulzuul00000000000000# Copyright (c) 2016 Juniper Networks # 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 testscenarios from oslo_log import log as logging from tempest import config from tempest.lib.common.utils import test_utils from tempest.lib import decorators from tempest.lib import exceptions as lib_exc from tempest import test from neutron_fwaas.tests.tempest_plugin.tests.scenario import base CONF = config.CONF LOG = logging.getLogger(__name__) load_tests = testscenarios.load_tests_apply_scenarios class TestFWaaS_v2(base.FWaaSScenarioTest_V2): """ Config Requirement in tempest.conf: - project_network_cidr_bits- specifies the subnet range for each network - project_network_cidr - public_network_id """ def setUp(self): LOG.debug("Initializing FWaaSScenarioTest Setup") super(TestFWaaS_v2, self).setUp() required_exts = ['fwaas_v2', 'security-group', 'router'] # if self.router_insertion: # required_exts.append('fwaasrouterinsertion') for ext in required_exts: if not test.is_extension_enabled(ext, 'network'): msg = "%s Extension not enabled." % ext raise self.skipException(msg) LOG.debug("FWaaSScenarioTest Setup done.") def _create_server(self, network, security_group=None): keys = self.create_keypair() kwargs = {} if security_group is not None: kwargs['security_groups'] = [{'name': security_group['name']}] server = self.create_server( key_name=keys['name'], networks=[{'uuid': network['id']}], wait_until='ACTIVE', **kwargs) return server, keys def _check_connectivity_between_internal_networks( self, floating_ip1, keys1, network2, server2, should_connect=True): internal_ips = (p['fixed_ips'][0]['ip_address'] for p in self.os_admin.ports_client.list_ports( tenant_id=server2['tenant_id'], network_id=network2['id'])['ports'] if p['device_owner'].startswith('network')) self._check_server_connectivity( floating_ip1, keys1, internal_ips, should_connect) def _check_server_connectivity(self, floating_ip, keys1, address_list, should_connect=True): ip_address = floating_ip['floating_ip_address'] private_key = keys1 ssh_source = self.get_remote_client( ip_address, private_key=private_key) for remote_ip in address_list: if should_connect: msg = ("Timed out waiting for %s to become " "reachable") % remote_ip else: msg = "ip address %s is reachable" % remote_ip try: self.assertTrue(self._check_remote_connectivity (ssh_source, remote_ip, should_connect), msg) except Exception: LOG.exception("Unable to access {dest} via ssh to " "floating-ip {src}".format(dest=remote_ip, src=floating_ip)) raise def _check_remote_connectivity(self, source, dest, should_succeed=True, nic=None): """check ping server via source ssh connection :param source: RemoteClient: an ssh connection from which to ping :param dest: and IP to ping against :param should_succeed: boolean should ping succeed or not :param nic: specific network interface to ping from :returns: boolean -- should_succeed == ping :returns: ping is false if ping failed """ def ping_remote(): try: source.ping_host(dest, nic=nic) except lib_exc.SSHExecCommandFailed: LOG.warning('Failed to ping IP: %s via a ssh connection ' 'from: %s.', dest, source.ssh_client.host) return not should_succeed return should_succeed return test_utils.call_until_true(ping_remote, CONF.validation.ping_timeout, 1) def _add_router_interface(self, router_id, subnet_id): resp = self.routers_client.add_router_interface( router_id, subnet_id=subnet_id) self.addCleanup(test_utils.call_and_ignore_notfound_exc, self.routers_client.remove_router_interface, router_id, subnet_id=subnet_id) return resp def _create_network_subnet(self): network = self._create_network() subnet_kwargs = dict(network=network) subnet = self._create_subnet(**subnet_kwargs) return network, subnet def _create_test_server(self, network, security_group): pub_network_id = CONF.network.public_network_id server, keys = self._create_server( network, security_group=security_group) private_key = keys['private_key'] server_floating_ip = self.create_floating_ip(server, pub_network_id) fixed_ip = server['addresses'].values()[0][0]['addr'] return server, private_key, fixed_ip, server_floating_ip def _create_topology(self): """ +--------+ +-------------+ |"server"| | "subnet" | | VM-1 +-------------+ "network-1" | +--------+ +----+--------+ | | router interface port +----+-----+ | "router" | +----+-----+ | router interface port | | +--------+ +-------------+ |"server"| | "subnet" | | VM-2 +-------------+ "network-2" | +--------+ +----+--------+ """ LOG.debug('Starting Topology Creation') resp = {} # Create Network1 and Subnet1. network1, subnet1 = self._create_network_subnet() resp['network1'] = network1 resp['subnet1'] = subnet1 # Create Network2 and Subnet2. network2, subnet2 = self._create_network_subnet() resp['network2'] = network2 resp['subnet2'] = subnet2 # Create a router and attach Network1, Network2 and External Networks # to it. router = self._create_router(namestart='SCENARIO-TEST-ROUTER') pub_network_id = CONF.network.public_network_id kwargs = {'external_gateway_info': dict(network_id=pub_network_id)} router = self.routers_client.update_router( router['id'], **kwargs)['router'] router_id = router['id'] resp_add_intf = self._add_router_interface( router_id, subnet_id=subnet1['id']) router_portid_1 = resp_add_intf['port_id'] resp_add_intf = self._add_router_interface( router_id, subnet_id=subnet2['id']) router_portid_2 = resp_add_intf['port_id'] resp['router'] = router resp['router_portid_1'] = router_portid_1 resp['router_portid_2'] = router_portid_2 # Create a VM on each of the network and assign it a floating IP. security_group = self._create_security_group() server1, private_key1, server_fixed_ip_1, server_floating_ip_1 = ( self._create_test_server(network1, security_group)) server2, private_key2, server_fixed_ip_2, server_floating_ip_2 = ( self._create_test_server(network2, security_group)) resp['server1'] = server1 resp['private_key1'] = private_key1 resp['server_fixed_ip_1'] = server_fixed_ip_1 resp['server_floating_ip_1'] = server_floating_ip_1 resp['server2'] = server2 resp['private_key2'] = private_key2 resp['server_fixed_ip_2'] = server_fixed_ip_2 resp['server_floating_ip_2'] = server_floating_ip_2 return resp @decorators.idempotent_id('77fdf3ea-82c1-453d-bfec-f7efe335625d') def test_icmp_reachability_scenarios(self): topology = self._create_topology() ssh_login = CONF.validation.image_ssh_user self.check_vm_connectivity( ip_address=topology['server_floating_ip_1']['floating_ip_address'], username=ssh_login, private_key=topology['private_key1']) self.check_vm_connectivity( ip_address=topology['server_floating_ip_2']['floating_ip_address'], username=ssh_login, private_key=topology['private_key2']) # Scenario 1: Add allow ICMP rules between the two VMs. fw_allow_icmp_rule = self.create_firewall_rule(action="allow", protocol="icmp") fw_allow_ssh_rule = self.create_firewall_rule(action="allow", protocol="tcp", destination_port=22) fw_policy = self.create_firewall_policy( firewall_rules=[fw_allow_icmp_rule['id'], fw_allow_ssh_rule['id']]) fw_group = self.create_firewall_group( ports=[ topology['router_portid_1'], topology['router_portid_2']], ingress_firewall_policy_id=fw_policy['id'], egress_firewall_policy_id=fw_policy['id']) self._wait_firewall_group_ready(fw_group['id']) LOG.debug('fw_allow_icmp_rule: %s\nfw_allow_ssh_rule: %s\n' 'fw_policy: %s\nfw_group: %s\n', fw_allow_icmp_rule, fw_allow_ssh_rule, fw_policy, fw_group) # Check the connectivity between VM1 and VM2. It should Pass. self._check_server_connectivity( topology['server_floating_ip_1'], topology['private_key1'], address_list=[topology['server_fixed_ip_2']], should_connect=True) # Scenario 2: Now remove the allow_icmp rule add a deny_icmp rule and # check that ICMP gets blocked fw_deny_icmp_rule = self.create_firewall_rule(action="deny", protocol="icmp") self.remove_firewall_rule_from_policy_and_wait( firewall_group_id=fw_group['id'], firewall_rule_id=fw_allow_icmp_rule['id'], firewall_policy_id=fw_policy['id']) self.insert_firewall_rule_in_policy_and_wait( firewall_group_id=fw_group['id'], firewall_rule_id=fw_deny_icmp_rule['id'], firewall_policy_id=fw_policy['id']) self._check_server_connectivity( topology['server_floating_ip_1'], topology['private_key1'], address_list=[topology['server_fixed_ip_2']], should_connect=False) # Scenario 3: Create a rule allowing ICMP only from server_fixed_ip_1 # to server_fixed_ip_2 and check that traffic from opposite direction # is blocked. fw_allow_unidirectional_icmp_rule = self.create_firewall_rule( action="allow", protocol="icmp", source_ip_address=topology['server_fixed_ip_1'], destination_ip_address=topology['server_fixed_ip_2']) self.remove_firewall_rule_from_policy_and_wait( firewall_group_id=fw_group['id'], firewall_rule_id=fw_deny_icmp_rule['id'], firewall_policy_id=fw_policy['id']) self.insert_firewall_rule_in_policy_and_wait( firewall_group_id=fw_group['id'], firewall_rule_id=fw_allow_unidirectional_icmp_rule['id'], firewall_policy_id=fw_policy['id']) self._check_server_connectivity( topology['server_floating_ip_1'], topology['private_key1'], address_list=[topology['server_fixed_ip_2']], should_connect=True) self._check_server_connectivity( topology['server_floating_ip_2'], topology['private_key2'], address_list=[topology['server_fixed_ip_1']], should_connect=False) # Disassociate ports of this firewall group for cleanup resources self.firewall_groups_client.update_firewall_group( fw_group['id'], ports=[]) neutron-fwaas-12.0.2/neutron_fwaas/tests/tempest_plugin/tests/scenario/manager.py0000664000175000017500000016301613555577713030445 0ustar zuulzuul00000000000000# Copyright 2012 OpenStack Foundation # Copyright 2013 IBM Corp. # 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 json import subprocess import netaddr from oslo_log import log from oslo_utils import netutils from tempest.common import compute from tempest.common import image as common_image from tempest.common.utils.linux import remote_client from tempest.common.utils import net_utils from tempest.common import waiters from tempest import config from tempest import exceptions from tempest.lib.common.utils import data_utils from tempest.lib.common.utils import test_utils from tempest.lib import exceptions as lib_exc import tempest.test CONF = config.CONF LOG = log.getLogger(__name__) class ScenarioTest(tempest.test.BaseTestCase): """Base class for scenario tests. Uses tempest own clients. """ credentials = ['primary'] @classmethod def setup_clients(cls): super(ScenarioTest, cls).setup_clients() # Clients (in alphabetical order) cls.flavors_client = cls.os_primary.flavors_client cls.compute_floating_ips_client = ( cls.os_primary.compute_floating_ips_client) if CONF.service_available.glance: # Check if glance v1 is available to determine which client to use. if CONF.image_feature_enabled.api_v1: cls.image_client = cls.os_primary.image_client elif CONF.image_feature_enabled.api_v2: cls.image_client = cls.os_primary.image_client_v2 else: raise lib_exc.InvalidConfiguration( 'Either api_v1 or api_v2 must be True in ' '[image-feature-enabled].') # Compute image client cls.compute_images_client = cls.os_primary.compute_images_client cls.keypairs_client = cls.os_primary.keypairs_client # Nova security groups client cls.compute_security_groups_client = ( cls.os_primary.compute_security_groups_client) cls.compute_security_group_rules_client = ( cls.os_primary.compute_security_group_rules_client) cls.servers_client = cls.os_primary.servers_client cls.interface_client = cls.os_primary.interfaces_client # Neutron network client cls.networks_client = cls.os_primary.networks_client cls.ports_client = cls.os_primary.ports_client cls.routers_client = cls.os_primary.routers_client cls.subnets_client = cls.os_primary.subnets_client cls.floating_ips_client = cls.os_primary.floating_ips_client cls.security_groups_client = cls.os_primary.security_groups_client cls.security_group_rules_client = ( cls.os_primary.security_group_rules_client) if CONF.volume_feature_enabled.api_v2: cls.volumes_client = cls.os_primary.volumes_v2_client cls.snapshots_client = cls.os_primary.snapshots_v2_client else: cls.volumes_client = cls.os_primary.volumes_client cls.snapshots_client = cls.os_primary.snapshots_client # Test functions library # # The create_[resource] functions only return body and discard the # resp part which is not used in scenario tests def _create_port(self, network_id, client=None, namestart='port-quotatest', **kwargs): if not client: client = self.ports_client name = data_utils.rand_name(namestart) result = client.create_port( name=name, network_id=network_id, **kwargs) self.assertIsNotNone(result, 'Unable to allocate port') port = result['port'] self.addCleanup(test_utils.call_and_ignore_notfound_exc, client.delete_port, port['id']) return port def create_keypair(self, client=None): if not client: client = self.keypairs_client name = data_utils.rand_name(self.__class__.__name__) # We don't need to create a keypair by pubkey in scenario body = client.create_keypair(name=name) self.addCleanup(client.delete_keypair, name) return body['keypair'] def create_server(self, name=None, image_id=None, flavor=None, validatable=False, wait_until='ACTIVE', clients=None, **kwargs): """Wrapper utility that returns a test server. This wrapper utility calls the common create test server and returns a test server. The purpose of this wrapper is to minimize the impact on the code of the tests already using this function. """ # NOTE(jlanoux): As a first step, ssh checks in the scenario # tests need to be run regardless of the run_validation and # validatable parameters and thus until the ssh validation job # becomes voting in CI. The test resources management and IP # association are taken care of in the scenario tests. # Therefore, the validatable parameter is set to false in all # those tests. In this way create_server just return a standard # server and the scenario tests always perform ssh checks. # Needed for the cross_tenant_traffic test: if clients is None: clients = self.os_primary if name is None: name = data_utils.rand_name(self.__class__.__name__ + "-server") vnic_type = CONF.network.port_vnic_type # If vnic_type is configured create port for # every network if vnic_type: ports = [] create_port_body = {'binding:vnic_type': vnic_type, 'namestart': 'port-smoke'} if kwargs: # Convert security group names to security group ids # to pass to create_port if 'security_groups' in kwargs: security_groups = \ clients.security_groups_client.list_security_groups( ).get('security_groups') sec_dict = dict([(s['name'], s['id']) for s in security_groups]) sec_groups_names = [s['name'] for s in kwargs.pop( 'security_groups')] security_groups_ids = [sec_dict[s] for s in sec_groups_names] if security_groups_ids: create_port_body[ 'security_groups'] = security_groups_ids networks = kwargs.pop('networks', []) else: networks = [] # If there are no networks passed to us we look up # for the project's private networks and create a port. # The same behaviour as we would expect when passing # the call to the clients with no networks if not networks: networks = clients.networks_client.list_networks( **{'router:external': False, 'fields': 'id'})['networks'] # It's net['uuid'] if networks come from kwargs # and net['id'] if they come from # clients.networks_client.list_networks for net in networks: net_id = net.get('uuid', net.get('id')) if 'port' not in net: port = self._create_port(network_id=net_id, client=clients.ports_client, **create_port_body) ports.append({'port': port['id']}) else: ports.append({'port': net['port']}) if ports: kwargs['networks'] = ports self.ports = ports tenant_network = self.get_tenant_network() body, servers = compute.create_test_server( clients, tenant_network=tenant_network, wait_until=wait_until, name=name, flavor=flavor, image_id=image_id, **kwargs) self.addCleanup(waiters.wait_for_server_termination, clients.servers_client, body['id']) self.addCleanup(test_utils.call_and_ignore_notfound_exc, clients.servers_client.delete_server, body['id']) server = clients.servers_client.show_server(body['id'])['server'] return server def create_volume(self, size=None, name=None, snapshot_id=None, imageRef=None, volume_type=None): if size is None: size = CONF.volume.volume_size if imageRef: image = self.compute_images_client.show_image(imageRef)['image'] min_disk = image.get('minDisk') size = max(size, min_disk) if name is None: name = data_utils.rand_name(self.__class__.__name__ + "-volume") kwargs = {'display_name': name, 'snapshot_id': snapshot_id, 'imageRef': imageRef, 'volume_type': volume_type, 'size': size} volume = self.volumes_client.create_volume(**kwargs)['volume'] self.addCleanup(self.volumes_client.wait_for_resource_deletion, volume['id']) self.addCleanup(test_utils.call_and_ignore_notfound_exc, self.volumes_client.delete_volume, volume['id']) # NOTE(e0ne): Cinder API v2 uses name instead of display_name if 'display_name' in volume: self.assertEqual(name, volume['display_name']) else: self.assertEqual(name, volume['name']) waiters.wait_for_volume_resource_status(self.volumes_client, volume['id'], 'available') # The volume retrieved on creation has a non-up-to-date status. # Retrieval after it becomes active ensures correct details. volume = self.volumes_client.show_volume(volume['id'])['volume'] return volume def create_volume_type(self, client=None, name=None, backend_name=None): if not client: client = self.admin_volume_types_client if not name: class_name = self.__class__.__name__ name = data_utils.rand_name(class_name + '-volume-type') randomized_name = data_utils.rand_name('scenario-type-' + name) LOG.debug("Creating a volume type: %s on backend %s", randomized_name, backend_name) extra_specs = {} if backend_name: extra_specs = {"volume_backend_name": backend_name} body = client.create_volume_type(name=randomized_name, extra_specs=extra_specs) volume_type = body['volume_type'] self.assertIn('id', volume_type) self.addCleanup(client.delete_volume_type, volume_type['id']) return volume_type def _create_loginable_secgroup_rule(self, secgroup_id=None): _client = self.compute_security_groups_client _client_rules = self.compute_security_group_rules_client if secgroup_id is None: sgs = _client.list_security_groups()['security_groups'] for sg in sgs: if sg['name'] == 'default': secgroup_id = sg['id'] # These rules are intended to permit inbound ssh and icmp # traffic from all sources, so no group_id is provided. # Setting a group_id would only permit traffic from ports # belonging to the same security group. rulesets = [ { # ssh 'ip_protocol': 'tcp', 'from_port': 22, 'to_port': 22, 'cidr': '0.0.0.0/0', }, { # ping 'ip_protocol': 'icmp', 'from_port': -1, 'to_port': -1, 'cidr': '0.0.0.0/0', } ] rules = list() for ruleset in rulesets: sg_rule = _client_rules.create_security_group_rule( parent_group_id=secgroup_id, **ruleset)['security_group_rule'] rules.append(sg_rule) return rules def _create_security_group(self): # Create security group sg_name = data_utils.rand_name(self.__class__.__name__) sg_desc = sg_name + " description" secgroup = self.compute_security_groups_client.create_security_group( name=sg_name, description=sg_desc)['security_group'] self.assertEqual(secgroup['name'], sg_name) self.assertEqual(secgroup['description'], sg_desc) self.addCleanup( test_utils.call_and_ignore_notfound_exc, self.compute_security_groups_client.delete_security_group, secgroup['id']) # Add rules to the security group self._create_loginable_secgroup_rule(secgroup['id']) return secgroup def get_remote_client(self, ip_address, username=None, private_key=None): """Get a SSH client to a remote server @param ip_address the server floating or fixed IP address to use for ssh validation @param username name of the Linux account on the remote server @param private_key the SSH private key to use @return a RemoteClient object """ if username is None: username = CONF.validation.image_ssh_user # Set this with 'keypair' or others to log in with keypair or # username/password. if CONF.validation.auth_method == 'keypair': password = None if private_key is None: private_key = self.keypair['private_key'] else: password = CONF.validation.image_ssh_password private_key = None linux_client = remote_client.RemoteClient(ip_address, username, pkey=private_key, password=password) try: linux_client.validate_authentication() except Exception as e: message = ('Initializing SSH connection to %(ip)s failed. ' 'Error: %(error)s' % {'ip': ip_address, 'error': e}) caller = test_utils.find_test_caller() if caller: message = '(%s) %s' % (caller, message) LOG.exception(message) self._log_console_output() raise return linux_client def _image_create(self, name, fmt, path, disk_format=None, properties=None): if properties is None: properties = {} name = data_utils.rand_name('%s-' % name) params = { 'name': name, 'container_format': fmt, 'disk_format': disk_format or fmt, } if CONF.image_feature_enabled.api_v1: params['is_public'] = 'False' params['properties'] = properties params = {'headers': common_image.image_meta_to_headers(**params)} else: params['visibility'] = 'private' # Additional properties are flattened out in the v2 API. params.update(properties) body = self.image_client.create_image(**params) image = body['image'] if 'image' in body else body self.addCleanup(self.image_client.delete_image, image['id']) self.assertEqual("queued", image['status']) with open(path, 'rb') as image_file: if CONF.image_feature_enabled.api_v1: self.image_client.update_image(image['id'], data=image_file) else: self.image_client.store_image_file(image['id'], image_file) return image['id'] def glance_image_create(self): img_path = CONF.scenario.img_dir + "/" + CONF.scenario.img_file aki_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.aki_img_file ari_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ari_img_file ami_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ami_img_file img_container_format = CONF.scenario.img_container_format img_disk_format = CONF.scenario.img_disk_format img_properties = CONF.scenario.img_properties LOG.debug("paths: img: %s, container_format: %s, disk_format: %s, " "properties: %s, ami: %s, ari: %s, aki: %s", img_path, img_container_format, img_disk_format, img_properties, ami_img_path, ari_img_path, aki_img_path) try: image = self._image_create('scenario-img', img_container_format, img_path, disk_format=img_disk_format, properties=img_properties) except IOError: LOG.debug("A qcow2 image was not found. Try to get a uec image.") kernel = self._image_create('scenario-aki', 'aki', aki_img_path) ramdisk = self._image_create('scenario-ari', 'ari', ari_img_path) properties = {'kernel_id': kernel, 'ramdisk_id': ramdisk} image = self._image_create('scenario-ami', 'ami', path=ami_img_path, properties=properties) LOG.debug("image:%s", image) return image def _log_console_output(self, servers=None): if not CONF.compute_feature_enabled.console_output: LOG.debug('Console output not supported, cannot log') return if not servers: servers = self.servers_client.list_servers() servers = servers['servers'] for server in servers: try: console_output = self.servers_client.get_console_output( server['id'])['output'] LOG.debug('Console output for %s\nbody=\n%s', server['id'], console_output) except lib_exc.NotFound: LOG.debug("Server %s disappeared(deleted) while looking " "for the console log", server['id']) def _log_net_info(self, exc): # network debug is called as part of ssh init if not isinstance(exc, lib_exc.SSHTimeout): LOG.debug('Network information on a devstack host') def create_server_snapshot(self, server, name=None): # Glance client _image_client = self.image_client # Compute client _images_client = self.compute_images_client if name is None: name = data_utils.rand_name(self.__class__.__name__ + 'snapshot') LOG.debug("Creating a snapshot image for server: %s", server['name']) image = _images_client.create_image(server['id'], name=name) image_id = image.response['location'].split('images/')[1] waiters.wait_for_image_status(_image_client, image_id, 'active') self.addCleanup(_image_client.wait_for_resource_deletion, image_id) self.addCleanup(test_utils.call_and_ignore_notfound_exc, _image_client.delete_image, image_id) if CONF.image_feature_enabled.api_v1: # In glance v1 the additional properties are stored in the headers. resp = _image_client.check_image(image_id) snapshot_image = common_image.get_image_meta_from_headers(resp) image_props = snapshot_image.get('properties', {}) else: # In glance v2 the additional properties are flattened. snapshot_image = _image_client.show_image(image_id) image_props = snapshot_image bdm = image_props.get('block_device_mapping') if bdm: bdm = json.loads(bdm) if bdm and 'snapshot_id' in bdm[0]: snapshot_id = bdm[0]['snapshot_id'] self.addCleanup( self.snapshots_client.wait_for_resource_deletion, snapshot_id) self.addCleanup(test_utils.call_and_ignore_notfound_exc, self.snapshots_client.delete_snapshot, snapshot_id) waiters.wait_for_volume_resource_status(self.snapshots_client, snapshot_id, 'available') image_name = snapshot_image['name'] self.assertEqual(name, image_name) LOG.debug("Created snapshot image %s for server %s", image_name, server['name']) return snapshot_image def nova_volume_attach(self, server, volume_to_attach): volume = self.servers_client.attach_volume( server['id'], volumeId=volume_to_attach['id'], device='/dev/%s' % CONF.compute.volume_device_name)['volumeAttachment'] self.assertEqual(volume_to_attach['id'], volume['id']) waiters.wait_for_volume_resource_status(self.volumes_client, volume['id'], 'in-use') # Return the updated volume after the attachment return self.volumes_client.show_volume(volume['id'])['volume'] def nova_volume_detach(self, server, volume): self.servers_client.detach_volume(server['id'], volume['id']) waiters.wait_for_volume_resource_status(self.volumes_client, volume['id'], 'available') volume = self.volumes_client.show_volume(volume['id'])['volume'] self.assertEqual('available', volume['status']) def rebuild_server(self, server_id, image=None, preserve_ephemeral=False, wait=True, rebuild_kwargs=None): if image is None: image = CONF.compute.image_ref rebuild_kwargs = rebuild_kwargs or {} LOG.debug("Rebuilding server (id: %s, image: %s, preserve eph: %s)", server_id, image, preserve_ephemeral) self.servers_client.rebuild_server( server_id=server_id, image_ref=image, preserve_ephemeral=preserve_ephemeral, **rebuild_kwargs) if wait: waiters.wait_for_server_status(self.servers_client, server_id, 'ACTIVE') def ping_ip_address(self, ip_address, should_succeed=True, ping_timeout=None, mtu=None): timeout = ping_timeout or CONF.validation.ping_timeout cmd = ['ping', '-c1', '-w1'] if mtu: cmd += [ # don't fragment '-M', 'do', # ping receives just the size of ICMP payload '-s', str(net_utils.get_ping_payload_size(mtu, 4)) ] cmd.append(ip_address) def ping(): proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) proc.communicate() return (proc.returncode == 0) == should_succeed caller = test_utils.find_test_caller() LOG.debug('%(caller)s begins to ping %(ip)s in %(timeout)s sec and the' ' expected result is %(should_succeed)s', { 'caller': caller, 'ip': ip_address, 'timeout': timeout, 'should_succeed': 'reachable' if should_succeed else 'unreachable' }) result = test_utils.call_until_true(ping, timeout, 1) LOG.debug('%(caller)s finishes ping %(ip)s in %(timeout)s sec and the ' 'ping result is %(result)s', { 'caller': caller, 'ip': ip_address, 'timeout': timeout, 'result': 'expected' if result else 'unexpected' }) return result def check_vm_connectivity(self, ip_address, username=None, private_key=None, should_connect=True, mtu=None): """Check server connectivity :param ip_address: server to test against :param username: server's ssh username :param private_key: server's ssh private key to be used :param should_connect: True/False indicates positive/negative test positive - attempt ping and ssh negative - attempt ping and fail if succeed :param mtu: network MTU to use for connectivity validation :raises: AssertError if the result of the connectivity check does not match the value of the should_connect param """ if should_connect: msg = "Timed out waiting for %s to become reachable" % ip_address else: msg = "ip address %s is reachable" % ip_address self.assertTrue(self.ping_ip_address(ip_address, should_succeed=should_connect, mtu=mtu), msg=msg) if should_connect: # no need to check ssh for negative connectivity self.get_remote_client(ip_address, username, private_key) def check_public_network_connectivity(self, ip_address, username, private_key, should_connect=True, msg=None, servers=None, mtu=None): # The target login is assumed to have been configured for # key-based authentication by cloud-init. LOG.debug('checking network connections to IP %s with user: %s', ip_address, username) try: self.check_vm_connectivity(ip_address, username, private_key, should_connect=should_connect, mtu=mtu) except Exception: ex_msg = 'Public network connectivity check failed' if msg: ex_msg += ": " + msg LOG.exception(ex_msg) self._log_console_output(servers) raise def create_floating_ip(self, thing, pool_name=None): """Create a floating IP and associates to a server on Nova""" if not pool_name: pool_name = CONF.network.floating_network_name floating_ip = (self.compute_floating_ips_client. create_floating_ip(pool=pool_name)['floating_ip']) self.addCleanup(test_utils.call_and_ignore_notfound_exc, self.compute_floating_ips_client.delete_floating_ip, floating_ip['id']) self.compute_floating_ips_client.associate_floating_ip_to_server( floating_ip['ip'], thing['id']) return floating_ip def create_timestamp(self, ip_address, dev_name=None, mount_path='/mnt', private_key=None): ssh_client = self.get_remote_client(ip_address, private_key=private_key) if dev_name is not None: ssh_client.make_fs(dev_name) ssh_client.mount(dev_name, mount_path) cmd_timestamp = 'sudo sh -c "date > %s/timestamp; sync"' % mount_path ssh_client.exec_command(cmd_timestamp) timestamp = ssh_client.exec_command('sudo cat %s/timestamp' % mount_path) if dev_name is not None: ssh_client.umount(mount_path) return timestamp def get_timestamp(self, ip_address, dev_name=None, mount_path='/mnt', private_key=None): ssh_client = self.get_remote_client(ip_address, private_key=private_key) if dev_name is not None: ssh_client.mount(dev_name, mount_path) timestamp = ssh_client.exec_command('sudo cat %s/timestamp' % mount_path) if dev_name is not None: ssh_client.umount(mount_path) return timestamp def get_server_ip(self, server): """Get the server fixed or floating IP. Based on the configuration we're in, return a correct ip address for validating that a guest is up. """ if CONF.validation.connect_method == 'floating': # The tests calling this method don't have a floating IP # and can't make use of the validation resources. So the # method is creating the floating IP there. return self.create_floating_ip(server)['ip'] elif CONF.validation.connect_method == 'fixed': # Determine the network name to look for based on config or creds # provider network resources. if CONF.validation.network_for_ssh: addresses = server['addresses'][ CONF.validation.network_for_ssh] else: creds_provider = self._get_credentials_provider() net_creds = creds_provider.get_primary_creds() network = getattr(net_creds, 'network', None) addresses = (server['addresses'][network['name']] if network else []) for address in addresses: if (address['version'] == CONF.validation.ip_version_for_ssh and address['OS-EXT-IPS:type'] == 'fixed'): return address['addr'] raise exceptions.ServerUnreachable(server_id=server['id']) else: raise lib_exc.InvalidConfiguration() class NetworkScenarioTest(ScenarioTest): """Base class for network scenario tests. This class provide helpers for network scenario tests, using the neutron API. Helpers from ancestor which use the nova network API are overridden with the neutron API. This Class also enforces using Neutron instead of novanetwork. Subclassed tests will be skipped if Neutron is not enabled """ credentials = ['primary', 'admin'] @classmethod def skip_checks(cls): super(NetworkScenarioTest, cls).skip_checks() if not CONF.service_available.neutron: raise cls.skipException('Neutron not available') def _create_network(self, networks_client=None, tenant_id=None, namestart='network-smoke-', port_security_enabled=True): if not networks_client: networks_client = self.networks_client if not tenant_id: tenant_id = networks_client.tenant_id name = data_utils.rand_name(namestart) network_kwargs = dict(name=name, tenant_id=tenant_id) # Neutron disables port security by default so we have to check the # config before trying to create the network with port_security_enabled if CONF.network_feature_enabled.port_security: network_kwargs['port_security_enabled'] = port_security_enabled result = networks_client.create_network(**network_kwargs) network = result['network'] self.assertEqual(network['name'], name) self.addCleanup(test_utils.call_and_ignore_notfound_exc, networks_client.delete_network, network['id']) return network def _create_subnet(self, network, subnets_client=None, routers_client=None, namestart='subnet-smoke', **kwargs): """Create a subnet for the given network within the cidr block configured for tenant networks. """ if not subnets_client: subnets_client = self.subnets_client if not routers_client: routers_client = self.routers_client def cidr_in_use(cidr, tenant_id): """Check cidr existence :returns: True if subnet with cidr already exist in tenant False else """ cidr_in_use = self.os_admin.subnets_client.list_subnets( tenant_id=tenant_id, cidr=cidr)['subnets'] return len(cidr_in_use) != 0 ip_version = kwargs.pop('ip_version', 4) if ip_version == 6: tenant_cidr = netaddr.IPNetwork( CONF.network.project_network_v6_cidr) num_bits = CONF.network.project_network_v6_mask_bits else: tenant_cidr = netaddr.IPNetwork(CONF.network.project_network_cidr) num_bits = CONF.network.project_network_mask_bits result = None str_cidr = None # Repeatedly attempt subnet creation with sequential cidr # blocks until an unallocated block is found. for subnet_cidr in tenant_cidr.subnet(num_bits): str_cidr = str(subnet_cidr) if cidr_in_use(str_cidr, tenant_id=network['tenant_id']): continue subnet = dict( name=data_utils.rand_name(namestart), network_id=network['id'], tenant_id=network['tenant_id'], cidr=str_cidr, ip_version=ip_version, **kwargs ) try: result = subnets_client.create_subnet(**subnet) break except lib_exc.Conflict as e: is_overlapping_cidr = 'overlaps with another subnet' in str(e) if not is_overlapping_cidr: raise self.assertIsNotNone(result, 'Unable to allocate tenant network') subnet = result['subnet'] self.assertEqual(subnet['cidr'], str_cidr) self.addCleanup(test_utils.call_and_ignore_notfound_exc, subnets_client.delete_subnet, subnet['id']) return subnet def _get_server_port_id_and_ip4(self, server, ip_addr=None): ports = self.os_admin.ports_client.list_ports( device_id=server['id'], fixed_ip=ip_addr)['ports'] # A port can have more than one IP address in some cases. # If the network is dual-stack (IPv4 + IPv6), this port is associated # with 2 subnets p_status = ['ACTIVE'] # NOTE(vsaienko) With Ironic, instances live on separate hardware # servers. Neutron does not bind ports for Ironic instances, as a # result the port remains in the DOWN state. # TODO(vsaienko) remove once bug: #1599836 is resolved. if getattr(CONF.service_available, 'ironic', False): p_status.append('DOWN') port_map = [(p["id"], fxip["ip_address"]) for p in ports for fxip in p["fixed_ips"] if (netutils.is_valid_ipv4(fxip["ip_address"]) and p['status'] in p_status)] inactive = [p for p in ports if p['status'] != 'ACTIVE'] if inactive: LOG.warning("Instance has ports that are not ACTIVE: %s", inactive) self.assertNotEqual(0, len(port_map), "No IPv4 addresses found in: %s" % ports) self.assertEqual(len(port_map), 1, "Found multiple IPv4 addresses: %s. " "Unable to determine which port to target." % port_map) return port_map[0] def _get_network_by_name(self, network_name): net = self.os_admin.networks_client.list_networks( name=network_name)['networks'] self.assertNotEqual(len(net), 0, "Unable to get network by name: %s" % network_name) return net[0] def create_floating_ip(self, thing, external_network_id=None, port_id=None, client=None): """Create a floating IP and associates to a resource/port on Neutron""" if not external_network_id: external_network_id = CONF.network.public_network_id if not client: client = self.floating_ips_client if not port_id: port_id, ip4 = self._get_server_port_id_and_ip4(thing) else: ip4 = None result = client.create_floatingip( floating_network_id=external_network_id, port_id=port_id, tenant_id=thing['tenant_id'], fixed_ip_address=ip4 ) floating_ip = result['floatingip'] self.addCleanup(test_utils.call_and_ignore_notfound_exc, client.delete_floatingip, floating_ip['id']) return floating_ip def _associate_floating_ip(self, floating_ip, server): port_id, _ = self._get_server_port_id_and_ip4(server) kwargs = dict(port_id=port_id) floating_ip = self.floating_ips_client.update_floatingip( floating_ip['id'], **kwargs)['floatingip'] self.assertEqual(port_id, floating_ip['port_id']) return floating_ip def _disassociate_floating_ip(self, floating_ip): """:param floating_ip: floating_ips_client.create_floatingip""" kwargs = dict(port_id=None) floating_ip = self.floating_ips_client.update_floatingip( floating_ip['id'], **kwargs)['floatingip'] self.assertIsNone(floating_ip['port_id']) return floating_ip def check_floating_ip_status(self, floating_ip, status): """Verifies floatingip reaches the given status :param dict floating_ip: floating IP dict to check status :param status: target status :raises: AssertionError if status doesn't match """ floatingip_id = floating_ip['id'] def refresh(): result = (self.floating_ips_client. show_floatingip(floatingip_id)['floatingip']) return status == result['status'] test_utils.call_until_true(refresh, CONF.network.build_timeout, CONF.network.build_interval) floating_ip = self.floating_ips_client.show_floatingip( floatingip_id)['floatingip'] self.assertEqual(status, floating_ip['status'], message="FloatingIP: {fp} is at status: {cst}. " "failed to reach status: {st}" .format(fp=floating_ip, cst=floating_ip['status'], st=status)) LOG.info("FloatingIP: {fp} is at status: {st}" .format(fp=floating_ip, st=status)) def _check_tenant_network_connectivity(self, server, username, private_key, should_connect=True, servers_for_debug=None): if not CONF.network.project_networks_reachable: msg = 'Tenant networks not configured to be reachable.' LOG.info(msg) return # The target login is assumed to have been configured for # key-based authentication by cloud-init. try: for net_name, ip_addresses in server['addresses'].items(): for ip_address in ip_addresses: self.check_vm_connectivity(ip_address['addr'], username, private_key, should_connect=should_connect) except Exception as e: LOG.exception('Tenant network connectivity check failed') self._log_console_output(servers_for_debug) self._log_net_info(e) raise def _check_remote_connectivity(self, source, dest, should_succeed=True, nic=None): """check ping server via source ssh connection :param source: RemoteClient: an ssh connection from which to ping :param dest: and IP to ping against :param should_succeed: boolean should ping succeed or not :param nic: specific network interface to ping from :returns: boolean -- should_succeed == ping :returns: ping is false if ping failed """ def ping_remote(): try: source.ping_host(dest, nic=nic) except lib_exc.SSHExecCommandFailed: LOG.warning('Failed to ping IP: %s via a ssh connection ' 'from: %s.', dest, source.ssh_client.host) return not should_succeed return should_succeed return test_utils.call_until_true(ping_remote, CONF.validation.ping_timeout, 1) def _create_security_group(self, security_group_rules_client=None, tenant_id=None, namestart='secgroup-smoke', security_groups_client=None): if security_group_rules_client is None: security_group_rules_client = self.security_group_rules_client if security_groups_client is None: security_groups_client = self.security_groups_client if tenant_id is None: tenant_id = security_groups_client.tenant_id secgroup = self._create_empty_security_group( namestart=namestart, client=security_groups_client, tenant_id=tenant_id) # Add rules to the security group rules = self._create_loginable_secgroup_rule( security_group_rules_client=security_group_rules_client, secgroup=secgroup, security_groups_client=security_groups_client) for rule in rules: self.assertEqual(tenant_id, rule['tenant_id']) self.assertEqual(secgroup['id'], rule['security_group_id']) return secgroup def _create_empty_security_group(self, client=None, tenant_id=None, namestart='secgroup-smoke'): """Create a security group without rules. Default rules will be created: - IPv4 egress to any - IPv6 egress to any :param tenant_id: secgroup will be created in this tenant :returns: the created security group """ if client is None: client = self.security_groups_client if not tenant_id: tenant_id = client.tenant_id sg_name = data_utils.rand_name(namestart) sg_desc = sg_name + " description" sg_dict = dict(name=sg_name, description=sg_desc) sg_dict['tenant_id'] = tenant_id result = client.create_security_group(**sg_dict) secgroup = result['security_group'] self.assertEqual(secgroup['name'], sg_name) self.assertEqual(tenant_id, secgroup['tenant_id']) self.assertEqual(secgroup['description'], sg_desc) self.addCleanup(test_utils.call_and_ignore_notfound_exc, client.delete_security_group, secgroup['id']) return secgroup def _default_security_group(self, client=None, tenant_id=None): """Get default secgroup for given tenant_id. :returns: default secgroup for given tenant """ if client is None: client = self.security_groups_client if not tenant_id: tenant_id = client.tenant_id sgs = [ sg for sg in list(client.list_security_groups().values())[0] if sg['tenant_id'] == tenant_id and sg['name'] == 'default' ] msg = "No default security group for tenant %s." % (tenant_id) self.assertGreater(len(sgs), 0, msg) return sgs[0] def _create_security_group_rule(self, secgroup=None, sec_group_rules_client=None, tenant_id=None, security_groups_client=None, **kwargs): """Create a rule from a dictionary of rule parameters. Create a rule in a secgroup. if secgroup not defined will search for default secgroup in tenant_id. :param secgroup: the security group. :param tenant_id: if secgroup not passed -- the tenant in which to search for default secgroup :param kwargs: a dictionary containing rule parameters: for example, to allow incoming ssh: rule = { direction: 'ingress' protocol:'tcp', port_range_min: 22, port_range_max: 22 } """ if sec_group_rules_client is None: sec_group_rules_client = self.security_group_rules_client if security_groups_client is None: security_groups_client = self.security_groups_client if not tenant_id: tenant_id = security_groups_client.tenant_id if secgroup is None: secgroup = self._default_security_group( client=security_groups_client, tenant_id=tenant_id) ruleset = dict(security_group_id=secgroup['id'], tenant_id=secgroup['tenant_id']) ruleset.update(kwargs) sg_rule = sec_group_rules_client.create_security_group_rule(**ruleset) sg_rule = sg_rule['security_group_rule'] self.assertEqual(secgroup['tenant_id'], sg_rule['tenant_id']) self.assertEqual(secgroup['id'], sg_rule['security_group_id']) return sg_rule def _create_loginable_secgroup_rule(self, security_group_rules_client=None, secgroup=None, security_groups_client=None): """Create loginable security group rule This function will create: 1. egress and ingress tcp port 22 allow rule in order to allow ssh access for ipv4. 2. egress and ingress ipv6 icmp allow rule, in order to allow icmpv6. 3. egress and ingress ipv4 icmp allow rule, in order to allow icmpv4. """ if security_group_rules_client is None: security_group_rules_client = self.security_group_rules_client if security_groups_client is None: security_groups_client = self.security_groups_client rules = [] rulesets = [ dict( # ssh protocol='tcp', port_range_min=22, port_range_max=22, ), dict( # ping protocol='icmp', ), dict( # ipv6-icmp for ping6 protocol='icmp', ethertype='IPv6', ) ] sec_group_rules_client = security_group_rules_client for ruleset in rulesets: for r_direction in ['ingress', 'egress']: ruleset['direction'] = r_direction try: sg_rule = self._create_security_group_rule( sec_group_rules_client=sec_group_rules_client, secgroup=secgroup, security_groups_client=security_groups_client, **ruleset) except lib_exc.Conflict as ex: # if rule already exist - skip rule and continue msg = 'Security group rule already exists' if msg not in ex._error_string: raise ex else: self.assertEqual(r_direction, sg_rule['direction']) rules.append(sg_rule) return rules def _get_router(self, client=None, tenant_id=None): """Retrieve a router for the given tenant id. If a public router has been configured, it will be returned. If a public router has not been configured, but a public network has, a tenant router will be created and returned that routes traffic to the public network. """ if not client: client = self.routers_client if not tenant_id: tenant_id = client.tenant_id router_id = CONF.network.public_router_id network_id = CONF.network.public_network_id if router_id: body = client.show_router(router_id) return body['router'] elif network_id: router = self._create_router(client, tenant_id) kwargs = {'external_gateway_info': dict(network_id=network_id)} router = client.update_router(router['id'], **kwargs)['router'] return router else: raise Exception("Neither of 'public_router_id' or " "'public_network_id' has been defined.") def _create_router(self, client=None, tenant_id=None, namestart='router-smoke'): if not client: client = self.routers_client if not tenant_id: tenant_id = client.tenant_id name = data_utils.rand_name(namestart) result = client.create_router(name=name, admin_state_up=True, tenant_id=tenant_id) router = result['router'] self.assertEqual(router['name'], name) self.addCleanup(test_utils.call_and_ignore_notfound_exc, client.delete_router, router['id']) return router def _update_router_admin_state(self, router, admin_state_up): kwargs = dict(admin_state_up=admin_state_up) router = self.routers_client.update_router( router['id'], **kwargs)['router'] self.assertEqual(admin_state_up, router['admin_state_up']) def create_networks(self, networks_client=None, routers_client=None, subnets_client=None, tenant_id=None, dns_nameservers=None, port_security_enabled=True): """Create a network with a subnet connected to a router. The baremetal driver is a special case since all nodes are on the same shared network. :param tenant_id: id of tenant to create resources in. :param dns_nameservers: list of dns servers to send to subnet. :returns: network, subnet, router """ if CONF.network.shared_physical_network: # NOTE(Shrews): This exception is for environments where tenant # credential isolation is available, but network separation is # not (the current baremetal case). Likely can be removed when # test account mgmt is reworked: # https://blueprints.launchpad.net/tempest/+spec/test-accounts if not CONF.compute.fixed_network_name: m = 'fixed_network_name must be specified in config' raise lib_exc.InvalidConfiguration(m) network = self._get_network_by_name( CONF.compute.fixed_network_name) router = None subnet = None else: network = self._create_network( networks_client=networks_client, tenant_id=tenant_id, port_security_enabled=port_security_enabled) router = self._get_router(client=routers_client, tenant_id=tenant_id) subnet_kwargs = dict(network=network, subnets_client=subnets_client, routers_client=routers_client) # use explicit check because empty list is a valid option if dns_nameservers is not None: subnet_kwargs['dns_nameservers'] = dns_nameservers subnet = self._create_subnet(**subnet_kwargs) if not routers_client: routers_client = self.routers_client router_id = router['id'] routers_client.add_router_interface(router_id, subnet_id=subnet['id']) # save a cleanup job to remove this association between # router and subnet self.addCleanup(test_utils.call_and_ignore_notfound_exc, routers_client.remove_router_interface, router_id, subnet_id=subnet['id']) return network, subnet, router class EncryptionScenarioTest(ScenarioTest): """Base class for encryption scenario tests""" credentials = ['primary', 'admin'] @classmethod def setup_clients(cls): super(EncryptionScenarioTest, cls).setup_clients() if CONF.volume_feature_enabled.api_v2: cls.admin_volume_types_client = cls.os_admin.volume_types_v2_client cls.admin_encryption_types_client =\ cls.os_admin.encryption_types_v2_client else: cls.admin_volume_types_client = cls.os_admin.volume_types_client cls.admin_encryption_types_client =\ cls.os_admin.encryption_types_client def create_encryption_type(self, client=None, type_id=None, provider=None, key_size=None, cipher=None, control_location=None): if not client: client = self.admin_encryption_types_client if not type_id: volume_type = self.create_volume_type() type_id = volume_type['id'] LOG.debug("Creating an encryption type for volume type: %s", type_id) client.create_encryption_type( type_id, provider=provider, key_size=key_size, cipher=cipher, control_location=control_location)['encryption'] class ObjectStorageScenarioTest(ScenarioTest): """Provide harness to do Object Storage scenario tests. Subclasses implement the tests that use the methods provided by this class. """ @classmethod def skip_checks(cls): super(ObjectStorageScenarioTest, cls).skip_checks() if not CONF.service_available.swift: skip_msg = ("%s skipped as swift is not available" % cls.__name__) raise cls.skipException(skip_msg) @classmethod def setup_credentials(cls): cls.set_network_resources() super(ObjectStorageScenarioTest, cls).setup_credentials() operator_role = CONF.object_storage.operator_role cls.os_operator = cls.get_client_manager(roles=[operator_role]) @classmethod def setup_clients(cls): super(ObjectStorageScenarioTest, cls).setup_clients() # Clients for Swift cls.account_client = cls.os_operator.account_client cls.container_client = cls.os_operator.container_client cls.object_client = cls.os_operator.object_client def get_swift_stat(self): """get swift status for our user account.""" self.account_client.list_account_containers() LOG.debug('Swift status information obtained successfully') def create_container(self, container_name=None): name = container_name or data_utils.rand_name( 'swift-scenario-container') self.container_client.create_container(name) # look for the container to assure it is created self.list_and_check_container_objects(name) LOG.debug('Container %s created', name) self.addCleanup(test_utils.call_and_ignore_notfound_exc, self.container_client.delete_container, name) return name def delete_container(self, container_name): self.container_client.delete_container(container_name) LOG.debug('Container %s deleted', container_name) def upload_object_to_container(self, container_name, obj_name=None): obj_name = obj_name or data_utils.rand_name('swift-scenario-object') obj_data = data_utils.random_bytes() self.object_client.create_object(container_name, obj_name, obj_data) self.addCleanup(test_utils.call_and_ignore_notfound_exc, self.object_client.delete_object, container_name, obj_name) return obj_name, obj_data def delete_object(self, container_name, filename): self.object_client.delete_object(container_name, filename) self.list_and_check_container_objects(container_name, not_present_obj=[filename]) def list_and_check_container_objects(self, container_name, present_obj=None, not_present_obj=None): # List objects for a given container and assert which are present and # which are not. if present_obj is None: present_obj = [] if not_present_obj is None: not_present_obj = [] _, object_list = self.container_client.list_container_contents( container_name) if present_obj: for obj in present_obj: self.assertIn(obj, object_list) if not_present_obj: for obj in not_present_obj: self.assertNotIn(obj, object_list) def change_container_acl(self, container_name, acl): metadata_param = {'metadata_prefix': 'x-container-', 'metadata': {'read': acl}} self.container_client.update_container_metadata(container_name, **metadata_param) resp, _ = self.container_client.list_container_metadata(container_name) self.assertEqual(resp['x-container-read'], acl) def download_and_verify(self, container_name, obj_name, expected_data): _, obj = self.object_client.get_object(container_name, obj_name) self.assertEqual(obj, expected_data) neutron-fwaas-12.0.2/neutron_fwaas/tests/tempest_plugin/tests/scenario/base.py0000664000175000017500000000610313555577713027736 0ustar zuulzuul00000000000000# Copyright (c) 2015 Midokura SARL # 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 tempest import config from tempest.lib.common import ssh from tempest.lib import exceptions as lib_exc from neutron_fwaas.tests.tempest_plugin.tests import fwaas_client from neutron_fwaas.tests.tempest_plugin.tests import fwaas_v2_client from neutron_fwaas.tests.tempest_plugin.tests.scenario import manager CONF = config.CONF class FWaaSScenarioTestBase(object): def check_connectivity(self, ip_address, username=None, private_key=None, should_connect=True, check_icmp=True, check_ssh=True, check_reverse_icmp_ip=None, should_reverse_connect=True): if should_connect: msg = "Timed out waiting for %s to become reachable" % ip_address else: msg = "ip address %s is reachable" % ip_address if check_icmp: ok = self.ping_ip_address(ip_address, should_succeed=should_connect) self.assertTrue(ok, msg=msg) if check_ssh: connect_timeout = CONF.validation.connect_timeout kwargs = {} if not should_connect: # Use a shorter timeout for negative case kwargs['timeout'] = 1 try: client = ssh.Client(ip_address, username, pkey=private_key, channel_timeout=connect_timeout, **kwargs) client.test_connection_auth() self.assertTrue(should_connect, "Unexpectedly reachable") if check_reverse_icmp_ip: cmd = 'ping -c1 -w1 %s' % check_reverse_icmp_ip try: client.exec_command(cmd) self.assertTrue(should_reverse_connect, "Unexpectedly reachable (reverse)") except lib_exc.SSHExecCommandFailed: if should_reverse_connect: raise except lib_exc.SSHTimeout: if should_connect: raise class FWaaSScenarioTest(fwaas_client.FWaaSClientMixin, FWaaSScenarioTestBase, manager.NetworkScenarioTest): pass class FWaaSScenarioTest_V2(fwaas_v2_client.FWaaSClientMixin, FWaaSScenarioTestBase, manager.NetworkScenarioTest): pass neutron-fwaas-12.0.2/neutron_fwaas/tests/tempest_plugin/tests/scenario/test_fwaas.py0000664000175000017500000003721713555577713031176 0ustar zuulzuul00000000000000# Copyright (c) 2015 Midokura SARL # 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 testscenarios from tempest import config from tempest.lib import decorators from tempest import test from neutron_fwaas.tests.tempest_plugin.tests.scenario import base CONF = config.CONF load_tests = testscenarios.load_tests_apply_scenarios class TestFWaaS(base.FWaaSScenarioTest): scenarios = [ ('without router insersion', { 'router_insertion': False, }), ('with router insersion', { 'router_insertion': True, }), ] def setUp(self): super(TestFWaaS, self).setUp() required_exts = ['fwaas', 'security-group', 'router'] if self.router_insertion: required_exts.append('fwaasrouterinsertion') for ext in required_exts: if not test.is_extension_enabled(ext, 'network'): msg = "%s Extension not enabled." % ext raise self.skipException(msg) self._router_ids = None def _create_server(self, network, security_group=None): keys = self.create_keypair() kwargs = {} if security_group is not None: kwargs['security_groups'] = [{'name': security_group['name']}] server = self.create_server( key_name=keys['name'], networks=[{'uuid': network['id']}], wait_until='ACTIVE', **kwargs) return server, keys def _create_firewall(self, **kwargs): if self._router_ids is not None: kwargs['router_ids'] = self._router_ids return self.create_firewall(**kwargs) def _empty_policy(self, **kwargs): # NOTE(yamamoto): an empty policy would deny all fw_policy = self.create_firewall_policy(firewall_rules=[]) fw = self._create_firewall(firewall_policy_id=fw_policy['id']) self._wait_firewall_ready(fw['id']) return { 'fw': fw, 'fw_policy': fw_policy, } def _all_disabled_rules(self, **kwargs): # NOTE(yamamoto): a policy whose rules are all disabled would deny all fw_rule = self.create_firewall_rule(action="allow", enabled=False) fw_policy = self.create_firewall_policy(firewall_rules=[fw_rule['id']]) fw = self._create_firewall(firewall_policy_id=fw_policy['id']) self._wait_firewall_ready(fw['id']) return { 'fw': fw, 'fw_policy': fw_policy, 'fw_rule': fw_rule, } def _block_ip(self, server1_fixed_ip, server2_fixed_ip, **kwargs): rules = [ # NOTE(yamamoto): The filtering is taken place after # destination ip is rewritten to fixed-ip. self.create_firewall_rule(destination_ip_address=server1_fixed_ip, action="deny"), self.create_firewall_rule(destination_ip_address=server2_fixed_ip, action="deny"), self.create_firewall_rule(action="allow"), ] rule_ids = [r['id'] for r in rules] fw_policy = self.create_firewall_policy(firewall_rules=rule_ids) fw = self._create_firewall(firewall_policy_id=fw_policy['id']) self._wait_firewall_ready(fw['id']) return { 'fw': fw, 'fw_policy': fw_policy, 'server1_fixed_ip': server1_fixed_ip, 'server2_fixed_ip': server2_fixed_ip, } def _block_icmp(self, **kwargs): fw_rule = self.create_firewall_rule( protocol="icmp", action="deny") fw_rule_allow = self.create_firewall_rule( action="allow") fw_policy = self.create_firewall_policy( firewall_rules=[fw_rule['id'], fw_rule_allow['id']]) fw = self._create_firewall(firewall_policy_id=fw_policy['id']) self._wait_firewall_ready(fw['id']) return { 'fw': fw, 'fw_policy': fw_policy, 'fw_rule': fw_rule, } def _block_all_with_default_allow(self, **kwargs): fw_rule = self.create_firewall_rule( action="deny") fw_rule_allow = self.create_firewall_rule( action="allow") fw_policy = self.create_firewall_policy( firewall_rules=[fw_rule['id'], fw_rule_allow['id']]) fw = self._create_firewall(firewall_policy_id=fw_policy['id']) self._wait_firewall_ready(fw['id']) return { 'fw': fw, 'fw_policy': fw_policy, 'fw_rule': fw_rule, } def _admin_disable(self, **kwargs): # NOTE(yamamoto): A firewall with admin_state_up=False would block all fw_rule = self.create_firewall_rule(action="allow") fw_policy = self.create_firewall_policy(firewall_rules=[fw_rule['id']]) fw = self._create_firewall(firewall_policy_id=fw_policy['id'], admin_state_up=False) self._wait_firewall_ready(fw['id']) return { 'fw': fw, 'fw_policy': fw_policy, 'fw_rule': fw_rule, } def _allow_ssh_and_icmp(self, ctx): fw_ssh_rule = self.create_firewall_rule( protocol="tcp", destination_port=22, action="allow") fw_icmp_rule = self.create_firewall_rule( protocol="icmp", action="allow") for rule in [fw_ssh_rule, fw_icmp_rule]: self.firewall_policies_client.insert_firewall_rule_in_policy( firewall_policy_id=ctx['fw_policy']['id'], firewall_rule_id=rule['id'], insert_before=ctx['fw_rule']['id']) self.addCleanup( self._remove_rule_and_wait, firewall_id=ctx['fw']['id'], firewall_policy_id=ctx['fw_policy']['id'], firewall_rule_id=rule['id']) self._wait_firewall_ready(ctx['fw']['id']) def _remove_rule_and_wait(self, firewall_id, firewall_policy_id, firewall_rule_id): self.firewall_policies_client.remove_firewall_rule_from_policy( firewall_policy_id=firewall_policy_id, firewall_rule_id=firewall_rule_id) self._wait_firewall_ready(firewall_id) def _delete_fw(self, ctx): self.delete_firewall_and_wait(ctx['fw']['id']) def _set_admin_up(self, firewall_id, up): self.firewalls_client.update_firewall(firewall_id=firewall_id, admin_state_up=up) self._wait_firewall_ready(firewall_id=firewall_id) def _admin_enable(self, ctx): self._set_admin_up(ctx['fw']['id'], up=True) def _remove_rule(self, ctx): self._remove_rule_and_wait( firewall_id=ctx['fw']['id'], firewall_policy_id=ctx['fw_policy']['id'], firewall_rule_id=ctx['fw_rule']['id']) def _disable_rule(self, ctx): self.firewall_rules_client.update_firewall_rule( firewall_rule_id=ctx['fw_rule']['id'], enabled=False) self._wait_firewall_ready(ctx['fw']['id']) def _allow_ip(self, ctx): self._delete_fw(ctx) server1_fixed_ip = ctx['server1_fixed_ip'] server2_fixed_ip = ctx['server2_fixed_ip'] rules = [ # NOTE(yamamoto): The filtering is taken place after # destination ip is rewritten to fixed-ip. # The return traffic should be allowed regardless # of firewall rules. self.create_firewall_rule( destination_ip_address=server1_fixed_ip, action="allow"), self.create_firewall_rule( destination_ip_address=server2_fixed_ip, action="allow"), ] rule_ids = [r['id'] for r in rules] fw_policy = self.create_firewall_policy(firewall_rules=rule_ids) fw = self._create_firewall(firewall_policy_id=fw_policy['id']) self._wait_firewall_ready(fw['id']) def _confirm_allowed(self, **kwargs): self.check_connectivity(check_reverse_icmp_ip=self._public_gateway_ip, **kwargs) def _confirm_allowed_oneway(self, **kwargs): # Can ping and ssh, but can't ping back to the public gateway. # Same as _confirm_allowed if _public_gateway_ip is None. self.check_connectivity(check_reverse_icmp_ip=self._public_gateway_ip, should_reverse_connect=False, **kwargs) def _confirm_blocked(self, **kwargs): self.check_connectivity(should_connect=False, **kwargs) def _confirm_icmp_blocked_but_tcp(self, **kwargs): self.check_connectivity(should_connect=False, check_ssh=False, **kwargs) self.check_connectivity(check_icmp=False, **kwargs) def _create_topology(self): """Create a topology for testing +--------+ +-----------+ |"server"| | "subnet" | | VM +-------------+ "network" | +--------+ +----+------+ | | router interface port +----+-----+ | "router" | +----+-----+ | router gateway port | | +----+------------------+ | existing network | | ("public_network_id") | +-----------------------+ """ public_network_id = CONF.network.public_network_id network, subnet, router = self.create_networks() security_group = self._create_security_group() server, keys = self._create_server(network, security_group=security_group) private_key = keys['private_key'] server_floating_ip = self.create_floating_ip(server, public_network_id) fixed_ip = server['addresses'].values()[0][0]['addr'] floating_ip = server_floating_ip['floating_ip_address'] return fixed_ip, floating_ip, private_key, router def _get_public_gateway_ip(self): self._public_gateway_ip = None router = self._get_router() ext_gw_info = router['external_gateway_info'] ext_fixed_ips = ext_gw_info['external_fixed_ips'] for ip in ext_fixed_ips: subnet_id = ip['subnet_id'] res = self.os_admin.subnets_client.show_subnet(subnet_id) subnet = res['subnet'] # REVISIT(yamamoto): IPv4 assumption if subnet['ip_version'] == 4: self._public_gateway_ip = subnet['gateway_ip'] return def _test_firewall_basic(self, block, allow=None, confirm_allowed=None, confirm_blocked=None): if allow is None: allow = self._delete_fw if confirm_allowed is None: confirm_allowed = self._confirm_allowed if confirm_blocked is None: confirm_blocked = self._confirm_blocked ssh_login = CONF.validation.image_ssh_user if self.router_insertion and CONF.network.public_router_id: # NOTE(yamamoto): If public_router_id is configured # router1 and router2 will be the same router. msg = "This test assumes no public_router_id configured" raise self.skipException(msg) server1_fixed_ip, server1_floating_ip, private_key1, router1 = \ self._create_topology() server2_fixed_ip, server2_floating_ip, private_key2, router2 = \ self._create_topology() self._get_public_gateway_ip() if self.router_insertion: # Specify the router when creating a firewall and ensures that # the other router (router2) is not affected by the firewall self._router_ids = [router1['id']] confirm_allowed2 = self.check_connectivity confirm_blocked2 = self.check_connectivity else: # Without router insertion, all routers should be affected # equally confirm_allowed2 = confirm_allowed confirm_blocked2 = confirm_blocked self.check_connectivity(ip_address=server1_floating_ip, username=ssh_login, private_key=private_key1) self.check_connectivity(ip_address=server2_floating_ip, username=ssh_login, private_key=private_key2) ctx = block(server1_fixed_ip=server1_fixed_ip, server1_floating_ip=server1_floating_ip, server2_fixed_ip=server2_fixed_ip, server2_floating_ip=server2_floating_ip) confirm_blocked(ip_address=server1_floating_ip, username=ssh_login, private_key=private_key1) confirm_blocked2(ip_address=server2_floating_ip, username=ssh_login, private_key=private_key2) allow(ctx) confirm_allowed(ip_address=server1_floating_ip, username=ssh_login, private_key=private_key1) confirm_allowed2(ip_address=server2_floating_ip, username=ssh_login, private_key=private_key2) @decorators.idempotent_id('f970f6b3-6541-47ac-a9ea-f769be1e21a8') def test_firewall_block_ip(self): self._test_firewall_basic(block=self._block_ip, allow=self._allow_ip, confirm_allowed=self._confirm_allowed_oneway) @decorators.idempotent_id('b985d010-994a-4055-bd5c-9e961464ccde') def test_firewall_block_icmp(self): self._test_firewall_basic( block=self._block_icmp, confirm_blocked=self._confirm_icmp_blocked_but_tcp) @decorators.idempotent_id('ca473af0-26f9-4fad-9550-1c34371c900e') def test_firewall_insert_rule(self): self._test_firewall_basic( block=self._block_icmp, allow=self._allow_ssh_and_icmp, confirm_blocked=self._confirm_icmp_blocked_but_tcp) @decorators.idempotent_id('54a937a6-cecf-444c-b3f9-b67a1c1b7411') def test_firewall_remove_rule(self): self._test_firewall_basic(block=self._block_all_with_default_allow, allow=self._remove_rule) @decorators.idempotent_id('12a18776-9b60-4479-9988-f45971c96a92') def test_firewall_disable_rule(self): self._test_firewall_basic(block=self._block_all_with_default_allow, allow=self._disable_rule) @decorators.idempotent_id('a2a58c1f-49ad-4b5f-9463-e746b9efe08a') def test_firewall_empty_policy(self): self._test_firewall_basic(block=self._empty_policy) @decorators.idempotent_id('477a47e0-5156-4784-9417-f77970d85c36') def test_firewall_all_disabled_rules(self): self._test_firewall_basic(block=self._all_disabled_rules) @decorators.idempotent_id('a83f51c5-1a18-4d2a-a778-c368e4d95c29') def test_firewall_admin_disable(self): self._test_firewall_basic(block=self._admin_disable, allow=self._admin_enable) neutron-fwaas-12.0.2/neutron_fwaas/tests/tempest_plugin/tests/scenario/__init__.py0000664000175000017500000000000013555577713030551 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/tempest_plugin/tests/fwaas_v2_client.py0000664000175000017500000001530613555577713030274 0ustar zuulzuul00000000000000# Copyright (c) 2015 Midokura SARL # 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 time from neutron_lib import constants as nl_constants from tempest import config from tempest import exceptions from tempest.lib.common.utils import data_utils from tempest.lib.common.utils import test_utils from tempest.lib import exceptions as lib_exc from neutron_fwaas.tests.tempest_plugin.services import v2_client CONF = config.CONF class FWaaSClientMixin(object): @classmethod def resource_setup(cls): super(FWaaSClientMixin, cls).resource_setup() manager = cls.os_primary cls.firewall_groups_client = v2_client.FirewallGroupsClient( manager.auth_provider, CONF.network.catalog_type, CONF.network.region or CONF.identity.region, endpoint_type=CONF.network.endpoint_type, build_interval=CONF.network.build_interval, build_timeout=CONF.network.build_timeout, **manager.default_params) cls.firewall_policies_client = v2_client.FirewallPoliciesClient( manager.auth_provider, CONF.network.catalog_type, CONF.network.region or CONF.identity.region, endpoint_type=CONF.network.endpoint_type, build_interval=CONF.network.build_interval, build_timeout=CONF.network.build_timeout, **manager.default_params) cls.firewall_rules_client = v2_client.FirewallRulesClient( manager.auth_provider, CONF.network.catalog_type, CONF.network.region or CONF.identity.region, endpoint_type=CONF.network.endpoint_type, build_interval=CONF.network.build_interval, build_timeout=CONF.network.build_timeout, **manager.default_params) def create_firewall_rule(self, **kwargs): body = self.firewall_rules_client.create_firewall_rule( name=data_utils.rand_name("fw-rule"), **kwargs) fw_rule = body['firewall_rule'] self.addCleanup(test_utils.call_and_ignore_notfound_exc, self.firewall_rules_client.delete_firewall_rule, fw_rule['id']) return fw_rule def create_firewall_policy(self, **kwargs): body = self.firewall_policies_client.create_firewall_policy( name=data_utils.rand_name("fw-policy"), **kwargs) fw_policy = body['firewall_policy'] self.addCleanup(test_utils.call_and_ignore_notfound_exc, self.firewall_policies_client.delete_firewall_policy, fw_policy['id']) return fw_policy def create_firewall_group(self, **kwargs): body = self.firewall_groups_client.create_firewall_group( name=data_utils.rand_name("fwg"), **kwargs) fwg = body['firewall_group'] self.addCleanup(test_utils.call_and_ignore_notfound_exc, self.delete_firewall_group_and_wait, fwg['id']) return fwg def delete_firewall_group_and_wait(self, firewall_group_id): self.firewall_groups_client.delete_firewall_group(firewall_group_id) self._wait_firewall_group_while(firewall_group_id, [nl_constants.PENDING_DELETE], not_found_ok=True) def insert_firewall_rule_in_policy_and_wait(self, firewall_group_id, firewall_policy_id, firewall_rule_id, **kwargs): self.firewall_policies_client.insert_firewall_rule_in_policy( firewall_policy_id=firewall_policy_id, firewall_rule_id=firewall_rule_id, **kwargs) self.addCleanup( self._call_and_ignore_exceptions, (lib_exc.NotFound, lib_exc.BadRequest), self.remove_firewall_rule_from_policy_and_wait, firewall_group_id=firewall_group_id, firewall_policy_id=firewall_policy_id, firewall_rule_id=firewall_rule_id) self._wait_firewall_group_ready(firewall_group_id) def remove_firewall_rule_from_policy_and_wait(self, firewall_group_id, firewall_policy_id, firewall_rule_id): self.firewall_policies_client.remove_firewall_rule_from_policy( firewall_policy_id=firewall_policy_id, firewall_rule_id=firewall_rule_id) self._wait_firewall_group_ready(firewall_group_id) @staticmethod def _call_and_ignore_exceptions(exc_list, func, *args, **kwargs): """Call the given function and pass if a given exception is raised.""" try: return func(*args, **kwargs) except exc_list: pass def _wait_firewall_group_ready(self, firewall_group_id): self._wait_firewall_group_while(firewall_group_id, [nl_constants.PENDING_CREATE, nl_constants.PENDING_UPDATE]) def _wait_firewall_group_while(self, firewall_group_id, statuses, not_found_ok=False): start = int(time.time()) if not_found_ok: expected_exceptions = (lib_exc.NotFound) else: expected_exceptions = () while True: try: fwg = self.firewall_groups_client.show_firewall_group( firewall_group_id) except expected_exceptions: break status = fwg['firewall_group']['status'] if status not in statuses: break if (int(time.time()) - start >= self.firewall_groups_client.build_timeout): msg = ("Firewall Group %(firewall_group)s failed to reach " "non PENDING status (current %(status)s)") % { "firewall_group": firewall_group_id, "status": status, } raise exceptions.TimeoutException(msg) time.sleep(1) neutron-fwaas-12.0.2/neutron_fwaas/tests/tempest_plugin/tests/__init__.py0000664000175000017500000000000013555577713026746 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/tempest_plugin/tests/api/0000775000175000017500000000000013555600005025375 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/tempest_plugin/tests/api/base.py0000664000175000017500000000146613555577713026713 0ustar zuulzuul00000000000000# Copyright (c) 2015 Midokura SARL # 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 tempest.api.network import base from neutron_fwaas.tests.tempest_plugin.tests import fwaas_client class BaseFWaaSTest(fwaas_client.FWaaSClientMixin, base.BaseNetworkTest): pass neutron-fwaas-12.0.2/neutron_fwaas/tests/tempest_plugin/tests/api/test_fwaasv2_extensions.py0000664000175000017500000003461213555577713032667 0ustar zuulzuul00000000000000# Copyright 2016 # # 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 netaddr import six from tempest import config from tempest.lib.common.utils import data_utils from tempest.lib.common.utils import test_utils from tempest.lib import decorators from tempest.lib import exceptions as lib_exc from tempest import test from neutron_fwaas.common import fwaas_constants from neutron_fwaas.tests.tempest_plugin.tests.api import v2_base CONF = config.CONF class FWaaSv2ExtensionTestJSON(v2_base.BaseFWaaSTest): """ Tests the following operations in the Neutron API using the REST client for Neutron: List firewall rules Create firewall rule Update firewall rule Delete firewall rule Show firewall rule List firewall policies Create firewall policy Update firewall policy Insert firewall rule to policy Remove firewall rule from policy Insert firewall rule after/before rule in policy Update firewall policy audited attribute Delete firewall policy Show firewall policy List firewall group Create firewall group Update firewall group Delete firewall group Show firewall group """ @classmethod def resource_setup(cls): super(FWaaSv2ExtensionTestJSON, cls).resource_setup() if not test.is_extension_enabled('fwaas_v2', 'network'): msg = "FWaaS v2 Extension not enabled." raise cls.skipException(msg) def setUp(self): super(FWaaSv2ExtensionTestJSON, self).setUp() self.fw_rule_1 = self.create_firewall_rule(action="allow", protocol="tcp") self.fw_rule_2 = self.create_firewall_rule(action="deny", protocol="udp") self.fw_policy_1 = self.create_firewall_policy( firewall_rules=[self.fw_rule_1['id']]) self.fw_policy_2 = self.create_firewall_policy( firewall_rules=[self.fw_rule_2['id']]) def _create_router_interfaces(self): network_1 = self.create_network() network_2 = self.create_network() cidr = netaddr.IPNetwork(CONF.network.project_network_cidr) mask_bits = CONF.network.project_network_mask_bits subnet_cidr_1 = list(cidr.subnet(mask_bits))[-1] subnet_cidr_2 = list(cidr.subnet(mask_bits))[-2] subnet_1 = self.create_subnet(network_1, cidr=subnet_cidr_1, mask_bits=mask_bits) subnet_2 = self.create_subnet(network_2, cidr=subnet_cidr_2, mask_bits=mask_bits) router = self.create_router( data_utils.rand_name('router-'), admin_state_up=True) self.addCleanup(self._try_delete_router, router) intf_1 = self.routers_client.add_router_interface(router['id'], subnet_id=subnet_1['id']) intf_2 = self.routers_client.add_router_interface(router['id'], subnet_id=subnet_2['id']) return intf_1, intf_2 def _try_delete_router(self, router): # delete router, if it exists try: self.delete_router(router) # if router is not found, this means it was deleted in the test except lib_exc.NotFound: pass def _try_delete_policy(self, policy_id): # delete policy, if it exists try: self.firewall_policies_client.delete_firewall_policy(policy_id) # if policy is not found, this means it was deleted in the test except lib_exc.NotFound: pass def _try_delete_rule(self, rule_id): # delete rule, if it exists try: self.firewall_rules_client.delete_firewall_rule(rule_id) # if rule is not found, this means it was deleted in the test except lib_exc.NotFound: pass def _try_delete_firewall_group(self, fwg_id): # delete firewall group, if it exists try: self.firewall_groups_client.delete_firewall_group(fwg_id) # if firewall group is not found, this means it was deleted in the test except lib_exc.NotFound: pass self.firewall_groups_client.wait_for_resource_deletion(fwg_id) def _wait_until_ready(self, fwg_id): target_states = ('ACTIVE', 'CREATED') def _wait(): firewall_group = self.firewall_groups_client.show_firewall_group( fwg_id) firewall_group = firewall_group['firewall_group'] return firewall_group['status'] in target_states if not test_utils.call_until_true(_wait, CONF.network.build_timeout, CONF.network.build_interval): m = ("Timed out waiting for firewall_group %s to reach %s " "state(s)" % (fwg_id, target_states)) raise lib_exc.TimeoutException(m) def _wait_until_deleted(self, fwg_id): def _wait(): try: fwg = self.firewall_groups_client.show_firewall_group(fwg_id) except lib_exc.NotFound: return True fwg_status = fwg['firewall_group']['status'] if fwg_status == 'ERROR': raise lib_exc.DeleteErrorException(resource_id=fwg_id) if not test_utils.call_until_true(_wait, CONF.network.build_timeout, CONF.network.build_interval): m = ("Timed out waiting for firewall_group %s deleted" % fwg_id) raise lib_exc.TimeoutException(m) @decorators.idempotent_id('ddccfa87-4af7-48a6-9e50-0bd0ad1348cb') def test_list_firewall_rules(self): # List firewall rules fw_rules = self.firewall_rules_client.list_firewall_rules() fw_rules = fw_rules['firewall_rules'] self.assertIn((self.fw_rule_1['id'], self.fw_rule_1['name'], self.fw_rule_1['action'], self.fw_rule_1['protocol'], self.fw_rule_1['ip_version'], self.fw_rule_1['enabled']), [(m['id'], m['name'], m['action'], m['protocol'], m['ip_version'], m['enabled']) for m in fw_rules]) @decorators.idempotent_id('ffc009fa-cd17-4029-8025-c4b81a7dd923') def test_create_update_delete_firewall_rule(self): # Create firewall rule body = self.firewall_rules_client.create_firewall_rule( name=data_utils.rand_name("fw-rule"), action="allow", protocol="tcp") fw_rule_id = body['firewall_rule']['id'] self.addCleanup(self._try_delete_rule, fw_rule_id) # Update firewall rule body = self.firewall_rules_client.update_firewall_rule(fw_rule_id, action="deny") self.assertEqual("deny", body["firewall_rule"]['action']) # Delete firewall rule self.firewall_rules_client.delete_firewall_rule(fw_rule_id) # Confirm deletion fw_rules = self.firewall_rules_client.list_firewall_rules() self.assertNotIn(fw_rule_id, [m['id'] for m in fw_rules['firewall_rules']]) @decorators.idempotent_id('76b07afc-444e-4bb9-abec-9b8c5f994dcd') def test_show_firewall_rule(self): # show a created firewall rule fw_rule = self.firewall_rules_client.show_firewall_rule( self.fw_rule_1['id']) for key, value in six.iteritems(fw_rule['firewall_rule']): if key != 'firewall_policy_id': self.assertEqual(self.fw_rule_1[key], value) # This check is placed because we cannot modify policy during # Create/Update of Firewall Rule but we can see the association # of a Firewall Rule with the policies it belongs to. @decorators.idempotent_id('f6b83902-746f-4e74-9403-2ec9899583a3') def test_list_firewall_policies(self): fw_policies = self.firewall_policies_client.list_firewall_policies() fw_policies = fw_policies['firewall_policies'] self.assertIn((self.fw_policy_1['id'], self.fw_policy_1['name'], self.fw_policy_1['firewall_rules']), [(m['id'], m['name'], m['firewall_rules']) for m in fw_policies]) @decorators.idempotent_id('6ef9bd02-7349-4d61-8d1f-80479f64d904') def test_create_update_delete_firewall_policy(self): # Create firewall policy body = self.firewall_policies_client.create_firewall_policy( name=data_utils.rand_name("fw-policy")) fw_policy_id = body['firewall_policy']['id'] self.addCleanup(self._try_delete_policy, fw_policy_id) # Update firewall policy body = self.firewall_policies_client.update_firewall_policy( fw_policy_id, name="updated_policy") updated_fw_policy = body["firewall_policy"] self.assertEqual("updated_policy", updated_fw_policy['name']) # Delete firewall policy self.firewall_policies_client.delete_firewall_policy(fw_policy_id) # Confirm deletion fw_policies = self.firewall_policies_client.list_firewall_policies() fw_policies = fw_policies['firewall_policies'] self.assertNotIn(fw_policy_id, [m['id'] for m in fw_policies]) @decorators.idempotent_id('164381de-61f4-483f-9a5a-48105b8e70e2') def test_show_firewall_policy(self): # show a created firewall policy fw_policy = self.firewall_policies_client.show_firewall_policy( self.fw_policy_1['id']) fw_policy = fw_policy['firewall_policy'] for key, value in six.iteritems(fw_policy): self.assertEqual(self.fw_policy_1[key], value) @decorators.idempotent_id('48dfcd75-3924-479d-bb65-b3ed33397663') def test_create_show_delete_firewall_group(self): # create router and add interfaces intf_1, intf_2 = self._create_router_interfaces() # Create firewall_group body = self.firewall_groups_client.create_firewall_group( name=data_utils.rand_name("firewall_group"), ingress_firewall_policy_id=self.fw_policy_1['id'], egress_firewall_policy_id=self.fw_policy_2['id'], ports=[intf_1['port_id'], intf_2['port_id']]) created_firewall_group = body['firewall_group'] fwg_id = created_firewall_group['id'] # Wait for the firewall resource to become ready self._wait_until_ready(fwg_id) # show created firewall_group firewall_group = self.firewall_groups_client.show_firewall_group( fwg_id) fwg = firewall_group['firewall_group'] for key, value in six.iteritems(fwg): if key == 'status': continue self.assertEqual(created_firewall_group[key], value) # list firewall_groups firewall_groups = self.firewall_groups_client.list_firewall_groups() fwgs = firewall_groups['firewall_groups'] self.assertIn((created_firewall_group['id'], created_firewall_group['name'], created_firewall_group['ingress_firewall_policy_id'], created_firewall_group['egress_firewall_policy_id']), [(m['id'], m['name'], m['ingress_firewall_policy_id'], m['egress_firewall_policy_id']) for m in fwgs]) # Disassociate all port with this firewall group self.firewall_groups_client.update_firewall_group(fwg_id, ports=[]) # Delete firewall_group self.firewall_groups_client.delete_firewall_group(fwg_id) # Wait for the firewall group to be deleted self._wait_until_deleted(fwg_id) # Confirm deletion firewall_groups = self.firewall_groups_client.list_firewall_groups() fwgs = firewall_groups['firewall_groups'] self.assertNotIn(fwg_id, [m['id'] for m in fwgs]) @decorators.idempotent_id('e021baab-d4f7-4bad-b382-bde4946e0e0b') def test_update_firewall_group(self): # create router and add interfaces intf_1, intf_2 = self._create_router_interfaces() # Create firewall_group body = self.firewall_groups_client.create_firewall_group( name=data_utils.rand_name("firewall_group"), ingress_firewall_policy_id=self.fw_policy_1['id'], egress_firewall_policy_id=self.fw_policy_2['id'], ports=[intf_1['port_id']]) created_firewall_group = body['firewall_group'] fwg_id = created_firewall_group['id'] self.addCleanup(self._try_delete_firewall_group, fwg_id) # Wait for the firewall resource to become ready self._wait_until_ready(fwg_id) # Update firewall group body = self.firewall_groups_client.update_firewall_group( fwg_id, ports=[intf_2['port_id']]) updated_fwg = body["firewall_group"] self.assertEqual([intf_2['port_id']], updated_fwg['ports']) # Delete firewall_group self.firewall_groups_client.delete_firewall_group(fwg_id) @decorators.idempotent_id('a1f524d8-5336-4769-aa7b-0830bb4df6c8') def test_error_on_create_firewall_group_name_default(self): try: # Create firewall_group with name == reserved default fwg self.firewall_groups_client.create_firewall_group( name=fwaas_constants.DEFAULT_FWG) except lib_exc.Conflict: pass @decorators.idempotent_id('fd24db16-c8cb-4cb4-ba60-b0cd18a66b83') def test_default_fwg_created_on_list_firewall_groups(self): fw_groups = self.firewall_groups_client.list_firewall_groups() fw_groups = fw_groups['firewall_groups'] self.assertIn(fwaas_constants.DEFAULT_FWG, [g['name'] for g in fw_groups]) neutron-fwaas-12.0.2/neutron_fwaas/tests/tempest_plugin/tests/api/__init__.py0000664000175000017500000000000013555577713027517 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/tempest_plugin/tests/api/v2_base.py0000664000175000017500000000143113555577713027312 0ustar zuulzuul00000000000000# 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 tempest.api.network import base from neutron_fwaas.tests.tempest_plugin.tests import fwaas_v2_client class BaseFWaaSTest(fwaas_v2_client.FWaaSClientMixin, base.BaseNetworkTest): pass neutron-fwaas-12.0.2/neutron_fwaas/tests/tempest_plugin/tests/api/test_fwaas_extensions.py0000664000175000017500000004466213555577713032425 0ustar zuulzuul00000000000000# Copyright 2014 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. import six from tempest import config from tempest.lib.common.utils import data_utils from tempest.lib.common.utils import test_utils from tempest.lib import decorators from tempest.lib import exceptions as lib_exc from tempest import test from neutron_fwaas.tests.tempest_plugin.tests.api import base CONF = config.CONF class FWaaSExtensionTestJSON(base.BaseFWaaSTest): """ Tests the following operations in the Neutron API using the REST client for Neutron: List firewall rules Create firewall rule Update firewall rule Delete firewall rule Show firewall rule List firewall policies Create firewall policy Update firewall policy Insert firewall rule to policy Remove firewall rule from policy Insert firewall rule after/before rule in policy Update firewall policy audited attribute Delete firewall policy Show firewall policy List firewall Create firewall Update firewall Delete firewall Show firewall """ @classmethod def resource_setup(cls): super(FWaaSExtensionTestJSON, cls).resource_setup() if not test.is_extension_enabled('fwaas', 'network'): msg = "FWaaS Extension not enabled." raise cls.skipException(msg) def setUp(self): super(FWaaSExtensionTestJSON, self).setUp() self.fw_rule = self.create_firewall_rule(action="allow", protocol="tcp") self.fw_policy = self.create_firewall_policy() def _try_delete_router(self, router): # delete router, if it exists try: self.delete_router(router) # if router is not found, this means it was deleted in the test except lib_exc.NotFound: pass def _try_delete_policy(self, policy_id): # delete policy, if it exists try: self.firewall_policies_client.delete_firewall_policy(policy_id) # if policy is not found, this means it was deleted in the test except lib_exc.NotFound: pass def _try_delete_rule(self, rule_id): # delete rule, if it exists try: self.firewall_rules_client.delete_firewall_rule(rule_id) # if rule is not found, this means it was deleted in the test except lib_exc.NotFound: pass def _try_delete_firewall(self, fw_id): # delete firewall, if it exists try: self.firewalls_client.delete_firewall(fw_id) # if firewall is not found, this means it was deleted in the test except lib_exc.NotFound: pass self.firewalls_client.wait_for_resource_deletion(fw_id) def _wait_until_ready(self, fw_id): target_states = ('ACTIVE', 'CREATED') def _wait(): firewall = self.firewalls_client.show_firewall(fw_id) firewall = firewall['firewall'] return firewall['status'] in target_states if not test_utils.call_until_true(_wait, CONF.network.build_timeout, CONF.network.build_interval): m = ("Timed out waiting for firewall %s to reach %s state(s)" % (fw_id, target_states)) raise lib_exc.TimeoutException(m) def _wait_until_deleted(self, fw_id): def _wait(): try: firewall = self.firewalls_client.show_firewall(fw_id) except lib_exc.NotFound: return True fw_status = firewall['firewall']['status'] if fw_status == 'ERROR': raise lib_exc.DeleteErrorException(resource_id=fw_id) if not test_utils.call_until_true(_wait, CONF.network.build_timeout, CONF.network.build_interval): m = ("Timed out waiting for firewall %s deleted" % fw_id) raise lib_exc.TimeoutException(m) @decorators.idempotent_id('1b84cf01-9c09-4ce7-bc72-b15e39076468') @decorators.attr(type='smoke') def test_list_firewall_rules(self): # List firewall rules fw_rules = self.firewall_rules_client.list_firewall_rules() fw_rules = fw_rules['firewall_rules'] self.assertIn((self.fw_rule['id'], self.fw_rule['name'], self.fw_rule['action'], self.fw_rule['protocol'], self.fw_rule['ip_version'], self.fw_rule['enabled']), [(m['id'], m['name'], m['action'], m['protocol'], m['ip_version'], m['enabled']) for m in fw_rules]) @decorators.idempotent_id('563564f7-7077-4f5e-8cdc-51f37ae5a2b9') @decorators.attr(type='smoke') def test_create_update_delete_firewall_rule(self): # Create firewall rule body = self.firewall_rules_client.create_firewall_rule( name=data_utils.rand_name("fw-rule"), action="allow", protocol="tcp") fw_rule_id = body['firewall_rule']['id'] self.addCleanup(self._try_delete_rule, fw_rule_id) # Update firewall rule body = self.firewall_rules_client.update_firewall_rule(fw_rule_id, action="deny") self.assertEqual("deny", body["firewall_rule"]['action']) # Delete firewall rule self.firewall_rules_client.delete_firewall_rule(fw_rule_id) # Confirm deletion fw_rules = self.firewall_rules_client.list_firewall_rules() self.assertNotIn(fw_rule_id, [m['id'] for m in fw_rules['firewall_rules']]) @decorators.idempotent_id('3ff8c08e-26ff-4034-ae48-810ed213a998') @decorators.attr(type='smoke') def test_show_firewall_rule(self): # show a created firewall rule fw_rule = self.firewall_rules_client.show_firewall_rule( self.fw_rule['id']) for key, value in six.iteritems(fw_rule['firewall_rule']): self.assertEqual(self.fw_rule[key], value) @decorators.idempotent_id('1086dd93-a4c0-4bbb-a1bd-6d4bc62c199f') @decorators.attr(type='smoke') def test_list_firewall_policies(self): fw_policies = self.firewall_policies_client.list_firewall_policies() fw_policies = fw_policies['firewall_policies'] self.assertIn((self.fw_policy['id'], self.fw_policy['name'], self.fw_policy['firewall_rules']), [(m['id'], m['name'], m['firewall_rules']) for m in fw_policies]) @decorators.idempotent_id('bbf37b6c-498c-421e-9c95-45897d3ed775') @decorators.attr(type='smoke') def test_create_update_delete_firewall_policy(self): # Create firewall policy body = self.firewall_policies_client.create_firewall_policy( name=data_utils.rand_name("fw-policy")) fw_policy_id = body['firewall_policy']['id'] self.addCleanup(self._try_delete_policy, fw_policy_id) # Update firewall policy body = self.firewall_policies_client.update_firewall_policy( fw_policy_id, name="updated_policy") updated_fw_policy = body["firewall_policy"] self.assertEqual("updated_policy", updated_fw_policy['name']) # Delete firewall policy self.firewall_policies_client.delete_firewall_policy(fw_policy_id) # Confirm deletion fw_policies = self.firewall_policies_client.list_firewall_policies() fw_policies = fw_policies['firewall_policies'] self.assertNotIn(fw_policy_id, [m['id'] for m in fw_policies]) @decorators.idempotent_id('1df59b3a-517e-41d4-96f6-fc31cf4ecff2') @decorators.attr(type='smoke') def test_show_firewall_policy(self): # show a created firewall policy fw_policy = self.firewall_policies_client.show_firewall_policy( self.fw_policy['id']) fw_policy = fw_policy['firewall_policy'] for key, value in six.iteritems(fw_policy): self.assertEqual(self.fw_policy[key], value) @decorators.idempotent_id('02082a03-3cdd-4789-986a-1327dd80bfb7') @decorators.attr(type='smoke') def test_create_show_delete_firewall(self): # Create tenant network resources required for an ACTIVE firewall network = self.create_network() subnet = self.create_subnet(network) router = self.create_router( data_utils.rand_name('router-'), admin_state_up=True) self.addCleanup(self._try_delete_router, router) self.routers_client.add_router_interface(router['id'], subnet_id=subnet['id']) # Create firewall body = self.firewalls_client.create_firewall( name=data_utils.rand_name("firewall"), firewall_policy_id=self.fw_policy['id']) created_firewall = body['firewall'] firewall_id = created_firewall['id'] self.addCleanup(self._try_delete_firewall, firewall_id) # Wait for the firewall resource to become ready self._wait_until_ready(firewall_id) # show a created firewall firewall = self.firewalls_client.show_firewall(firewall_id) firewall = firewall['firewall'] for key, value in six.iteritems(firewall): if key == 'status': continue self.assertEqual(created_firewall[key], value) # list firewall firewalls = self.firewalls_client.list_firewalls() firewalls = firewalls['firewalls'] self.assertIn((created_firewall['id'], created_firewall['name'], created_firewall['firewall_policy_id']), [(m['id'], m['name'], m['firewall_policy_id']) for m in firewalls]) # Delete firewall self.firewalls_client.delete_firewall(firewall_id) # Wait for the firewall resource to be deleted self._wait_until_deleted(firewall_id) # Confirm deletion firewalls = self.firewalls_client.list_firewalls()['firewalls'] self.assertNotIn(firewall_id, [m['id'] for m in firewalls]) @decorators.idempotent_id('1355cf5c-77d4-4bb9-87d7-e50c194d08b5') def test_firewall_insertion_mode_add_remove_router(self): # Create legacy routers router1 = self.create_router( data_utils.rand_name('router-'), admin_state_up=True) self.addCleanup(self._try_delete_router, router1) router2 = self.create_router( data_utils.rand_name('router-'), admin_state_up=True) self.addCleanup(self._try_delete_router, router2) # Create firewall on a router1 body = self.firewalls_client.create_firewall( name=data_utils.rand_name("firewall"), firewall_policy_id=self.fw_policy['id'], router_ids=[router1['id']]) created_firewall = body['firewall'] firewall_id = created_firewall['id'] self.addCleanup(self._try_delete_firewall, firewall_id) self.assertEqual([router1['id']], created_firewall['router_ids']) # Legacy routers are scheduled on L3 agents on network plug events # Hence firewall resource will not became ready at this stage network = self.create_network() subnet = self.create_subnet(network) self.routers_client.add_router_interface(router1['id'], subnet_id=subnet['id']) # Wait for the firewall resource to become ready self._wait_until_ready(firewall_id) # Add router2 to the firewall body = self.firewalls_client.update_firewall( firewall_id, router_ids=[router1['id'], router2['id']]) updated_firewall = body['firewall'] self.assertIn(router2['id'], updated_firewall['router_ids']) self.assertEqual(2, len(updated_firewall['router_ids'])) # Wait for the firewall resource to become ready self._wait_until_ready(firewall_id) # Remove router1 from the firewall body = self.firewalls_client.update_firewall( firewall_id, router_ids=[router2['id']]) updated_firewall = body['firewall'] self.assertNotIn(router1['id'], updated_firewall['router_ids']) self.assertEqual(1, len(updated_firewall['router_ids'])) @decorators.idempotent_id('c60ceff5-d51f-451d-b6e6-cb983d16ab6b') def test_firewall_insertion_mode_one_firewall_per_router(self): # Create router required for an ACTIVE firewall router = self.create_router( data_utils.rand_name('router1-'), admin_state_up=True) self.addCleanup(self._try_delete_router, router) # Create firewall body = self.firewalls_client.create_firewall( name=data_utils.rand_name("firewall"), firewall_policy_id=self.fw_policy['id'], router_ids=[router['id']]) created_firewall = body['firewall'] self.addCleanup(self._try_delete_firewall, created_firewall['id']) # Try to create firewall with the same router self.assertRaisesRegex( lib_exc.Conflict, "already associated with other firewall", self.firewalls_client.create_firewall, name=data_utils.rand_name("firewall"), firewall_policy_id=self.fw_policy['id'], router_ids=[router['id']]) @decorators.attr(type='smoke') @decorators.idempotent_id('53305b4b-9897-4e01-87c0-2ae386083180') def test_firewall_rule_insertion_position_removal_rule_from_policy(self): # Create firewall rule body = self.firewall_rules_client.create_firewall_rule( name=data_utils.rand_name("fw-rule"), action="allow", protocol="tcp") fw_rule_id1 = body['firewall_rule']['id'] self.addCleanup(self._try_delete_rule, fw_rule_id1) # Create firewall policy body = self.firewall_policies_client.create_firewall_policy( name=data_utils.rand_name("fw-policy")) fw_policy_id = body['firewall_policy']['id'] self.addCleanup(self._try_delete_policy, fw_policy_id) # Insert rule to firewall policy self.firewall_policies_client.insert_firewall_rule_in_policy( fw_policy_id, fw_rule_id1, '', '') # Verify insertion of rule in policy self.assertIn(fw_rule_id1, self._get_list_fw_rule_ids(fw_policy_id)) # Create another firewall rule body = self.firewall_rules_client.create_firewall_rule( name=data_utils.rand_name("fw-rule"), action="allow", protocol="icmp") fw_rule_id2 = body['firewall_rule']['id'] self.addCleanup(self._try_delete_rule, fw_rule_id2) # Insert rule to firewall policy after the first rule self.firewall_policies_client.insert_firewall_rule_in_policy( fw_policy_id, fw_rule_id2, fw_rule_id1, '') # Verify the position of rule after insertion fw_rule = self.firewall_rules_client.show_firewall_rule( fw_rule_id2) self.assertEqual(int(fw_rule['firewall_rule']['position']), 2) # Remove rule from the firewall policy self.firewall_policies_client.remove_firewall_rule_from_policy( fw_policy_id, fw_rule_id2) # Insert rule to firewall policy before the first rule self.firewall_policies_client.insert_firewall_rule_in_policy( fw_policy_id, fw_rule_id2, '', fw_rule_id1) # Verify the position of rule after insertion fw_rule = self.firewall_rules_client.show_firewall_rule( fw_rule_id2) self.assertEqual(int(fw_rule['firewall_rule']['position']), 1) # Remove rule from the firewall policy self.firewall_policies_client.remove_firewall_rule_from_policy( fw_policy_id, fw_rule_id2) # Verify removal of rule from firewall policy self.assertNotIn(fw_rule_id2, self._get_list_fw_rule_ids(fw_policy_id)) # Remove rule from the firewall policy self.firewall_policies_client.remove_firewall_rule_from_policy( fw_policy_id, fw_rule_id1) # Verify removal of rule from firewall policy self.assertNotIn(fw_rule_id1, self._get_list_fw_rule_ids(fw_policy_id)) def _get_list_fw_rule_ids(self, fw_policy_id): fw_policy = self.firewall_policies_client.show_firewall_policy( fw_policy_id) return [ruleid for ruleid in fw_policy['firewall_policy'] ['firewall_rules']] @decorators.idempotent_id('8515ca8a-0d2f-4298-b5ff-6f924e4587ca') def test_update_firewall_policy_audited_attribute(self): # Create firewall rule body = self.firewall_rules_client.create_firewall_rule( name=data_utils.rand_name("fw-rule"), action="allow", protocol="icmp") fw_rule_id = body['firewall_rule']['id'] self.addCleanup(self._try_delete_rule, fw_rule_id) # Create firewall policy body = self.firewall_policies_client.create_firewall_policy( name=data_utils.rand_name('fw-policy')) fw_policy_id = body['firewall_policy']['id'] self.addCleanup(self._try_delete_policy, fw_policy_id) self.assertFalse(body['firewall_policy']['audited']) # Update firewall policy audited attribute to true self.firewall_policies_client.update_firewall_policy(fw_policy_id, audited=True) # Insert Firewall rule to firewall policy self.firewall_policies_client.insert_firewall_rule_in_policy( fw_policy_id, fw_rule_id, '', '') body = self.firewall_policies_client.show_firewall_policy( fw_policy_id) self.assertFalse(body['firewall_policy']['audited']) neutron-fwaas-12.0.2/neutron_fwaas/tests/contrib/0000775000175000017500000000000013555600005022063 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/contrib/filters.template0000664000175000017500000000137413555577713025320 0ustar zuulzuul00000000000000# 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 neutron-fwaas-12.0.2/neutron_fwaas/tests/contrib/gate_hook_tempest.sh0000775000175000017500000000204613555577713026150 0ustar zuulzuul00000000000000#!/bin/bash set -ex FWAAS_VERSION=$1 GATE_DEST=$BASE/new GATE_HOOKS=$GATE_DEST/neutron-fwaas/neutron_fwaas/tests/contrib/hooks DEVSTACK_PATH=$GATE_DEST/devstack LOCAL_CONF=$DEVSTACK_PATH/late-local.conf DSCONF=/tmp/devstack-tools/bin/dsconf # Install devstack-tools used to produce local.conf; we can't rely on # test-requirements.txt because the gate hook is triggered before neutron-fwaas # is installed sudo -H pip install virtualenv virtualenv /tmp/devstack-tools /tmp/devstack-tools/bin/pip install -U devstack-tools==0.4.0 # Inject config from hook into localrc function load_rc_hook { local hook="$1" local tmpfile local config tmpfile=$(tempfile) config=$(cat $GATE_HOOKS/$hook) echo "[[local|localrc]]" > $tmpfile $DSCONF setlc_raw $tmpfile "$config" $DSCONF merge_lc $LOCAL_CONF $tmpfile rm -f $tmpfile } LOCAL_CONF=$DEVSTACK_PATH/local.conf load_rc_hook api_extensions-base load_rc_hook api_extensions-${FWAAS_VERSION} export DEVSTACK_LOCALCONF=$(cat $LOCAL_CONF) $BASE/new/devstack-gate/devstack-vm-gate.sh neutron-fwaas-12.0.2/neutron_fwaas/tests/contrib/post_test_hook.sh0000775000175000017500000000225013555577713025510 0ustar zuulzuul00000000000000#!/bin/bash set -xe FWAAS_DIR="$BASE/new/neutron-fwaas" NEUTRON_DIR="$BASE/new/neutron" TEMPEST_DIR="$BASE/new/tempest" SCRIPTS_DIR="/usr/os-testr-env/bin" function generate_testr_results { # Give job user rights to access tox logs sudo -H -u $owner chmod o+rw . sudo -H -u $owner chmod o+rw -R .testrepository if [ -f ".testrepository/0" ] ; then .tox/dsvm-functional/bin/subunit-1to2 < .testrepository/0 > ./testrepository.subunit $SCRIPTS_DIR/subunit2html ./testrepository.subunit testr_results.html gzip -9 ./testrepository.subunit gzip -9 ./testr_results.html sudo mv ./*.gz /opt/stack/logs/ fi } function dsvm_functional_prep_func { : } owner=stack prep_func="dsvm_functional_prep_func" # Set owner permissions according to job's requirements. cd $FWAAS_DIR sudo chown -R $owner:stack $FWAAS_DIR sudo chown -R $owner:stack $NEUTRON_DIR # Prep the environment according to job's requirements. $prep_func # Run tests echo "Running neutron dsvm-functional test suite" set +e sudo -H -u $owner tox -e dsvm-functional testr_exit_code=$? set -e # Collect and parse results generate_testr_results exit $testr_exit_code neutron-fwaas-12.0.2/neutron_fwaas/tests/contrib/functional-testing.filters0000664000175000017500000000034413555577713027316 0ustar zuulzuul00000000000000# 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 neutron-fwaas-12.0.2/neutron_fwaas/tests/contrib/hooks/0000775000175000017500000000000013555600005023206 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/contrib/hooks/api_extensions-v10000664000175000017500000000006413555577713026530 0ustar zuulzuul00000000000000NETWORK_API_EXTENSIONS+=,fwaas,fwaasrouterinsertion neutron-fwaas-12.0.2/neutron_fwaas/tests/contrib/hooks/api_extensions-v20000664000175000017500000000004213555577713026525 0ustar zuulzuul00000000000000NETWORK_API_EXTENSIONS+=,fwaas_v2 neutron-fwaas-12.0.2/neutron_fwaas/tests/contrib/hooks/api_extensions-base0000664000175000017500000000045013555577713027113 0ustar zuulzuul00000000000000NETWORK_API_EXTENSIONS=agent,binding,dhcp_agent_scheduler,external-net,ext-gw-mode,extra_dhcp_opts,quotas,router,security-group,subnet_allocation,network-ip-availability,auto-allocated-topology,timestamp_core,tag,service-type,rbac-policies,standard-attr-description,pagination,sorting,project-id neutron-fwaas-12.0.2/neutron_fwaas/tests/contrib/hooks/api_extensions-legacy0000664000175000017500000000006413555577713027446 0ustar zuulzuul00000000000000NETWORK_API_EXTENSIONS+=,fwaas,fwaasrouterinsertion neutron-fwaas-12.0.2/neutron_fwaas/tests/contrib/gate_hook.sh0000775000175000017500000000073613555577713024413 0ustar zuulzuul00000000000000#!/bin/bash set -ex VENV=${1:-"dsvm-functional"} GATE_DEST=$BASE/new FWAAS_PATH=$GATE_DEST/neutron-fwaas DEVSTACK_PATH=$GATE_DEST/devstack case $VENV in "dsvm-functional"|"dsvm-fullstack") # The following need to be set before sourcing # configure_for_fwaas_func_testing. GATE_STACK_USER=stack PROJECT_NAME=neutron-fwaas IS_GATE=True source $FWAAS_PATH/tools/configure_for_fwaas_func_testing.sh configure_host_for_func_testing ;; esac neutron-fwaas-12.0.2/neutron_fwaas/tests/contrib/README0000664000175000017500000000022613555577713022766 0ustar zuulzuul00000000000000The 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. neutron-fwaas-12.0.2/neutron_fwaas/tests/base.py0000664000175000017500000000307313555577713021735 0ustar zuulzuul00000000000000# 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) neutron-fwaas-12.0.2/neutron_fwaas/tests/__init__.py0000664000175000017500000000000013555577713022545 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/functional/0000775000175000017500000000000013555600005022565 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/functional/privileged/0000775000175000017500000000000013555600005024717 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/functional/privileged/test_utils.py0000664000175000017500000000260513555577713027516 0ustar zuulzuul00000000000000# 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 from neutron.tests.common import net_helpers from neutron.tests.functional import base from neutron_fwaas.privileged.tests.functional import utils def get_netns_inode(namespace): return os.stat('/var/run/netns/%s' % namespace).st_ino class InNamespaceTest(base.BaseSudoTestCase): def test_in_namespace(self): namespace = self.useFixture(net_helpers.NamespaceFixture()).name expected = get_netns_inode(namespace) before, observed, after = utils.get_in_namespace_netns_inodes( namespace) self.assertEqual(expected, observed) self.assertEqual(before, after) def test_in_no_namespace(self): inodes = utils.get_in_namespace_netns_inodes(None) self.assertEqual(1, len(set(inodes))) neutron-fwaas-12.0.2/neutron_fwaas/tests/functional/privileged/__init__.py0000664000175000017500000000000013555577713027041 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/functional/privileged/test_netlink_lib.py0000664000175000017500000001625113555577713030652 0ustar zuulzuul00000000000000# 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 neutron.agent.linux import utils as linux_utils from neutron.tests.common import net_helpers from neutron.tests.functional import base as functional_base from oslo_log import log as logging import neutron_fwaas.privileged.netlink_lib as nl_lib LOG = logging.getLogger(__name__) def check_nf_conntrack_ipv6_is_loaded(): try: output = linux_utils.execute(['lsmod']) except RuntimeError: msg = "Failed execute command lsmod!" raise RuntimeError(msg) if 'nf_conntrack_ipv6' in output: return True return False def _create_entries(namespace, conntrack_cmds): for cmd in conntrack_cmds: exec_cmd = ['ip', 'netns', 'exec', namespace] + cmd try: linux_utils.execute(exec_cmd, run_as_root=True, check_exit_code=True, extra_ok_codes=[1]) except RuntimeError: raise Exception('Error while creating entry') class NetlinkLibTestCase(functional_base.BaseSudoTestCase): """Functional test for netlink_lib: List, delete, flush conntrack entries. For each function, first we add a specific namespace, then create real conntrack entries. netlink_lib function will do list, delete and flush these entries. This class will test this netlink_lib function work as expected. """ CONNTRACK_CMDS = ( ['conntrack', '-I', '-p', 'tcp', '-s', '1.1.1.1', '-d', '2.2.2.2', '--sport', '1', '--dport', '2', '--state', 'ESTABLISHED', '--timeout', '1234'], ['conntrack', '-I', '-p', 'udp', '-s', '1.1.1.1', '-d', '2.2.2.2', '--sport', '1', '--dport', '2', '--timeout', '1234'], ['conntrack', '-I', '-p', 'icmp', '-s', '1.1.1.1', '-d', '2.2.2.2', '--icmp-type', '8', '--icmp-code', '0', '--icmp-id', '3333', '--timeout', '1234'], ['conntrack', '-I', '-p', 'icmp', '-s', '1.1.1.1', '-d', '2.2.2.2', '--icmp-type', '8', '--icmp-code', '0', '--icmp-id', '3333', '--timeout', '1234'], ) def test_list_entries(self): namespace = self.useFixture(net_helpers.NamespaceFixture()).name _create_entries(namespace, self.CONNTRACK_CMDS) expected = ( (4, 'icmp', 8, 0, '1.1.1.1', '2.2.2.2', 3333), (4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2'), (4, 'udp', 1, 2, '1.1.1.1', '2.2.2.2') ) entries_list = nl_lib.list_entries(namespace=namespace) self.assertEqual(expected, entries_list) def _delete_entry(self, delete_entries, remain_entries): namespace = self.useFixture(net_helpers.NamespaceFixture()).name _create_entries(namespace, self.CONNTRACK_CMDS) nl_lib.delete_entries(namespace=namespace, entries=delete_entries) entries_list = nl_lib.list_entries(namespace) self.assertEqual(remain_entries, entries_list) def test_delete_icmp_entry(self): icmp_entry = [(4, 'icmp', 8, 0, '1.1.1.1', '2.2.2.2', 3333)] remain_entries = ( (4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2'), (4, 'udp', 1, 2, '1.1.1.1', '2.2.2.2'), ) self._delete_entry(icmp_entry, remain_entries) def test_delete_tcp_entry(self): tcp_entry = [(4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2')] remain_entries = ( (4, 'icmp', 8, 0, '1.1.1.1', '2.2.2.2', 3333), (4, 'udp', 1, 2, '1.1.1.1', '2.2.2.2') ) self._delete_entry(tcp_entry, remain_entries) def test_delete_udp_entry(self): udp_entry = [(4, 'udp', 1, 2, '1.1.1.1', '2.2.2.2')] remain_entries = ( (4, 'icmp', 8, 0, '1.1.1.1', '2.2.2.2', 3333), (4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2') ) self._delete_entry(udp_entry, remain_entries) def test_delete_multiple_entries(self): delete_entries = ( (4, 'icmp', 8, 0, '1.1.1.1', '2.2.2.2', 3333), (4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2'), (4, 'udp', 1, 2, '1.1.1.1', '2.2.2.2') ) remain_entries = () self._delete_entry(delete_entries, remain_entries) def test_flush_entries(self): namespace = self.useFixture(net_helpers.NamespaceFixture()).name _create_entries(namespace, self.CONNTRACK_CMDS) nl_lib.flush_entries(namespace) entries_list = nl_lib.list_entries(namespace) self.assertEqual((), entries_list) class NetlinkLibTestCaseIPv6(functional_base.BaseSudoTestCase): CONNTRACK_CMDS = ( ['conntrack', '-I', '-p', 'icmp', '-s', '1.1.1.1', '-d', '2.2.2.2', '--icmp-type', '8', '--icmp-code', '0', '--icmp-id', '3333', '--timeout', '1234'], ['conntrack', '-I', '-p', 'icmpv6', '-s', '10::10', '-d', '20::20', '--icmpv6-type', '128', '--icmpv6-code', '0', '--icmpv6-id', '3456', '--timeout', '1234'], ) def setUp(self): super(NetlinkLibTestCaseIPv6, self).setUp() if not check_nf_conntrack_ipv6_is_loaded(): self.skipTest( "nf_conntrack_ipv6 module wasn't loaded. Please load" "this module into your system if you want to use " "netlink conntrack with ipv6" ) def test_list_entries(self): namespace = self.useFixture(net_helpers.NamespaceFixture()).name _create_entries(namespace, self.CONNTRACK_CMDS) expected = ( (4, 'icmp', 8, 0, '1.1.1.1', '2.2.2.2', 3333), (6, 'icmpv6', 128, 0, '10::10', '20::20', 3456), ) entries_list = nl_lib.list_entries(namespace=namespace) self.assertEqual(expected, entries_list) def _delete_entry(self, delete_entries, remain_entries): namespace = self.useFixture(net_helpers.NamespaceFixture()).name _create_entries(namespace, self.CONNTRACK_CMDS) nl_lib.delete_entries(namespace=namespace, entries=delete_entries) entries_list = nl_lib.list_entries(namespace) self.assertEqual(remain_entries, entries_list) def test_delete_icmpv6_entry(self): icmp_entry = [(6, 'icmpv6', 128, 0, '10::10', '20::20', 3456)] remain_entries = ( (4, 'icmp', 8, 0, '1.1.1.1', '2.2.2.2', 3333), ) self._delete_entry(icmp_entry, remain_entries) def test_flush_entries(self): namespace = self.useFixture(net_helpers.NamespaceFixture()).name _create_entries(namespace, self.CONNTRACK_CMDS) nl_lib.flush_entries(namespace) entries_list = nl_lib.list_entries(namespace) self.assertEqual((), entries_list) neutron-fwaas-12.0.2/neutron_fwaas/tests/functional/privileged/test_dummy.py0000664000175000017500000000150113555577713027503 0ustar zuulzuul00000000000000# 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 neutron.tests.functional import base from neutron_fwaas.privileged.tests.functional import dummy class DummyTest(base.BaseSudoTestCase): def test_dummy(self): dummy.dummy() neutron-fwaas-12.0.2/neutron_fwaas/tests/functional/__init__.py0000664000175000017500000000000013555577713024707 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/functional/db/0000775000175000017500000000000013555600005023152 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/functional/db/test_migrations.py0000664000175000017500000000753513555577713026774 0ustar zuulzuul00000000000000# 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 alembic import script as alembic_script from neutron.db.migration.alembic_migrations import external from neutron.db.migration import cli as migration from neutron.tests.functional.db import test_migrations from neutron.tests.unit import testlib_api from oslo_config import cfg import sqlalchemy from neutron_fwaas.db.models import head # EXTERNAL_TABLES should contain all names of tables that are not related to # current repo. EXTERNAL_TABLES = set(external.TABLES) - set(external.FWAAS_TABLES) # Model moved to vendor repo EXTERNAL_TABLES.update({'cisco_firewall_associations'}) VERSION_TABLE = 'alembic_version_fwaas' class _TestModelsMigrationsFWaaS(test_migrations._TestModelsMigrations): def db_sync(self, engine): cfg.CONF.set_override('connection', engine.url, group='database') for conf in migration.get_alembic_configs(): self.alembic_config = conf self.alembic_config.neutron_config = cfg.CONF migration.do_alembic_command(conf, 'upgrade', 'heads') def get_metadata(self): return head.get_metadata() def include_object(self, object_, name, type_, reflected, compare_to): if type_ == 'table' and (name.startswith('alembic') or name == VERSION_TABLE or name in EXTERNAL_TABLES): return False if type_ == 'index' and reflected and name.startswith("idx_autoinc_"): return False return True class TestModelsMigrationsMysql(testlib_api.MySQLTestCaseMixin, _TestModelsMigrationsFWaaS, testlib_api.SqlTestCaseLight): pass class TestModelsMigrationsPostgresql(testlib_api.PostgreSQLTestCaseMixin, _TestModelsMigrationsFWaaS, testlib_api.SqlTestCaseLight): pass class TestSanityCheck(testlib_api.SqlTestCaseLight): BUILD_SCHEMA = False def setUp(self): super(TestSanityCheck, self).setUp() for conf in migration.get_alembic_configs(): self.alembic_config = conf self.alembic_config.neutron_config = cfg.CONF def _drop_table(self, table): with self.engine.begin() as conn: table.drop(conn) def test_check_sanity_f24e0d5e5bff(self): current_revision = "f24e0d5e5bff" fwg_port_association = sqlalchemy.Table( 'firewall_group_port_associations_v2', sqlalchemy.MetaData(), sqlalchemy.Column('firewall_group_id', sqlalchemy.String(36)), sqlalchemy.Column('port_id', sqlalchemy.String(36))) with self.engine.connect() as conn: fwg_port_association.create(conn) self.addCleanup(self._drop_table, fwg_port_association) conn.execute(fwg_port_association.insert(), [ {'firewall_group_id': '1234', 'port_id': '12345'}, {'firewall_group_id': '12343', 'port_id': '12345'} ]) script_dir = alembic_script.ScriptDirectory.from_config( self.alembic_config) script = script_dir.get_revision(current_revision).module self.assertRaises( script.DuplicatePortRecordinFirewallGroupPortAssociation, script.check_sanity, conn) neutron-fwaas-12.0.2/neutron_fwaas/tests/functional/db/__init__.py0000664000175000017500000000000013555577713025274 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/etc/0000775000175000017500000000000013555600005021176 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/etc/neutron/0000775000175000017500000000000013555600005022670 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/etc/neutron/policy.d/0000775000175000017500000000000013555600005024411 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/tests/etc/neutron/policy.d/neutron-fwaas.json0000664000175000017500000000367013555577713030126 0ustar zuulzuul00000000000000{ "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" } neutron-fwaas-12.0.2/neutron_fwaas/db/0000775000175000017500000000000013555600005017646 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/db/models/0000775000175000017500000000000013555600005021131 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/db/models/head.py0000664000175000017500000000151013555577713022424 0ustar zuulzuul00000000000000# 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 neutron-fwaas-12.0.2/neutron_fwaas/db/models/__init__.py0000664000175000017500000000000013555577713023253 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/db/firewall/0000775000175000017500000000000013555600005021453 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/db/firewall/firewall_db.py0000664000175000017500000007433313555577713024334 0ustar zuulzuul00000000000000# 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() neutron-fwaas-12.0.2/neutron_fwaas/db/firewall/firewall_router_insertion_db.py0000664000175000017500000000747713555577713030033 0ustar zuulzuul00000000000000# 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 neutron-fwaas-12.0.2/neutron_fwaas/db/firewall/__init__.py0000664000175000017500000000000013555577713023575 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/db/firewall/v2/0000775000175000017500000000000013555600005022002 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/db/firewall/v2/__init__.py0000664000175000017500000000000013555577713024124 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/db/firewall/v2/firewall_db_v2.py0000664000175000017500000015067413555577713025275 0ustar zuulzuul00000000000000# 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 copy import netaddr from neutron.db import api as db_api from neutron.db import common_db_mixin as base_db from neutron_lib.api.definitions import constants as fw_const from neutron_lib.api import validators from neutron_lib import constants as nl_constants from neutron_lib.db import constants as db_constants from neutron_lib.db import model_base from neutron_lib import exceptions from neutron_lib.exceptions import firewall_v2 as f_exc from oslo_config import cfg from oslo_db import exception as db_exc 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 or_ from sqlalchemy import orm from sqlalchemy.orm import exc from neutron_fwaas.common import fwaas_constants as const from neutron_fwaas.extensions import firewall_v2 as fw_ext LOG = logging.getLogger(__name__) class FirewallDefaultParameterExists(exceptions.InUse): """Default Firewall Parameter conflict exception Occurs when user creates/updates any existing firewall resource with reserved parameter names. """ message = ("Operation cannot be performed since '%(name)s' " "is a reserved name for %(resource_type)s.") class FirewallDefaultObjectUpdateRestricted(FirewallDefaultParameterExists): message = ("Operation cannot be performed on default object " "'%(resource_id)s' of type %(resource_type)s.") class HasName(object): name = sa.Column(sa.String(db_constants.NAME_FIELD_SIZE)) class HasDescription(object): description = sa.Column( sa.String(db_constants.LONG_DESCRIPTION_FIELD_SIZE)) 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(db_constants.NAME_FIELD_SIZE)) description = sa.Column( sa.String(db_constants.LONG_DESCRIPTION_FIELD_SIZE)) ingress_firewall_policy_id = sa.Column( sa.String(db_constants.UUID_FIELD_SIZE), sa.ForeignKey('firewall_policies_v2.id')) egress_firewall_policy_id = sa.Column( sa.String(db_constants.UUID_FIELD_SIZE), sa.ForeignKey('firewall_policies_v2.id')) admin_state_up = sa.Column(sa.Boolean) status = sa.Column(sa.String(db_constants.STATUS_FIELD_SIZE)) shared = sa.Column(sa.Boolean) class DefaultFirewallGroup(model_base.BASEV2, model_base.HasProjectPrimaryKey): __tablename__ = "default_firewall_groups" firewall_group_id = sa.Column(sa.String(db_constants.UUID_FIELD_SIZE), sa.ForeignKey('firewall_groups_v2.id', ondelete="CASCADE"), nullable=False) firewall_group = orm.relationship( FirewallGroup, lazy='joined', backref=orm.backref('default_firewall_group', cascade='all,delete'), primaryjoin="FirewallGroup.id==DefaultFirewallGroup.firewall_group_id", ) class FirewallGroupPortAssociation(model_base.BASEV2): __tablename__ = 'firewall_group_port_associations_v2' firewall_group_id = sa.Column(sa.String(db_constants.UUID_FIELD_SIZE), sa.ForeignKey('firewall_groups_v2.id', ondelete="CASCADE"), primary_key=True) port_id = sa.Column(sa.String(db_constants.UUID_FIELD_SIZE), sa.ForeignKey('ports.id', ondelete="CASCADE"), unique=True, 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(db_constants.UUID_FIELD_SIZE), sa.ForeignKey('firewall_policies_v2.id', ondelete="CASCADE"), primary_key=True) firewall_rule_id = sa.Column(sa.String(db_constants.UUID_FIELD_SIZE), 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(db_constants.NAME_FIELD_SIZE)) description = sa.Column( sa.String(db_constants.LONG_DESCRIPTION_FIELD_SIZE)) 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, policies=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'], 'firewall_policy_id': policies, '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_default_firewall_rules(self, context, tenant_id): # NOTE(xgerman) Maybe generating the final set of rules from a # configuration file makes sense. Can be done some time later # 1. Drop any IPv4 packets for ingress traffic in_fwr_v4 = {'firewall_rule': { 'description': 'default ingress rule for IPv4', 'name': 'default ingress ipv4 (deny all)', 'shared': False, 'protocol': None, 'tenant_id': tenant_id, 'ip_version': nl_constants.IP_VERSION_4, 'action': fw_const.FWAAS_DENY, 'enabled': True, 'source_port': None, 'source_ip_address': None, 'destination_port': None, 'destination_ip_address': None, }} # 2. Drop any IPv6 packets for ingress traffic in_fwr_v6 = copy.deepcopy(in_fwr_v4) in_fwr_v6[ 'firewall_rule']['description'] = 'default ingress rule for IPv6' in_fwr_v6['firewall_rule']['name'] = 'default ingress ipv6 (deny all)' in_fwr_v6['firewall_rule']['ip_version'] = nl_constants.IP_VERSION_6 # 3. Allow any IPv4 packets for egress traffic eg_fwr_v4 = copy.deepcopy(in_fwr_v4) eg_fwr_v4[ 'firewall_rule']['description'] = 'default egress rule for IPv4' eg_fwr_v4['firewall_rule']['action'] = fw_const.FWAAS_ALLOW eg_fwr_v4['firewall_rule']['name'] = 'default egress ipv4 (allow all)' # 4. Allow any IPv6 packets for egress traffic eg_fwr_v6 = copy.deepcopy(in_fwr_v6) eg_fwr_v6[ 'firewall_rule']['description'] = 'default egress rule for IPv6' eg_fwr_v6['firewall_rule']['name'] = 'default egress ipv6 (allow all)' eg_fwr_v6['firewall_rule']['action'] = fw_const.FWAAS_ALLOW return { 'in_ipv4': self.create_firewall_rule(context, in_fwr_v4)['id'], 'in_ipv6': self.create_firewall_rule(context, in_fwr_v6)['id'], 'eg_ipv4': self.create_firewall_rule(context, eg_fwr_v4)['id'], 'eg_ipv6': self.create_firewall_rule(context, eg_fwr_v6)['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(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 '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(rule_info) firewall_rule_id = rule_info['firewall_rule_id'] 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) policies = self._get_policies_with_rule(context, id) or None return self._make_firewall_rule_dict(fwr, fields, policies=policies) 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, rule_info): """Validate rule_info dict Check that all mandatory fields are present, otherwise raise proper exception. """ if not rule_info or 'firewall_rule_id' not in rule_info: raise f_exc.FirewallRuleInfoMissing() # Validator doesn't return anything if the check passes if validators.validate_uuid(rule_info['firewall_rule_id']): raise f_exc.FirewallRuleNotFound( firewall_rule_id=rule_info['firewall_rule_id']) 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): with context.session.begin(subtransactions=True): fwg_with_fwp_id_db = context.session.query(FirewallGroup).filter( or_(FirewallGroup.ingress_firewall_policy_id == fwp_id, FirewallGroup.egress_firewall_policy_id == fwp_id)) 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 _delete_all_rules_from_policy(self, context, fwp_db): """Deletes all FirewallPolicyRuleAssociation objects fwp_db is an DB dict representing firewall policy. Returns a dictionary with updated rule_associations. """ 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 fwp_db 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: self._delete_all_rules_from_policy(context, fwp_db) 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_all_rules_from_policy(context, fwp_db) # 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_default_firewall_policy(self, context, tenant_id, policy_type, **kwargs): fwrs = kwargs.get('firewall_rules', []) description = kwargs.get('description', '') name = (const.DEFAULT_FWP_INGRESS if policy_type == 'ingress' else const.DEFAULT_FWP_EGRESS) firewall_policy = {'firewall_policy': { 'name': name, 'description': description, 'audited': False, 'shared': False, 'firewall_rules': fwrs, 'tenant_id': tenant_id, }} return self._do_create_firewall_policy(context, firewall_policy) def _do_create_firewall_policy(self, context, firewall_policy): 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 create_firewall_policy(self, context, firewall_policy): LOG.debug("create_firewall_policy() called") self._ensure_not_default_resource(firewall_policy, 'firewall_policy') return self._do_create_firewall_policy(context, firewall_policy) 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) self._ensure_not_default_resource(fwp_db, 'firewall_policy', action="update") 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: fwp_db = self._delete_all_rules_from_policy(context, fwp_db) 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_tenant_for_fwg_policies(self, context, fwg, fwg_tenant_id): # On updates, all keys will not be present so fetch 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 exc_ports = [] for port_id in port_id_list: try: with context.session.begin(subtransactions=True): fwg_port_db = FirewallGroupPortAssociation( firewall_group_id=fwg_db['id'], port_id=port_id) context.session.add(fwg_port_db) except db_exc.DBDuplicateEntry: exc_ports.append(port_id) if exc_ports: raise f_exc.FirewallGroupPortInUse(port_ids=exc_ports) 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 _get_default_fwg_id(self, context, tenant_id): """Returns an id of default firewall group for given tenant or None""" default_fwg = self._model_query(context, FirewallGroup).filter_by( project_id=tenant_id, name=const.DEFAULT_FWG).first() if default_fwg: return default_fwg.id def _ensure_default_firewall_group(self, context, tenant_id): """Create a default firewall group if one doesn't exist for a tenant Returns the default firewall group id for a given tenant. """ exists = self._get_default_fwg_id(context, tenant_id) if exists: return exists try: # NOTE(cby): default fwg not created => we try to create it! with db_api.autonested_transaction(context.session): fwr_ids = self._create_default_firewall_rules( context, tenant_id) ingress_fwp = { 'description': 'Ingress firewall policy', 'firewall_rules': [fwr_ids['in_ipv4'], fwr_ids['in_ipv6']], } egress_fwp = { 'description': 'Egress firewall policy', 'firewall_rules': [fwr_ids['eg_ipv4'], fwr_ids['eg_ipv6']], } ingress_fwp_db = self._create_default_firewall_policy( context, tenant_id, 'ingress', **ingress_fwp) egress_fwp_db = self._create_default_firewall_policy( context, tenant_id, 'egress', **egress_fwp) fwg = { 'firewall_group': {'name': const.DEFAULT_FWG, 'tenant_id': tenant_id, 'ingress_firewall_policy_id': ingress_fwp_db['id'], 'egress_firewall_policy_id': egress_fwp_db['id'], 'ports': [], 'shared': False, 'admin_state_up': True, 'description': 'Default firewall group'} } fwg_db = self._create_firewall_group( context, fwg, status=nl_constants.INACTIVE, default_fwg=True) context.session.add(DefaultFirewallGroup( firewall_group_id=fwg_db['id'], project_id=tenant_id)) return fwg_db['id'] except db_exc.DBDuplicateEntry: # NOTE(cby): default fwg created concurrently LOG.debug("Default FWG was concurrently created") return self._get_default_fwg_id(context, tenant_id) def _create_firewall_group(self, context, firewall_group, status=None, default_fwg=False): """Create a firewall group If default_fwg is True then a default firewall group is being created for a given tenant. """ fwg = firewall_group['firewall_group'] tenant_id = fwg['tenant_id'] if not status: status = (nl_constants.CREATED if cfg.CONF.router_distributed else nl_constants.PENDING_CREATE) if default_fwg: # A default firewall group is being created. default_fwg_id = self._get_default_fwg_id(context, tenant_id) if default_fwg_id is not None: # Default fwg for a given tenant exists, fetch it and return return self.get_firewall_group(default_fwg_id) else: # An ordinary firewall group is being created BUT let's make sure # that a default firewall group for given tenant exists self._ensure_default_firewall_group(context, tenant_id) self._validate_tenant_for_fwg_policies(context, fwg, tenant_id) with context.session.begin(subtransactions=True): fwg_db = FirewallGroup( id=uuidutils.generate_uuid(), tenant_id=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 create_firewall_group(self, context, firewall_group, status=None): self._ensure_not_default_resource(firewall_group, 'firewall_group') return self._create_firewall_group(context, firewall_group, status) def update_firewall_group(self, context, id, firewall_group): LOG.debug("update_firewall() called") fwg = firewall_group['firewall_group'] # make sure that no group can be updated to have name=default self._ensure_not_default_resource(firewall_group, 'firewall_group') with context.session.begin(subtransactions=True): fwg_db = self.get_firewall_group(context, id) if _is_default(fwg_db): attrs = [ 'name', 'description', 'admin_state_up', 'ingress_firewall_policy_id', 'egress_firewall_policy_id' ] if context.is_admin: attrs = ['name'] for attr in attrs: if attr in fwg: raise FirewallDefaultObjectUpdateRestricted( resource_type='Firewall Group', resource_id=fwg_db['id']) self._validate_tenant_for_fwg_policies(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'] # If fwg is empty, skip updating if fwg: 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): # Note: Plugin should ensure that it's okay to delete if the # firewall is active LOG.debug("delete_firewall_group() called") with context.session.begin(subtransactions=True): # if no such group exists -> don't raise an exception according to # 80fe2ba1, return None try: fwg_db = self._get_firewall_group(context, id) except f_exc.FirewallGroupNotFound: return if _is_default(fwg_db): if context.is_admin: # Like Rules in Default SG, when the Default FWG is deleted # its associated Rules and policies would also be deleted. # Delete fwg first and then associated policies context.session.query( FirewallGroup).filter_by(id=id).delete() fwp = [fwg_db['ingress_firewall_policy_id'], fwg_db['egress_firewall_policy_id']] for fwp_id in fwp: self.delete_firewall_policy(context, fwp_id) else: # only admin can delete default fwg raise f_exc.FirewallGroupCannotRemoveDefault() else: context.session.query( FirewallGroup).filter_by(id=id).delete() def _ensure_not_default_resource(self, resource_dict, r_type, action=None): """Checks that a resource is not default by checking its name A resource_dict can be either a dictionary in form {r_type : {}} or a serialized object from db. Action is used to determine type of exception to be raised. """ resource = resource_dict.get(r_type) or resource_dict if r_type == 'firewall_group': if resource.get('name', '') == const.DEFAULT_FWG: if action == "update": raise FirewallDefaultObjectUpdateRestricted( resource_type='Firewall Group', resource_id=resource['id']) raise FirewallDefaultParameterExists( resource_type='Firewall Group', name=resource['name']) elif r_type == 'firewall_policy': if resource.get('name', '') in [const.DEFAULT_FWP_INGRESS, const.DEFAULT_FWP_EGRESS]: if action == "update": raise FirewallDefaultObjectUpdateRestricted( resource_type='Firewall Group', resource_id=resource['id']) raise FirewallDefaultParameterExists( resource_type='Firewall Policy', name=resource['name']) 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") if context.tenant_id: tenant_id = filters.get('tenant_id') if filters else None tenant_id = tenant_id[0] if tenant_id else context.tenant_id self._ensure_default_firewall_group(context, tenant_id) return self._get_collection(context, FirewallGroup, self._make_firewall_group_dict, filters=filters, fields=fields) def get_firewall_group_for_port(self, context, port_id): """Get firewall group is associated with a port :param context: context object :param port_id: Port ID. """ filters = {'port_id': [port_id]} fwg_port_binding = self._get_collection_query( context, FirewallGroupPortAssociation, filters=filters).first() if fwg_port_binding: fwg_id = fwg_port_binding['firewall_group_id'] return self._make_firewall_group_dict_with_rules(context, fwg_id) def _get_default_fwg(self, context, project_id): query = self._model_query(context, DefaultFirewallGroup) def_fwg_id = query.filter_by( project_id=project_id).one().firewall_group_id return self.get_firewall_group(context, def_fwg_id) def set_port_for_default_firewall_group(self, context, port_id, project_id): """Set a port into default firewall group :param context: Context object :param port_id: Port ID :param project_id: ProjectID """ with context.session.begin(subtransactions=True): def_fwg_db = self._get_default_fwg(context, project_id) try: self._set_ports_for_firewall_group( context, def_fwg_db, {'ports': [port_id]}) except f_exc.FirewallGroupPortInUse: LOG.warning("Port %s has been already associated with default " "firewall group %s and skip association", port_id, def_fwg_db['id']) else: # Update default fwg status to PENDING_UPDATE to wait updating # from agent self.update_firewall_group_status( context, def_fwg_db['id'], nl_constants.PENDING_UPDATE) def _is_default(fwg_db): return fwg_db['name'] == const.DEFAULT_FWG neutron-fwaas-12.0.2/neutron_fwaas/db/__init__.py0000664000175000017500000000000013555577713021770 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/db/migration/0000775000175000017500000000000013555600005021637 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/db/migration/__init__.py0000664000175000017500000000000013555577713023761 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/0000775000175000017500000000000013555600005025467 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/script.py.mako0000664000175000017500000000203513555577713030316 0ustar zuulzuul00000000000000# Copyright ${create_date.year} # # 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"} neutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/0000775000175000017500000000000013555600005027337 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/pike/0000775000175000017500000000000013555600005030267 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/pike/contract/0000775000175000017500000000000013555600005032104 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000022200000000000011211 Lustar 00000000000000neutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/pike/contract/fd38cd995cc0_shared_attribute_for_firewall_resources.pyneutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/pike/contract/fd38cd995c0000664000175000017500000000240613555577713033541 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """change shared attribute for firewall resource Revision ID: fd38cd995cc0 Revises: f83a0b2964d0 Create Date: 2017-03-31 14:22:21.063392 """ from alembic import op import sqlalchemy as sa # revision identifiers, used by Alembic. revision = 'fd38cd995cc0' down_revision = 'f83a0b2964d0' depends_on = ('d6a12e637e28',) def upgrade(): op.alter_column('firewall_rules_v2', 'public', new_column_name='shared', existing_type=sa.Boolean) op.alter_column('firewall_groups_v2', 'public', new_column_name='shared', existing_type=sa.Boolean) op.alter_column('firewall_policies_v2', 'public', new_column_name='shared', existing_type=sa.Boolean) neutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/liberty/0000775000175000017500000000000013555600005031011 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/liberty/contract/0000775000175000017500000000000013555600005032626 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000016400000000000011216 Lustar 00000000000000neutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/liberty/contract/67c8e8d61d5_initial.pyneutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/liberty/contract/67c8e8d0000664000175000017500000000203113555577713033660 0ustar zuulzuul00000000000000# 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 neutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/liberty/expand/0000775000175000017500000000000013555600005032270 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000016200000000000011214 Lustar 00000000000000neutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/liberty/expand/c40fbb377ad_initial.pyneutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/liberty/expand/c40fbb3770000664000175000017500000000163313555577713033542 0ustar zuulzuul00000000000000# 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 ././@LongLink0000000000000000000000000000017300000000000011216 Lustar 00000000000000neutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/liberty/expand/4b47ea298795_add_reject_rule.pyneutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/liberty/expand/4b47ea2980000664000175000017500000000277213555577713033477 0ustar zuulzuul00000000000000# 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) ././@LongLink0000000000000000000000000000016300000000000011215 Lustar 00000000000000neutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/540142f314f4_fwaas_router_insertion.pyneutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/540142f314f4_fwaas_route0000664000175000017500000000403213555577713033272 0ustar zuulzuul00000000000000# Copyright 2014 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """FWaaS router insertion Revision ID: 540142f314f4 Revises: 4202e3047e47 Create Date: 2015-02-06 17:02:24.279337 """ from alembic import op import sqlalchemy as sa from sqlalchemy.engine import reflection # revision identifiers, used by Alembic. revision = '540142f314f4' down_revision = '4202e3047e47' SQL_STATEMENT = ( "insert into firewall_router_associations " "select " "f.id as fw_id, r.id as router_id " "from firewalls f, routers r " "where " "f.tenant_id=r.%s" ) def upgrade(): op.create_table('firewall_router_associations', sa.Column('fw_id', sa.String(length=36), nullable=False), sa.Column('router_id', sa.String(length=36), nullable=False), sa.ForeignKeyConstraint(['fw_id'], ['firewalls.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['router_id'], ['routers.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('fw_id', 'router_id'), ) # Depending on when neutron-fwaas is installed with neutron, this script # may be run before or after the neutron core tables have had their # tenant_id columns renamed to project_id. Account for both scenarios. bind = op.get_bind() insp = reflection.Inspector.from_engine(bind) columns = insp.get_columns('routers') if 'tenant_id' in [c['name'] for c in columns]: op.execute(SQL_STATEMENT % 'tenant_id') else: op.execute(SQL_STATEMENT % 'project_id') neutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/queens/0000775000175000017500000000000013555600005030637 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/queens/expand/0000775000175000017500000000000013555600005032116 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000022100000000000011210 Lustar 00000000000000neutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/queens/expand/f24e0d5e5bff_uniq_firewallgroupportassociation0port.pyneutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/queens/expand/f24e0d5e5b0000664000175000017500000000455313555577713033540 0ustar zuulzuul00000000000000# Copyright 2017 Fujitsu Limited # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """uniq_firewallgroupportassociation0port Revision ID: f24e0d5e5bff Revises: 876782258a43 Create Date: 2017-11-08 15:55:40.990272 """ from alembic import op from neutron_lib import exceptions import sqlalchemy as sa from neutron._i18n import _ # revision identifiers, used by Alembic. revision = 'f24e0d5e5bff' down_revision = '876782258a43' fwg_port_association = sa.Table( 'firewall_group_port_associations_v2', sa.MetaData(), sa.Column('firewall_group_id', sa.String(36)), sa.Column('port_id', sa.String(36))) class DuplicatePortRecordinFirewallGroupPortAssociation(exceptions.Conflict): message = _("Duplicate port(s) %(port_id)s records exist in" "firewall_group_port_associations_v2 table. Database cannot" "be upgraded. Please remove all duplicated records before" "upgrading the database.") def upgrade(): op.create_unique_constraint( 'uniq_firewallgroupportassociation0port_id', 'firewall_group_port_associations_v2', ['port_id']) def check_sanity(connection): duplicated_port_ids = ( get_duplicate_port_records_in_fwg_port_association(connection)) if duplicated_port_ids: raise DuplicatePortRecordinFirewallGroupPortAssociation( port_id=",".join(duplicated_port_ids)) def get_duplicate_port_records_in_fwg_port_association(connection): insp = sa.engine.reflection.Inspector.from_engine(connection) if 'firewall_group_port_associations_v2' not in insp.get_table_names(): return [] session = sa.orm.Session(bind=connection.connect()) query = (session.query(fwg_port_association.c.port_id) .group_by(fwg_port_association.c.port_id) .having(sa.func.count() > 1)).all() return [q[0] for q in query] ././@LongLink0000000000000000000000000000021700000000000011215 Lustar 00000000000000neutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/queens/expand/876782258a43_create_default_firewall_groups_table.pyneutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/queens/expand/876782258a0000664000175000017500000000457513555577713033264 0ustar zuulzuul00000000000000# Copyright 2017 FUJITSU LIMITED # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """create_default_firewall_groups_table Revision ID: 876782258a43 Revises: d6a12e637e28 Create Date: 2017-01-26 23:47:42.795504 """ from alembic import op from neutron_lib.db import constants as db_constants from neutron_lib import exceptions import sqlalchemy as sa from neutron_fwaas._i18n import _ from neutron_fwaas.common import fwaas_constants as const from neutron_fwaas.common import resources # revision identifiers, used by Alembic. revision = '876782258a43' down_revision = 'd6a12e637e28' class DuplicateDefaultFirewallGroup(exceptions.Conflict): message = _("Duplicate Firewall group found named '%s'. " "Database cannot be upgraded. Please, remove all duplicates " "before upgrading the database.") % const.DEFAULT_FWG def upgrade(): op.create_table( 'default_firewall_groups', sa.Column('project_id', sa.String(length=db_constants.PROJECT_ID_FIELD_SIZE), nullable=False), sa.Column('firewall_group_id', sa.String(length=db_constants.UUID_FIELD_SIZE), nullable=False), sa.PrimaryKeyConstraint('project_id'), sa.ForeignKeyConstraint(['firewall_group_id'], ['firewall_groups_v2.id'], ondelete="CASCADE")) def check_sanity(connection): # check for already existing firewall groups with name == DEFAULT_FWG insp = sa.engine.reflection.Inspector.from_engine(connection) if 'firewall_groups_v2' not in insp.get_table_names(): return [] session = sa.orm.Session(bind=connection.connect()) default_fwg = session.query(resources.FIREWALL_GROUP.name).filter( resources.FIREWALL_GROUP.name == const.DEFAULT_FWG).first() if default_fwg: raise DuplicateDefaultFirewallGroup() ././@LongLink0000000000000000000000000000016000000000000011212 Lustar 00000000000000neutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/4202e3047e47_add_index_tenant_id.pyneutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/4202e3047e47_add_index_t0000664000175000017500000000210213555577713033214 0ustar zuulzuul00000000000000# Copyright 2015 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """add_index_tenant_id Revision ID: 4202e3047e47 Revises: start_neutron_fwaas Create Date: 2015-02-10 17:17:47.846764 """ from alembic import op # revision identifiers, used by Alembic. revision = '4202e3047e47' down_revision = 'start_neutron_fwaas' TABLES = ['firewall_rules', 'firewalls', 'firewall_policies'] def upgrade(): for table in TABLES: op.create_index(op.f('ix_%s_tenant_id' % table), table, ['tenant_id'], unique=False) ././@LongLink0000000000000000000000000000015300000000000011214 Lustar 00000000000000neutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/796c68dffbb_cisco_csr_fwaas.pyneutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/796c68dffbb_cisco_csr_fw0000664000175000017500000000301113555577713033655 0ustar zuulzuul00000000000000# Copyright 2015 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """cisco_csr_fwaas Revision ID: 796c68dffbb Revises: 540142f314f4 Create Date: 2015-02-02 13:11:55.184112 """ from alembic import op import sqlalchemy as sa # revision identifiers, used by Alembic. revision = '796c68dffbb' down_revision = '540142f314f4' def upgrade(active_plugins=None, options=None): op.create_table('cisco_firewall_associations', sa.Column('fw_id', sa.String(length=36), nullable=False), sa.Column('port_id', sa.String(length=36), nullable=True), sa.Column('direction', sa.String(length=16), nullable=True), sa.Column('acl_id', sa.String(length=36), nullable=True), sa.Column('router_id', sa.String(length=36), nullable=True), sa.ForeignKeyConstraint(['fw_id'], ['firewalls.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['port_id'], ['ports.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('fw_id') ) neutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/EXPAND_HEAD0000664000175000017500000000001513555577713031041 0ustar zuulzuul00000000000000f24e0d5e5bff neutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/CONTRACT_HEAD0000664000175000017500000000001513555577713031277 0ustar zuulzuul00000000000000fd38cd995cc0 neutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/start_neutron_fwaas.py0000664000175000017500000000153313555577713034026 0ustar zuulzuul00000000000000# 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 neutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/newton/0000775000175000017500000000000013555600005030651 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/newton/contract/0000775000175000017500000000000013555600005032466 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000020500000000000011212 Lustar 00000000000000neutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/newton/contract/f83a0b2964d0_rename_tenant_to_project.pyneutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/newton/contract/f83a0b290000664000175000017500000000652413555577713033601 0ustar zuulzuul00000000000000# Copyright 2016 # # 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() } neutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/newton/expand/0000775000175000017500000000000013555600005032130 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000017500000000000011220 Lustar 00000000000000neutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/newton/expand/d6a12e637e28_neutron_fwaas_v2_0.pyneutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/newton/expand/d6a12e637e0000664000175000017500000001054113555577713033466 0ustar zuulzuul00000000000000# 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)) neutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/kilo_release.py0000664000175000017500000000151213555577713032371 0ustar zuulzuul00000000000000# 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 neutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/mitaka/0000775000175000017500000000000013555600005030605 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/mitaka/contract/0000775000175000017500000000000013555600005032422 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000017200000000000011215 Lustar 00000000000000neutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/mitaka/contract/458aa42b14b_fw_table_alter.pyneutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/versions/mitaka/contract/458aa42b0000664000175000017500000000256213555577713033527 0ustar zuulzuul00000000000000#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 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) neutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/env.py0000664000175000017500000000466113555577713026663 0ustar zuulzuul00000000000000# 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() neutron-fwaas-12.0.2/neutron_fwaas/db/migration/alembic_migrations/README0000664000175000017500000000004613555577713026372 0ustar zuulzuul00000000000000Generic single-database configuration.neutron-fwaas-12.0.2/neutron_fwaas/common/0000775000175000017500000000000013555600005020551 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/common/exceptions.py0000664000175000017500000000171613555577713023334 0ustar zuulzuul00000000000000# Copyright 2018 Fujitsu Limited. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import exceptions as n_exc from neutron_fwaas._i18n import _ # TODO(annp): migrate to neutron-lib after Queen release class FirewallGroupPortNotSupported(n_exc.Conflict): message = _("Port %(port_id)s is not supported by firewall L2 driver. " "This may happen due to incompatible driver combination.") neutron-fwaas-12.0.2/neutron_fwaas/common/fwaas_constants.py0000664000175000017500000000223513555577713024345 0ustar zuulzuul00000000000000# Copyright 2015 Cisco Systems, Inc # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. FIREWALL = 'FIREWALL' FIREWALL_V2 = 'FIREWALL_V2' # Constants for "topics" FIREWALL_PLUGIN = 'q-firewall-plugin' FW_AGENT = 'firewall_agent' FIREWALL_RULE_LIST = 'firewall_rule_list' # V2 Constants DEFAULT_FWG = 'default' DEFAULT_FWP_INGRESS = 'default ingress' DEFAULT_FWP_EGRESS = 'default egress' # Firewall group events for agent-side DELETE_FWG = 'delete_firewall_group' UPDATE_FWG = 'update_firewall_group' CREATE_FWG = 'create_firewall_group' # Port events for L2 agent extension HANDLE_PORT = 'handle_port' DELETE_PORT = 'delete_port' neutron-fwaas-12.0.2/neutron_fwaas/common/__init__.py0000664000175000017500000000000013555577713022673 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/neutron_fwaas/common/resources.py0000664000175000017500000000140213555577713023155 0ustar zuulzuul00000000000000# 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 neutron-fwaas-12.0.2/neutron_fwaas/_i18n.py0000664000175000017500000000203713555577713020576 0ustar zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import oslo_i18n DOMAIN = "neutron_fwaas" _translators = oslo_i18n.TranslatorFactory(domain=DOMAIN) # The primary translation function using the well-known name "_" _ = _translators.primary # The contextual translation function using the name "_C" _C = _translators.contextual_form # The plural translation function using the name "_P" _P = _translators.plural_form def get_available_languages(): return oslo_i18n.get_available_languages(DOMAIN) neutron-fwaas-12.0.2/.pylintrc0000664000175000017500000000664313555577713016307 0ustar zuulzuul00000000000000# The format of this file isn't really documented; just use --generate-rcfile [MASTER] # Add 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 neutron-fwaas-12.0.2/etc/0000775000175000017500000000000013555600005015161 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/etc/README.txt0000664000175000017500000000047513555577713016710 0ustar zuulzuul00000000000000To 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 neutron-fwaas-12.0.2/etc/oslo-config-generator/0000775000175000017500000000000013555600005021364 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/etc/oslo-config-generator/fwaas_driver.ini0000664000175000017500000000014013555577713024557 0ustar zuulzuul00000000000000[DEFAULT] output_file = etc/fwaas_driver.ini.sample wrap_width = 79 namespace = firewall.agent neutron-fwaas-12.0.2/etc/oslo-config-generator/neutron_fwaas.conf0000664000175000017500000000014213555577713025126 0ustar zuulzuul00000000000000[DEFAULT] output_file = etc/neutron_fwaas.conf.sample wrap_width = 79 namespace = neutron.fwaas neutron-fwaas-12.0.2/etc/neutron/0000775000175000017500000000000013555600005016653 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/etc/neutron/rootwrap.d/0000775000175000017500000000000013555600005020752 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/etc/neutron/rootwrap.d/fwaas-privsep.filters0000664000175000017500000000041213555577713025153 0ustar zuulzuul00000000000000# 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 neutron-fwaas-12.0.2/etc/neutron/policy.d/0000775000175000017500000000000013555600005020374 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/etc/neutron/policy.d/neutron-fwaas.json0000664000175000017500000000367013555577713024111 0ustar zuulzuul00000000000000{ "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" } neutron-fwaas-12.0.2/.testr.conf0000664000175000017500000000037213555577713016521 0ustar zuulzuul00000000000000[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 neutron-fwaas-12.0.2/releasenotes/0000775000175000017500000000000013555600005017077 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/releasenotes/source/0000775000175000017500000000000013555600005020377 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/releasenotes/source/_templates/0000775000175000017500000000000013555600005022534 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/releasenotes/source/_templates/.placeholder0000664000175000017500000000000013555577713025030 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/releasenotes/source/conf.py0000664000175000017500000002177313555577713021733 0ustar zuulzuul00000000000000# -*- coding: utf-8 -*- # 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 Release Notes documentation build configuration file, created # by # sphinx-quickstart on Tue Nov 3 17:40:50 2015. # # This file is execfile()d with the current directory set to its # 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. # 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. # sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'openstackdocstheme', 'reno.sphinxext', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'Neutron FWaaS Release Notes' copyright = u'2015, Neutron FWaaS Developers' # Release notes are version independent. # The full version, including alpha/beta/rc tags. release = '' # The short X.Y version. version = '' # 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 patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used 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 = False # 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 = [] # If true, keep warnings as "system message" paragraphs in the built documents. # keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. 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 = [] # The name for this set of Sphinx documents. If None, it defaults to # " v 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'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. # html_extra_path = [] # 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_domain_indices = 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, "Created using Sphinx" is shown in the HTML footer. Default is True. # html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'NeutronFWaaSReleaseNotesdoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # 'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'NeutronFWaaSReleaseNotes.tex', u'Neutron FWaaS Release Notes Documentation', u'Neutron FWaaS Developers', '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 # If true, show page references after internal links. # latex_show_pagerefs = False # If true, show URL addresses after external links. # latex_show_urls = False # Documents to append as an appendix to all manuals. # latex_appendices = [] # If false, no module index is generated. # latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'neutronfwaasreleasenotes', u'Neutron FWaaS Release Notes ' 'Documentation', [u'Neutron FWaaS Developers'], 1) ] # If true, show URL addresses after external links. # man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'NeutronFWaaSReleaseNotes', u'Neutron FWaaS Release Notes ' 'Documentation', u'Neutron FWaaS Developers', 'NeutronFWaaSReleaseNotes', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. # texinfo_appendices = [] # If false, no module index is generated. # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. # texinfo_no_detailmenu = False # -- Options for Internationalization output ------------------------------ locale_dirs = ['locale/'] # -- Options for openstackdocstheme ------------------------------------------- repository_name = 'openstack/neutron-fwaas' bug_project = 'neutron' bug_tag = 'doc' neutron-fwaas-12.0.2/releasenotes/source/locale/0000775000175000017500000000000013555600005021636 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/releasenotes/source/locale/en_GB/0000775000175000017500000000000013555600005022610 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/releasenotes/source/locale/en_GB/LC_MESSAGES/0000775000175000017500000000000013555600005024375 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po0000664000175000017500000002414513555577713027457 0ustar zuulzuul00000000000000# Andi Chandler , 2017. #zanata msgid "" msgstr "" "Project-Id-Version: Neutron FWaaS Release Notes\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-12-07 05:25+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2017-12-12 08:38+0000\n" "Last-Translator: Andi Chandler \n" "Language-Team: English (United Kingdom)\n" "Language: en-GB\n" "X-Generator: Zanata 3.9.6\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" msgid "11.0.0" msgstr "11.0.0" msgid "12.0.0.0b1" msgstr "12.0.0.0b1" msgid "12.0.0.0b1-12" msgstr "12.0.0.0b1-12" msgid "7.0.2" msgstr "7.0.2" msgid "7.1.1" msgstr "7.1.1" msgid "8.0.0" msgstr "8.0.0" msgid "9.0.0" msgstr "9.0.0" msgid "9.0.0.0b2" msgstr "9.0.0.0b2" msgid "9.0.0.0b3" msgstr "9.0.0.0b3" msgid "9.0.0.0rc1" msgstr "9.0.0.0rc1" msgid "And the Security Group driver is specified as:" msgstr "And the Security Group driver is specified as:" msgid "Bug Fixes" msgstr "Bug Fixes" msgid "Current Series Release Notes" msgstr "Current Series Release Notes" msgid "Currently, the FWaaSv2 L2 driver can be configured as:" msgstr "Currently, the FWaaSv2 L2 driver can be configured as:" msgid "" "Earlier the FWaaS agent integrated with the L3 agent by having the L3 Agent " "class inherit from the FWaaS Agent class. This meant that other service " "agents could not also integrate with the L3 agent. Now, using the L3 agent " "extensions mechanism, FWaaS (v1 and v2) plugs in to the L3 agent. This " "means that it can interoperate peacefully with other L3 advanced services " "that also implement the L3 agent extension mechanism, all without any code " "changes to Neutron." msgstr "" "Earlier the FWaaS agent integrated with the L3 agent by having the L3 Agent " "class inherit from the FWaaS Agent class. This meant that other service " "agents could not also integrate with the L3 agent. Now, using the L3 agent " "extensions mechanism, FWaaS (v1 and v2) plugs in to the L3 agent. This " "means that it can interoperate peacefully with other L3 advanced services " "that also implement the L3 agent extension mechanism, all without any code " "changes to Neutron." msgid "Enable quotas for FWaaS." msgstr "Enable quotas for FWaaS." msgid "" "FWaaS v1 can not be enabled at the same time as FWaaS v2; one or the other " "must be chosen." msgstr "" "FWaaS v1 can not be enabled at the same time as FWaaS v2; one or the other " "must be chosen." msgid "Generation of sample Neutron FWaaS configuration files." msgstr "Generation of sample Neutron FWaaS configuration files." msgid "" "If both are configured, the packet will still only hit the FWaaS table in " "OVS and will not traverse the rules in the SG table. There are some fixes " "needed to support this model which are being tested and will be merged " "shortly. Currently there are no checks to allow only one of FWaaS L2 or SG " "to be configured." msgstr "" "If both are configured, the packet will still only hit the FWaaS table in " "OVS and will not traverse the rules in the SG table. There are some fixes " "needed to support this model which are being tested and will be merged " "shortly. Currently there are no checks to allow only one of FWaaS L2 or SG " "to be configured." msgid "" "In FWaaS v2 firewall policies are applied to router ports, as opposed to " "applying to routers in FWaaS v1." msgstr "" "In FWaaS v2 firewall policies are applied to router ports, as opposed to " "applying to routers in FWaaS v1." msgid "Known Issues" msgstr "Known Issues" msgid "Liberty Series Release Notes" msgstr "Liberty Series Release Notes" msgid "Mitaka Series Release Notes" msgstr "Mitaka Series Release Notes" msgid "Neutron FWaaS Release Notes" msgstr "Neutron FWaaS Release Notes" msgid "" "Neutron FWaaS no longer includes static example configuration files. " "Instead, use tools/generate_config_file_samples.sh to generate them. The " "files are generated with a .sample extension." msgstr "" "Neutron FWaaS no longer includes static example configuration files. " "Instead, use tools/generate_config_file_samples.sh to generate them. The " "files are generated with a .sample extension." msgid "" "Neutron Firewall as a Service can be configured by the users with the newly " "introduced fwaas configuration file." msgstr "" "Neutron Firewall as a Service can be configured by the users with the newly " "introduced FWaaS configuration file." msgid "New Features" msgstr "New Features" msgid "Newton Series Release Notes" msgstr "Newton Series Release Notes" msgid "Ocata Series Release Notes" msgstr "Ocata Series Release Notes" msgid "" "Operators that increase the default limit for quota_routers from 10 may want " "to bump FWaaS quotas as well, since with router insertion a tenant can " "potentially have a unique policy and firewall for each router." msgstr "" "Operators that increase the default limit for quota_routers from 10 may want " "to bump FWaaS quotas as well, since with router insertion a tenant can " "potentially have a unique policy and firewall for each router." msgid "Other Notes" msgstr "Other Notes" msgid "Pike Series Release Notes" msgstr "Pike Series Release Notes" msgid "Prelude" msgstr "Prelude" msgid "Start using reno to manage release notes." msgstr "Start using Reno to manage release notes." msgid "" "Tenants may receive a 409 Conflict error with a message body containing a " "quota exceeded message during resource creation if their quota is exceeded." msgstr "" "Tenants may receive a 409 Conflict error with a message body containing a " "quota exceeded message during resource creation if their quota is exceeded." msgid "" "The Cisco FWaaS driver will not be available from the neutron-fwaas repo in " "Newton. For the Cisco FWaaS driver, refer to the openstack/networking-cisco " "repo." msgstr "" "The Cisco FWaaS driver will not be available from the neutron-fwaas repo in " "Newton. For the Cisco FWaaS driver, refer to the openstack/networking-cisco " "repo." msgid "" "The Cisco Firewall Driver is being moved from the FWaaS repo to the Cisco " "specific repo: https://github.com/openstack/networking-cisco" msgstr "" "The Cisco Firewall Driver is being moved from the FWaaS repo to the Cisco " "specific repo: https://github.com/openstack/networking-cisco" msgid "" "The FWaaS extension can register quotas. The default values for " "quota_firewall, quota_firewall_policy, and quota_firewall_rule are set to -1 " "(unlimited)." msgstr "" "The FWaaS extension can register quotas. The default values for " "quota_firewall, quota_firewall_policy, and quota_firewall_rule are set to -1 " "(unlimited)." msgid "" "The FWaaS extension will register quotas. The default values for " "quota_firewall and quota_firewall_policy are set to 10. The default value " "for quota_firewall_rule is set to 100. Quotas can be adjusted in the conf " "files, including -1 values to allow unlimited." msgstr "" "The FWaaS extension will register quotas. The default values for " "quota_firewall and quota_firewall_policy are set to 10. The default value " "for quota_firewall_rule is set to 100. Quotas can be adjusted in the conf " "files, including -1 values to allow unlimited." msgid "" "The FWaaS team is pleased to release FWaaS v2.0. This release of FWaaS " "supports either the original FWaaS v1 or the new FWaaS v2." msgstr "" "The FWaaS team is pleased to release FWaaS v2.0. This release of FWaaS " "supports either the original FWaaS v1 or the new FWaaS v2." msgid "" "The McAfee Firewall Driver is being removed from the FwaaS repo, due to lack " "of active maintainers." msgstr "" "The McAfee Firewall Driver is being removed from the FWaaS repo, due to lack " "of active maintainers." msgid "" "The McAfee Firewall Driver will not be available for use in the Newton " "release." msgstr "" "The McAfee Firewall Driver will not be available for use in the Newton " "release." msgid "" "The vArmour Firewall Driver is being removed from the FwaaS repo, as per " "decision to remove vendor drivers from the community repo." msgstr "" "The vArmour Firewall Driver is being removed from the FWaaS repo, as per " "decision to remove vendor drivers from the community repo." msgid "" "The vArmour Firewall Driver will not be available for use in the Newton " "release." msgstr "" "The vArmour Firewall Driver will not be available for use in the Newton " "release." msgid "The vyatta Firewall Driver is being removed from the FwaaS repo," msgstr "The Vyatta Firewall Driver is being removed from the FWaaS repo," msgid "" "The vyatta Firewall Driver will not be available for use in the Newton " "release from the community repo." msgstr "" "The Vyatta Firewall Driver will not be available for use in the Newton " "release from the community repo." msgid "" "There is not currently a defined upgrade path from FWaaS v1 to FWaaS v2." msgstr "" "There is not currently a defined upgrade path from FWaaS v1 to FWaaS v2." msgid "Upgrade Notes" msgstr "Upgrade Notes" msgid "" "[`bug 1702242 `__] Port " "range specification of a firewall rule now works expectedly with the " "reference L3 agent based implementation. Previously, when creating a " "firewall rule with port range like ``8778:9000``, the rule was not deleted " "correctly and only entries associated with the first port number were clean " "up. Note that this bug is only applied to the reference L3 agent based " "implementation." msgstr "" "[`bug 1702242 `__] Port " "range specification of a firewall rule now works expectedly with the " "reference L3 agent based implementation. Previously, when creating a " "firewall rule with port range like ``8778:9000``, the rule was not deleted " "correctly and only entries associated with the first port number were clean " "up. Note that this bug is only applied to the reference L3 agent based " "implementation." msgid "``firewall_driver = openvswitch``" msgstr "``firewall_driver = openvswitch``" msgid "``firewall_driver = ovs``" msgstr "``firewall_driver = ovs``" msgid "as per decision to remove vendor drivers from the community repo." msgstr "as per decision to remove vendor drivers from the community repo." neutron-fwaas-12.0.2/releasenotes/source/locale/fr/0000775000175000017500000000000013555600005022245 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/releasenotes/source/locale/fr/LC_MESSAGES/0000775000175000017500000000000013555600005024032 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/releasenotes/source/locale/fr/LC_MESSAGES/releasenotes.po0000664000175000017500000000267313555577713027116 0ustar zuulzuul00000000000000# Gérald LONLAS , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: Neutron FWaaS Release Notes 11.0.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-08-16 20:31+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-10-22 05:48+0000\n" "Last-Translator: Gérald LONLAS \n" "Language-Team: French\n" "Language: fr\n" "X-Generator: Zanata 3.9.6\n" "Plural-Forms: nplurals=2; plural=(n > 1)\n" msgid "7.0.2" msgstr "7.0.2" msgid "7.1.1" msgstr "7.1.1" msgid "8.0.0" msgstr "8.0.0" msgid "9.0.0" msgstr "9.0.0" msgid "9.0.0.0b2" msgstr "9.0.0.0b2" msgid "9.0.0.0b3" msgstr "9.0.0.0b3" msgid "9.0.0.0rc1" msgstr "9.0.0.0rc1" msgid "Current Series Release Notes" msgstr "Note de la release actuelle" msgid "Known Issues" msgstr "Problèmes connus" msgid "Liberty Series Release Notes" msgstr "Note de release pour Liberty" msgid "Mitaka Series Release Notes" msgstr "Note de release pour Mitaka" msgid "Neutron FWaaS Release Notes" msgstr "Note de release de Neutron FWaaS" msgid "New Features" msgstr "Nouvelles fonctionnalités" msgid "Newton Series Release Notes" msgstr "Note de release pour Newton" msgid "Other Notes" msgstr "Autres notes" msgid "Start using reno to manage release notes." msgstr "Commence à utiliser reno pour la gestion des notes de release" msgid "Upgrade Notes" msgstr "Notes de mises à jours" neutron-fwaas-12.0.2/releasenotes/source/pike.rst0000664000175000017500000000021713555577713022104 0ustar zuulzuul00000000000000=================================== Pike Series Release Notes =================================== .. release-notes:: :branch: stable/pike neutron-fwaas-12.0.2/releasenotes/source/ocata.rst0000664000175000017500000000023013555577713022236 0ustar zuulzuul00000000000000=================================== Ocata Series Release Notes =================================== .. release-notes:: :branch: origin/stable/ocata neutron-fwaas-12.0.2/releasenotes/source/_static/0000775000175000017500000000000013555600005022025 5ustar zuulzuul00000000000000neutron-fwaas-12.0.2/releasenotes/source/_static/.placeholder0000664000175000017500000000000013555577713024321 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/releasenotes/source/liberty.rst0000664000175000017500000000022213555577713022622 0ustar zuulzuul00000000000000============================== Liberty Series Release Notes ============================== .. release-notes:: :branch: origin/stable/liberty neutron-fwaas-12.0.2/releasenotes/source/newton.rst0000664000175000017500000000023213555577713022463 0ustar zuulzuul00000000000000=================================== Newton Series Release Notes =================================== .. release-notes:: :branch: origin/stable/newton neutron-fwaas-12.0.2/releasenotes/source/mitaka.rst0000664000175000017500000000023213555577713022417 0ustar zuulzuul00000000000000=================================== Mitaka Series Release Notes =================================== .. release-notes:: :branch: origin/stable/mitaka neutron-fwaas-12.0.2/releasenotes/source/index.rst0000664000175000017500000000026613555577713022267 0ustar zuulzuul00000000000000============================= Neutron FWaaS Release Notes ============================= .. toctree:: :maxdepth: 1 unreleased pike ocata newton mitaka liberty neutron-fwaas-12.0.2/releasenotes/source/unreleased.rst0000664000175000017500000000016013555577713023300 0ustar zuulzuul00000000000000============================== Current Series Release Notes ============================== .. release-notes:: neutron-fwaas-12.0.2/releasenotes/notes/0000775000175000017500000000000013555600005020227 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000014700000000000011217 Lustar 00000000000000neutron-fwaas-12.0.2/releasenotes/notes/adding-new-tables-for-future-consumption-ffd537c1f82e2e01.yamlneutron-fwaas-12.0.2/releasenotes/notes/adding-new-tables-for-future-consumption-ffd537c1f82e2e01.ya0000664000175000017500000000065513555577713033045 0ustar zuulzuul00000000000000--- prelude: > Adding new tables for future consumption. features: - | New tables ``ACCEPTED_EGRESS_TRAFFIC_TABLE=91`` and ``ACCEPTED_INGRESS_TRAFFIC_TABLE=92`` & ``DROPPED_TRAFFIC_TABLE=93`` are added to OVS based FWaaS L2 driver for future comsumption like logging service. fixes: - | The limitation related to logging for security group in case of co-existence between SG and FWG is also fixed.neutron-fwaas-12.0.2/releasenotes/notes/vyatta-fwaas-driver-removal-e38e6ecde5105084.yaml0000664000175000017500000000042213555577713030717 0ustar zuulzuul00000000000000--- prelude: > - The vyatta Firewall Driver is being removed from the FwaaS repo, as per decision to remove vendor drivers from the community repo. upgrade: - The vyatta Firewall Driver will not be available for use in the Newton release from the community repo. neutron-fwaas-12.0.2/releasenotes/notes/mcafee-fwaas-driver-removal-8915271e5d4288cf.yaml0000664000175000017500000000033413555577713030504 0ustar zuulzuul00000000000000--- prelude: > - The McAfee Firewall Driver is being removed from the FwaaS repo, due to lack of active maintainers. upgrade: - The McAfee Firewall Driver will not be available for use in the Newton release. neutron-fwaas-12.0.2/releasenotes/notes/fwaas-config-9c780ccfb0e7887f.yaml0000664000175000017500000000020513555577713026016 0ustar zuulzuul00000000000000--- features: - Neutron Firewall as a Service can be configured by the users with the newly introduced fwaas configuration file. neutron-fwaas-12.0.2/releasenotes/notes/coexistence-between-sg-and-fwg-1f77a755539a9463.yaml0000664000175000017500000000141213555577713031040 0ustar zuulzuul00000000000000--- prelude: > Coexistence between security group and firewall group. features: - L2 firewall group driver based OVS can work in coexistence mode. That means, if a port is associated with both firewall group and security group, then a packet must be allowed by both features. other: - If a port is associated with both firewall group & security group and there is a security group logging, which is enabled to collect ``DROP`` events for this port, then most of invalid packets will be dropped at firewall group for performance reason except first dropped packet, which is allowed by firewall group but not accepted by security group. So not every dropped packet will be logged (like in case of security group works in standalone mode). neutron-fwaas-12.0.2/releasenotes/notes/enable-quotas-a3d0a21743bb1985.yaml0000664000175000017500000000135213555577713026021 0ustar zuulzuul00000000000000--- prelude: > Enable quotas for FWaaS. features: - The FWaaS extension will register quotas. The default values for quota_firewall and quota_firewall_policy are set to 10. The default value for quota_firewall_rule is set to 100. Quotas can be adjusted in the conf files, including -1 values to allow unlimited. issues: - Tenants may receive a 409 Conflict error with a message body containing a quota exceeded message during resource creation if their quota is exceeded. other: - Operators that increase the default limit for quota_routers from 10 may want to bump FWaaS quotas as well, since with router insertion a tenant can potentially have a unique policy and firewall for each router. neutron-fwaas-12.0.2/releasenotes/notes/validation_if_port_is_supported-639d0df705eb67f9.yaml0000664000175000017500000000044313555577713032047 0ustar zuulzuul00000000000000--- prelude: > Validating if a port is supported by FWaaS V2 fixes: - | [`bug 1746855 `__] Now, FWaaS V2 will validate if a port is supported before adding it to a FWG. This helps to make sure FWaaS V2 API works as expected.neutron-fwaas-12.0.2/releasenotes/notes/.placeholder0000664000175000017500000000000013555577713022523 0ustar zuulzuul00000000000000neutron-fwaas-12.0.2/releasenotes/notes/bug-1746404-493a66faac333403.yaml0000664000175000017500000000067713555577713024701 0ustar zuulzuul00000000000000--- prelude: > Taking security for VM instance into consideration, we've removed an option to disable automatic association with default firewall group feature. Therefore, `auto_associate_default_firewall_group` has been removed. fixes: - | There is no validation to check if an updated port is for VM or not so far. After this fix, default firewall group association is called only for VM ports which are newly created. neutron-fwaas-12.0.2/releasenotes/notes/cisco-fwaas-driver-move-8f46325d13c93543.yaml0000664000175000017500000000053313555577713027575 0ustar zuulzuul00000000000000--- prelude: > The Cisco Firewall Driver is being moved from the FWaaS repo to the Cisco specific repo: https://github.com/openstack/networking-cisco upgrade: - The Cisco FWaaS driver will not be available from the neutron-fwaas repo in Newton. For the Cisco FWaaS driver, refer to the openstack/networking-cisco repo. neutron-fwaas-12.0.2/releasenotes/notes/bug-1702242-c917c832ac2fa4e1.yaml0000664000175000017500000000076213555577713025027 0ustar zuulzuul00000000000000--- fixes: - | [`bug 1702242 `__] Port range specification of a firewall rule now works expectedly with the reference L3 agent based implementation. Previously, when creating a firewall rule with port range like ``8778:9000``, the rule was not deleted correctly and only entries associated with the first port number were clean up. Note that this bug is only applied to the reference L3 agent based implementation. neutron-fwaas-12.0.2/releasenotes/notes/config-file-generation-265c5256668a26bf.yaml0000664000175000017500000000043513555577713027537 0ustar zuulzuul00000000000000--- prelude: > Generation of sample Neutron FWaaS configuration files. features: - Neutron FWaaS no longer includes static example configuration files. Instead, use tools/generate_config_file_samples.sh to generate them. The files are generated with a .sample extension. ././@LongLink0000000000000000000000000000014600000000000011216 Lustar 00000000000000neutron-fwaas-12.0.2/releasenotes/notes/auto-association-default-firewall-group-7e9faf1afca1df85.yamlneutron-fwaas-12.0.2/releasenotes/notes/auto-association-default-firewall-group-7e9faf1afca1df85.yam0000664000175000017500000000071013555577713033344 0ustar zuulzuul00000000000000--- prelude: > Associating default firewall group for new VM ports within a project automatically. features: - | The default firewall group won't be applied to all new VM ports as default. However, if option ``auto_associate_default_firewall_group`` is enabled in neutron_fwaas.conf like: [fwaas] auto_associate_default_firewall_group = True Then, the default firewall group will be applied to all new VM ports. neutron-fwaas-12.0.2/releasenotes/notes/fwaas_v2-374471c215af0ca0.yaml0000664000175000017500000000167013555577713024767 0ustar zuulzuul00000000000000--- prelude: > The FWaaS team is pleased to release FWaaS v2.0. This release of FWaaS supports either the original FWaaS v1 or the new FWaaS v2. features: - In FWaaS v2 firewall policies are applied to router ports, as opposed to applying to routers in FWaaS v1. - Earlier the FWaaS agent integrated with the L3 agent by having the L3 Agent class inherit from the FWaaS Agent class. This meant that other service agents could not also integrate with the L3 agent. Now, using the L3 agent extensions mechanism, FWaaS (v1 and v2) plugs in to the L3 agent. This means that it can interoperate peacefully with other L3 advanced services that also implement the L3 agent extension mechanism, all without any code changes to Neutron. upgrade: - There is not currently a defined upgrade path from FWaaS v1 to FWaaS v2. - FWaaS v1 can not be enabled at the same time as FWaaS v2; one or the other must be chosen. neutron-fwaas-12.0.2/releasenotes/notes/varmour-fwaas-driver-removal-f7aa304a4544134a.yaml0000664000175000017500000000037513555577713031007 0ustar zuulzuul00000000000000--- prelude: > - The vArmour Firewall Driver is being removed from the FwaaS repo, as per decision to remove vendor drivers from the community repo. upgrade: - The vArmour Firewall Driver will not be available for use in the Newton release. neutron-fwaas-12.0.2/releasenotes/notes/ovs-firewall-driver-c347ea0a560b7e38.yaml0000664000175000017500000000103413555577713027250 0ustar zuulzuul00000000000000--- issues: - | Currently, the FWaaSv2 L2 driver can be configured as: ``firewall_driver = ovs`` And the Security Group driver is specified as: ``firewall_driver = openvswitch`` If both are configured, the packet will still only hit the FWaaS table in OVS and will not traverse the rules in the SG table. There are some fixes needed to support this model which are being tested and will be merged shortly. Currently there are no checks to allow only one of FWaaS L2 or SG to be configured.