diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6ed3ae5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +*.pyc +*.swp +*.egg*/ +.coverage +.tox/ +build/ +cover/ +dist/ +AUTHORS +ChangeLog + +# Translation catalogs +*.mo +*.pot diff --git a/.zuul.yaml b/.zuul.yaml new file mode 100644 index 0000000..9e7e032 --- /dev/null +++ b/.zuul.yaml @@ -0,0 +1,35 @@ +- job: + name: neutron-fwaas-dashboard-integration-tests + parent: horizon-integration-tests + required-projects: + - name: openstack/horizon + - name: openstack/neutron-fwaas + - name: openstack/neutron-fwaas-dashboard + roles: + - zuul: openstack-infra/devstack + - zuul: openstack/horizon + irrelevant-files: + - ^.*\.rst$ + - ^doc/.*$ + - ^releasenotes/.*$ + vars: + devstack_plugins: + neutron-fwaas: https://git.openstack.org/openstack/neutron-fwaas + neutron-fwaas-dashboard: https://git.openstack.org/openstack/neutron-fwaas-dashboard + devstack_services: + horizon: true + tox_envlist: integration + +- project: + templates: + - check-requirements + - horizon-non-primary-django-jobs + - openstack-cover-jobs-horizon + - openstack-lower-constraints-jobs-horizon + - openstack-python3-victoria-jobs-horizon + - publish-openstack-docs-pti + - release-notes-jobs-python3 + check: + jobs: + - neutron-fwaas-dashboard-integration-tests: + voting: false diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst new file mode 100644 index 0000000..ec26845 --- /dev/null +++ b/CONTRIBUTING.rst @@ -0,0 +1,17 @@ +If you would like to contribute to the development of OpenStack, you must +follow the steps in this page: + + https://docs.openstack.org/infra/manual/developers.html + +If you already have a good understanding of how the system works and your +OpenStack accounts are set up, you can skip to the development workflow +section of this documentation to learn how changes to OpenStack should be +submitted for review via the Gerrit tool: + + https://docs.openstack.org/infra/manual/developers.html#development-workflow + +Pull requests submitted through GitHub will be ignored. + +Bugs should be filed on Launchpad, not GitHub: + + https://bugs.launchpad.net/neutron-fwaas-dashboard diff --git a/HACKING.rst b/HACKING.rst new file mode 100644 index 0000000..78e093b --- /dev/null +++ b/HACKING.rst @@ -0,0 +1,13 @@ +========================================== +Neutron FWaaS Dashboard Style Commandments +========================================== + +Read the OpenStack Style Commandments +https://docs.openstack.org/hacking/latest/user/hacking.html + +Project Specific Commandments +----------------------------- + +- Read the Horizon contributing documentation at + https://docs.openstack.org/horizon/latest/contributor/contributing.html +- [M322] Method's default argument shouldn't be mutable. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..68c771a --- /dev/null +++ b/LICENSE @@ -0,0 +1,176 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + diff --git a/README.rst b/README.rst index 4ee2c5f..8326526 100644 --- a/README.rst +++ b/README.rst @@ -1,10 +1,18 @@ -This project is no longer maintained. +======================= +Neutron FWaaS Dashboard +======================= -The contents of this repository are still available in the Git -source code management system. To see the contents of this -repository before it reached its end of life, please check out the -previous commit with "git checkout HEAD^1". +.. warning:: + Due to lack of maintainers this project is now deprecated in the Neutron + stadium and will be removed from stadium in ``W`` cycle. + If You want to step in and be maintainer of this project to keep it in the + Neutron stadium, please contact the ``neutron team`` via + openstack-discuss@lists.openstack.org or IRC channel #openstack-neutron + @freenode. -For any further questions, please email -openstack-discuss@lists.openstack.org or join #openstack-dev on -OFTC. +OpenStack Dashboard panels for Neutron FWaaS + +* Documentation: https://docs.openstack.org/neutron-fwaas-dashboard/latest/ +* Source: https://opendev.org/openstack/neutron-fwaas-dashboard +* Bugs: https://bugs.launchpad.net/neutron-fwaas-dashboard +* Release Notes: https://docs.openstack.org/releasenotes/neutron-fwaas-dashboard/ diff --git a/babel-django.cfg b/babel-django.cfg new file mode 100644 index 0000000..9a8ac3b --- /dev/null +++ b/babel-django.cfg @@ -0,0 +1,2 @@ +[python: **.py] +[django: **/templates/**.html] diff --git a/babel-djangojs.cfg b/babel-djangojs.cfg new file mode 100644 index 0000000..b92caf6 --- /dev/null +++ b/babel-djangojs.cfg @@ -0,0 +1,2 @@ +[javascript: **.js] +[angular: **/static/**.html] diff --git a/bindep.txt b/bindep.txt new file mode 100644 index 0000000..da92738 --- /dev/null +++ b/bindep.txt @@ -0,0 +1,5 @@ +# selenium tests +firefox [selenium] +xvfb [selenium platform:dpkg] +# already part of xorg-x11-server on openSUSE +xorg-x11-server-Xvfb [selenium platform:redhat] diff --git a/devstack/README.rst b/devstack/README.rst new file mode 100644 index 0000000..915f97a --- /dev/null +++ b/devstack/README.rst @@ -0,0 +1,25 @@ +=========================================== +DevStack plugin for neutron-fwaas-dashboard +=========================================== + +This is setup as a DevStack plugin. +For more information on DevStack plugins, +see the `DevStack Plugins documentation +`__. + +If neutron-fwaas-dashboard DevStack plugin is enabled, +Neutron FWaaS dashboard is automatically enabled and +the appropriate version of FWaaS panel is displayed based on +the FWaaS version enabled in your neutron server. +You do not need to specify FWaaS API version in the DevStack plugin +configuration. + +To enable FWaaS dashboard, add the following to the localrc section +of your local.conf. + +.. code-block:: none + + [[local|localrc]] + enable_plugin neutron-fwaas https://opendev.org/openstack/neutron-fwaas master + enable_service q-fwaas-v2 + enable_plugin neutron-fwaas-dashboard https://opendev.org/openstack/neutron-fwaas-dashboard master diff --git a/devstack/plugin.sh b/devstack/plugin.sh new file mode 100644 index 0000000..ba088e6 --- /dev/null +++ b/devstack/plugin.sh @@ -0,0 +1,64 @@ +# plugin.sh - DevStack plugin.sh dispatch script neutron-fwaas-dashboard + +FWAAS_DASHBOARD_DIR=$(cd $(dirname $BASH_SOURCE)/.. && pwd) +FWAAS_ENABLED_DIR=$FWAAS_DASHBOARD_DIR/neutron_fwaas_dashboard/enabled +OPENSTACK_DASHBOARD_DIR=$DEST/horizon/openstack_dashboard +HORIZON_ENABLED_DIR=$OPENSTACK_DASHBOARD_DIR/local/enabled + +function install_neutron_fwaas_dashboard { + setup_develop $FWAAS_DASHBOARD_DIR +} + +function configure_neutron_fwaas_dashboard { + cp -a $FWAAS_ENABLED_DIR/_[0-9]*.py $HORIZON_ENABLED_DIR + # NOTE: If locale directory does not exist, compilemessages will fail, + # so check for an existence of locale directory is required. + if [ -d $FWAAS_DASHBOARD_DIR/neutron_fwaas_dashboard/locale ]; then + (cd $FWAAS_DASHBOARD_DIR/neutron_fwaas_dashboard; \ + DJANGO_SETTINGS_MODULE=openstack_dashboard.settings $PYTHON ../manage.py compilemessages) + fi + # Add policy file for FWaaS + cp $FWAAS_DASHBOARD_DIR/etc/neutron-fwaas-policy.json $OPENSTACK_DASHBOARD_DIR/conf/ + cp $FWAAS_DASHBOARD_DIR/neutron_fwaas_dashboard/local_settings.d/_7000_neutron_fwaas.py \ + $OPENSTACK_DASHBOARD_DIR/local/local_settings.d/ +} + +# check for service enabled +if is_service_enabled neutron-fwaas-dashboard; then + + if [[ "$1" == "stack" && "$2" == "pre-install" ]]; then + # Set up system services + # no-op + : + + elif [[ "$1" == "stack" && "$2" == "install" ]]; then + # Perform installation of service source + echo_summary "Installing Neutron FWaaS Dashboard" + install_neutron_fwaas_dashboard + + elif [[ "$1" == "stack" && "$2" == "post-config" ]]; then + # Configure after the other layer 1 and 2 services have been configured + echo_summary "Configurng Neutron FWaaS Dashboard" + configure_neutron_fwaas_dashboard + + elif [[ "$1" == "stack" && "$2" == "extra" ]]; then + # no-op + : + fi + + if [[ "$1" == "unstack" ]]; then + # Remove enabled file(s) + for _enabled_file in $FWAAS_ENABLED_DIR/_[0-9]*.py; do + _enabled_basename=$(basename $_enabled_file .py) + rm -f $HORIZON_ENABLED_DIR/${_enabled_basename}.py* + rm -f $HORIZON_ENABLED_DIR/__pycache__/${_enabled_basename}.*pyc + done + fi + + if [[ "$1" == "clean" ]]; then + # Remove state and transient data + # Remember clean.sh first calls unstack.sh + rm -f $OPENSTACK_DASHBOARD_DIR/local/local_settings.d/_7000_neutron_fwaas.py* + rm -f $OPENSTACK_DASHBOARD_DIR/conf/neutron-fwaas-policy.json + fi +fi diff --git a/devstack/settings b/devstack/settings new file mode 100644 index 0000000..4b61ae1 --- /dev/null +++ b/devstack/settings @@ -0,0 +1,2 @@ +# settings file for neutron-fwaas-dashboard plugin +enable_service neutron-fwaas-dashboard diff --git a/doc/source/conf.py b/doc/source/conf.py new file mode 100644 index 0000000..14654d6 --- /dev/null +++ b/doc/source/conf.py @@ -0,0 +1,215 @@ +# 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. +# +# Horizon documentation build configuration file, created by +# sphinx-quickstart on Thu Oct 27 11:38:59 2011. +# +# 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. + +import os +import sys + +import django + +PROJECT = 'neutron-fwaas-dashboard' +BASE_DIR = os.path.dirname(os.path.abspath(__file__)) +ROOT = os.path.abspath(os.path.join(BASE_DIR, "..", "..")) + +sys.path.insert(0, ROOT) + +# This is required for ReadTheDocs.org, but isn't a bad idea anyway. +os.environ.setdefault('DJANGO_SETTINGS_MODULE', + 'neutron_fwaas_dashboard.test.settings') + +django.setup() + + +# 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 = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.todo', + 'sphinx.ext.coverage', + 'sphinx.ext.viewcode', + 'reno.sphinxext', + 'openstackdocstheme', +] + +# openstackdocstheme options +openstackdocs_repo_name = 'openstack/neutron-fwaas-dashboard' +openstackdocs_pdf_link = True +openstackdocs_auto_name = False +openstackdocs_bug_project = 'neutron-fwaas-dashboard' +openstackdocs_bug_tag = 'doc' + +# 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 Dashboard' +copyright = u'2017, OpenStack Foundation' + +# 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 = 'native' + +# A list of ignored prefixes for module index sorting. +modindex_common_prefix = ['neutron_fwaas_dashboard.'] + +primary_domain = 'py' +nitpicky = 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_path = ['.'] +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 = { + "nosidebar": "false" +} + +# 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 = [] + +# 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' + +# 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 = 'neutronfwaasdashboarddoc' + +# -- Options for LaTeX output ------------------------------------------------ + +latex_documents = [ + ('index', 'doc-neutron-fwaas-dashboard.tex', + u'Neutron FWaaS Dashboard Documentation', + u'Neutron Contributors', 'howto'), +] diff --git a/doc/source/configuration/index.rst b/doc/source/configuration/index.rst new file mode 100644 index 0000000..719d4d8 --- /dev/null +++ b/doc/source/configuration/index.rst @@ -0,0 +1,28 @@ +.. + Copyright 2017 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. + +============= +Configuration +============= + +Neutron FWaaS dashboard has no configuration option. +It checks if Neutron FWaaS service is enabled in your neutron server +and the FWaaS panel is displayed if enabled. + +For more configurations, see +`Settings Reference +`__ +in the Horizon documentation. diff --git a/doc/source/contributor/devstack-plugin.rst b/doc/source/contributor/devstack-plugin.rst new file mode 100644 index 0000000..da85f63 --- /dev/null +++ b/doc/source/contributor/devstack-plugin.rst @@ -0,0 +1 @@ +.. include:: ../../../devstack/README.rst diff --git a/doc/source/contributor/index.rst b/doc/source/contributor/index.rst new file mode 100644 index 0000000..3fdcd2d --- /dev/null +++ b/doc/source/contributor/index.rst @@ -0,0 +1,32 @@ +.. + Copyright 2017 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. + +================= +Contributor Guide +================= + +There is no topic specific to neutron-fwaas-dashboard now. + +See `Horizon Contributor Documentation +`__ +for general topic on developing a dashboard on horizon. + +---- + +.. toctree:: + :maxdepth: 1 + + DevStack plugin diff --git a/doc/source/index.rst b/doc/source/index.rst new file mode 100644 index 0000000..c6b4f5b --- /dev/null +++ b/doc/source/index.rst @@ -0,0 +1,37 @@ +.. + Copyright 2017 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. + +================================ +Neutron FWaaS Dashboard Project +================================ + +.. warning:: + Due to lack of maintainers this project is now deprecated in the Neutron + stadium and will be removed from stadium in ``W`` cycle. + If You want to step in and be maintainer of this project to keep it in the + Neutron stadium, please contact the ``neutron team`` via + openstack-discuss@lists.openstack.org or IRC channel #openstack-neutron + @freenode. + +Neutron FWaaS Dashboard is a horizon plugin for Neutron FWaaS. + +.. toctree:: + :maxdepth: 1 + + install/index + configuration/index + contributor/index + Release Notes diff --git a/doc/source/install/index.rst b/doc/source/install/index.rst new file mode 100644 index 0000000..5bc63db --- /dev/null +++ b/doc/source/install/index.rst @@ -0,0 +1,99 @@ +.. + Copyright 2017 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. + +============ +Installation +============ + +Manual Installation +------------------- + +Before installing neutron-fwaas-dashboard, +you first need to install horizon in your environment. + +Fetch the source code from git and run pip install. +Make sure to install neutron-fwaas-dashboard into the same python environment +where horizon is installed. + +.. code-block:: console + + $ git clone https://opendev.org/openstack/neutron-fwaas-dashboard + $ cd neutron-fwaas-dashboard + $ sudo pip install . + +Enable the horizon plugin. + +.. code-block:: console + + $ cp neutron_fwaas_dashboard/enabled/_70*_*.py \ + /opt/stack/horizon/openstack_dashboard/local/enabled/ + +.. note:: + + The directory ``local/enabled`` may be different depending on your + environment or distribution used. For example, for Ubuntu, this is + ``/usr/share/openstack-dashboard/openstack_dashboard/local/enabled``. + +.. note:: + + The number of the plugin enabled file determines the order of panels. + If you would like to configure the place of the Neutron FWaaS dashboard, + change the number of the file. + +.. note:: + + For more detail of the horizon plugin settings, + see `Pluggable Settings + `__ + in the horizon documentation. + +Configure the policy file for Neutron FWaaS dashboard +in OpenStack Dashboard ``local_settings.py``. +```` is a directory which contains +configurations for Neutron FWaaS dashboard and the location varies +across distributions or deployments. + +.. code-block:: python + + POLICY_FILES['neutron-fwaas'] = '/etc/neutron-fwaas-policy.json' + +.. note:: + + If you do not configure ``POLICY_FILES`` in your ``local_settings.py``, + you also need to define the default ``POLICY_FILES`` in + ``local_settings.py``. If you use the example ``local_settings.py`` file + from horizon, what you need is to uncomment ``POLICY_FILES`` (which contains + the default values). + +Compile the message catalogs of Neutron FWaaS dashboard. + +.. code-block:: console + + $ cd neutron-fwaas-dashboard + $ ./manage.py compilemessages + +Run the Django update commands (if you use). + +.. code-block:: console + + $ DJANGO_SETTINGS_MODULE=openstack_dashboard.settings python manage.py collectstatic --noinput + $ DJANGO_SETTINGS_MODULE=openstack_dashboard.settings python manage.py compress --force + +Restart Apache: + +.. code-block:: console + + $ sudo service apache2 restart diff --git a/etc/neutron-fwaas-policy.json b/etc/neutron-fwaas-policy.json new file mode 100644 index 0000000..ecd00ab --- /dev/null +++ b/etc/neutron-fwaas-policy.json @@ -0,0 +1,36 @@ +{ + "context_is_admin": "role:admin", + "owner": "tenant_id:%(tenant_id)s", + "admin_or_owner": "rule:context_is_admin or rule:owner", + "admin_only": "rule:context_is_admin", + "shared_firewalls": "field:firewalls:shared=True", + "shared_firewall_policies": "field:firewall_policies:shared=True", + "default": "rule:admin_or_owner", + + "create_firewall": "", + "get_firewall": "rule:admin_or_owner", + "create_firewall:shared": "rule:admin_only", + "get_firewall:shared": "rule:admin_only", + "update_firewall": "rule:admin_or_owner", + "update_firewall:shared": "rule:admin_only", + "delete_firewall": "rule:admin_or_owner", + + "create_firewall_policy": "", + "get_firewall_policy": "rule:admin_or_owner or rule:shared_firewall_policies", + "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", + + "insert_rule": "rule:admin_or_owner", + "remove_rule": "rule:admin_or_owner", + + "create_firewall_rule": "", + "get_firewall_rule": "rule:admin_or_owner or rule:shared_firewalls", + "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" +} diff --git a/lower-constraints.txt b/lower-constraints.txt new file mode 100644 index 0000000..646bfb2 --- /dev/null +++ b/lower-constraints.txt @@ -0,0 +1,151 @@ +alabaster==0.7.10 +amqp==2.1.1 +appdirs==1.3.0 +asn1crypto==0.23.0 +Babel==2.3.4 +cachetools==2.0.0 +cffi==1.7.0 +cliff==2.8.0 +cmd2==0.8.0 +contextlib2==0.4.0 +coverage==4.0 +cryptography==2.1 +debtcollector==1.2.0 +decorator==4.1.0 +deprecation==1.0 +Django==2.2 +django-appconf==1.0.2 +django-babel==0.6.2 +django-compressor==2.0 +django-pyscss==2.0.2 +docutils==0.12 +dogpile.cache==0.6.2 +dulwich==0.15.0 +eventlet==0.18.2 +extras==1.0.0 +fasteners==0.7.0 +fixtures==3.0.0 +flake8==3.6.0 +flake8-import-order==0.13 +futurist==1.2.0 +greenlet==0.4.10 +hacking==3.0.1 +horizon==17.1.0 +idna==2.6 +imagesize==0.7.1 +iso8601==0.1.11 +Jinja2==2.10 +jmespath==0.9.0 +jsonpatch==1.16 +jsonpointer==1.13 +jsonschema==2.6.0 +keystoneauth1==3.4.0 +kombu==4.0.0 +linecache2==1.0.0 +MarkupSafe==1.1.1 +mccabe==0.6.0 +monotonic==0.6 +msgpack-python==0.4.0 +munch==2.1.0 +netaddr==0.7.18 +netifaces==0.10.4 +openstackdocstheme==2.2.1 +openstacksdk==0.11.2 +os-client-config==1.28.0 +os-service-types==1.2.0 +osc-lib==1.8.0 +oslo.concurrency==3.26.0 +oslo.config==5.2.0 +oslo.context==2.19.2 +oslo.i18n==3.15.3 +oslo.log==3.36.0 +oslo.messaging==5.29.0 +oslo.middleware==3.31.0 +oslo.policy==1.30.0 +oslo.serialization==2.18.0 +oslo.service==1.24.0 +oslo.utils==3.33.0 +osprofiler==2.3.0 +Paste==2.0.2 +PasteDeploy==1.5.0 +pbr==2.0.0 +pep8==1.5.7 +pika==0.10.0 +pika-pool==0.1.3 +Pint==0.5 +positional==1.2.1 +prettytable==0.7.2 +pycodestyle==2.4.0 +pycparser==2.18 +pyflakes==2.0.0 +Pygments==2.2.0 +pyinotify==0.9.6 +pymongo==3.0.2 +pyOpenSSL==17.1.0 +pyparsing==2.1.0 +pyperclip==1.5.27 +pyScss==1.3.7 +python-cinderclient==5.0.0 +python-dateutil==2.5.3 +python-glanceclient==2.8.0 +python-keystoneclient==3.22.0 +python-mimeparse==1.6.0 +python-neutronclient==6.7.0 +python-novaclient==9.1.0 +python-swiftclient==3.2.0 +pytz==2013.6 +PyYAML==3.12 +rcssmin==1.0.6 +reno==3.1.0 +repoze.lru==0.7 +requests==2.14.2 +requestsexceptions==1.2.0 +rfc3986==0.3.1 +rjsmin==1.0.12 +Routes==2.3.1 +selenium==2.50.1 +semantic-version==2.3.1 +simplejson==3.5.1 +six==1.10.0 +snowballstemmer==1.2.1 +Sphinx==2.0.0 +sphinxcontrib-websupport==1.0.1 +statsd==3.2.1 +stevedore==1.20.0 +tenacity==3.2.1 +testtools==2.2.0 +traceback2==1.4.0 +unittest2==1.1.0 +vine==1.1.4 +warlock==1.2.0 +WebOb==1.7.1 +wrapt==1.7.0 +XStatic==1.0.0 +XStatic-Angular==1.5.8.0 +XStatic-Angular-Bootstrap==2.2.0.0 +XStatic-Angular-FileUpload==12.0.4.0 +XStatic-Angular-Gettext==2.3.8.0 +XStatic-Angular-lrdragndrop==1.0.2.2 +XStatic-Angular-Schema-Form==0.8.13.0 +XStatic-Bootstrap-Datepicker==1.3.1.0 +XStatic-Bootstrap-SCSS==3.3.7.1 +XStatic-bootswatch==3.3.7.0 +XStatic-D3==3.5.17.0 +XStatic-Font-Awesome==4.7.0.0 +XStatic-Hogan==2.0.0.2 +XStatic-Jasmine==2.4.1.1 +XStatic-jQuery==1.8.2.1 +XStatic-JQuery-Migrate==1.2.1.1 +XStatic-jquery-ui==1.10.4.1 +XStatic-JQuery.quicksearch==2.0.3.1 +XStatic-JQuery.TableSorter==2.14.5.1 +XStatic-JSEncrypt==2.3.1.1 +XStatic-mdi==1.4.57.0 +XStatic-objectpath==1.2.1.0 +XStatic-Rickshaw==1.5.0.0 +XStatic-roboto-fontface==0.5.0.0 +XStatic-smart-table==1.4.13.2 +XStatic-Spin==1.2.5.2 +XStatic-term.js==0.0.7.0 +XStatic-tv4==1.2.7.0 +xvfbwrapper==0.1.3 diff --git a/manage.py b/manage.py new file mode 100755 index 0000000..b1a10ad --- /dev/null +++ b/manage.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +# 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 sys + +from django.core.management import execute_from_command_line # noqa + +if __name__ == "__main__": + os.environ.setdefault("DJANGO_SETTINGS_MODULE", + "neutron_fwaas_dashboard.test.settings") + execute_from_command_line(sys.argv) diff --git a/neutron_fwaas_dashboard/__init__.py b/neutron_fwaas_dashboard/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/neutron_fwaas_dashboard/api/__init__.py b/neutron_fwaas_dashboard/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/neutron_fwaas_dashboard/api/fwaas_v2.py b/neutron_fwaas_dashboard/api/fwaas_v2.py new file mode 100644 index 0000000..50d3954 --- /dev/null +++ b/neutron_fwaas_dashboard/api/fwaas_v2.py @@ -0,0 +1,348 @@ +# 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 collections + +from openstack_dashboard.api import neutron +import openstack_dashboard.api.nova as nova +from openstack_dashboard.contrib.developer.profiler import api as profiler + + +neutronclient = neutron.neutronclient + + +class Port(neutron.NeutronAPIDictWrapper): + """Wrapper for neutron port.""" + + +class Rule(neutron.NeutronAPIDictWrapper): + """Wrapper for neutron firewall rule.""" + + +class Policy(neutron.NeutronAPIDictWrapper): + """Wrapper for neutron firewall policy.""" + + +class FirewallGroup(neutron.NeutronAPIDictWrapper): + """Wrapper for neutron firewall group.""" + + +def rule_create(request, **kwargs): + """Create a firewall rule + + :param request: request context + :param name: name for rule + :param description: description for rule + :param protocol: protocol for rule + :param action: action for rule + :param source_ip_address: source IP address or subnet + :param source_port: integer in [1, 65535] or range in a:b + :param destination_ip_address: destination IP address or subnet + :param destination_port: integer in [1, 65535] or range in a:b + :param shared: boolean (default false) + :param enabled: boolean (default true) + :return: Rule object + """ + body = {'firewall_rule': kwargs} + rule = neutronclient(request).create_fwaas_firewall_rule( + body).get('firewall_rule') + return Rule(rule) + + +@profiler.trace +def get_network_names(request): + networks = neutronclient(request).list_networks(fields=["name", "id"])\ + .get('networks', []) + mapped = {n['id']: neutron.Network(n) for n in networks} + return mapped + + +@profiler.trace +def get_router_names(request): + routers = neutronclient(request).list_routers(fields=["name", "id"])\ + .get('routers', []) + mapped = {r['id']: neutron.Router(r) for r in routers} + return mapped + + +@profiler.trace +def get_servers(request): + servers = nova.server_list(request)[0] + mapped = {s.id: s for s in servers} + return mapped + + +@profiler.trace +def rule_list(request, **kwargs): + return _rule_list(request, **kwargs) + + +@profiler.trace +def port_list(request, tenant_id, **kwargs): + kwargs['tenant_id'] = tenant_id + ports = neutronclient(request).list_ports(**kwargs).get('ports') + + return { + p['id']: Port(p) for p in ports if _is_target(p) + } + + +# Gets ids of all ports assigned to firewall groups +@profiler.trace +def fwg_port_list(request, **kwargs): + fwgs = neutronclient(request).list_fwaas_firewall_groups( + **kwargs).get('firewall_groups') + ports = set() + for fwg in fwgs: + if fwg['ports']: + ports.update(fwg['ports']) + return ports + + +@profiler.trace +def fwg_port_list_for_tenant(request, tenant_id, **kwargs): + kwargs['tenant_id'] = tenant_id + ports = neutronclient(request).list_ports(**kwargs).get('ports') + # TODO(SarathMekala): Remove ports which are already associated with a FWG + fwgs = neutronclient(request).list_fwaas_firewall_groups( + **kwargs).get('firewall_groups') + fwg_ports = [] + for fwg in fwgs: + if not fwg['ports']: + continue + fwg_ports += fwg['ports'] + return [Port(p) for p in ports + if _is_target(p) and p['id'] not in fwg_ports] + + +def _is_target(port): + return (port['device_owner'].startswith('compute:') or + port['device_owner'].startswith('network:router_interface')) + + +@profiler.trace +def rule_list_for_tenant(request, tenant_id, **kwargs): + """Return a rule list available for the tenant. + + The list contains rules owned by the tenant and shared rules. + This is required because Neutron returns all resources including + all tenants if a user has admin role. + """ + rules = rule_list(request, tenant_id=tenant_id, shared=False, **kwargs) + shared_rules = rule_list(request, shared=True, **kwargs) + return rules + shared_rules + + +def _rule_list(request, **kwargs): + rules = neutronclient(request).list_fwaas_firewall_rules( + **kwargs).get('firewall_rules') + return [Rule(r) for r in rules] + + +@profiler.trace +def rule_get(request, rule_id): + return _rule_get(request, rule_id) + + +def _rule_get(request, rule_id): + rule = neutronclient(request).show_fwaas_firewall_rule( + rule_id).get('firewall_rule') + return Rule(rule) + + +@profiler.trace +def rule_delete(request, rule_id): + neutronclient(request).delete_fwaas_firewall_rule(rule_id) + + +@profiler.trace +def rule_update(request, rule_id, **kwargs): + body = {'firewall_rule': kwargs} + rule = neutronclient(request).update_fwaas_firewall_rule( + rule_id, body).get('firewall_rule') + return Rule(rule) + + +@profiler.trace +def policy_create(request, **kwargs): + """Create a firewall policy + + :param request: request context + :param name: name for policy + :param description: description for policy + :param firewall_rules: ordered list of rules in policy + :param shared: boolean (default false) + :param audited: boolean (default false) + :return: Policy object + """ + body = {'firewall_policy': kwargs} + policy = neutronclient(request).create_fwaas_firewall_policy( + body).get('firewall_policy') + return Policy(policy) + + +@profiler.trace +def policy_list(request, **kwargs): + return _policy_list(request, expand_rule=True, **kwargs) + + +@profiler.trace +def policy_list_for_tenant(request, tenant_id, **kwargs): + """Return a policy list available for the tenant. + + The list contains policies owned by the tenant and shared policies. + This is required because Neutron returns all resources including + all tenants if a user has admin role. + """ + policies = policy_list(request, tenant_id=tenant_id, + shared=False, **kwargs) + shared_policies = policy_list(request, shared=True, **kwargs) + return policies + shared_policies + + +def _policy_list(request, expand_rule, **kwargs): + policies = neutronclient(request).list_fwaas_firewall_policies( + **kwargs).get('firewall_policies') + if expand_rule and policies: + rules = _rule_list(request) + rule_dict = collections.OrderedDict((rule.id, rule) for rule in rules) + for p in policies: + p['rules'] = [rule_dict.get(rule) for rule in p['firewall_rules']] + return [Policy(p) for p in policies] + + +@profiler.trace +def policy_get(request, policy_id): + return _policy_get(request, policy_id, expand_rule=True) + + +def _policy_get(request, policy_id, expand_rule): + policy = neutronclient(request).show_fwaas_firewall_policy( + policy_id).get('firewall_policy') + if expand_rule: + policy_rules = policy['firewall_rules'] + if policy_rules: + rules = _rule_list(request, firewall_policy_id=policy_id) + rule_dict = collections.OrderedDict((rule.id, rule) + for rule in rules) + policy['rules'] = [rule_dict.get(rule) for rule in policy_rules] + else: + policy['rules'] = [] + return Policy(policy) + + +@profiler.trace +def policy_delete(request, policy_id): + neutronclient(request).delete_fwaas_firewall_policy(policy_id) + + +@profiler.trace +def policy_update(request, policy_id, **kwargs): + body = {'firewall_policy': kwargs} + policy = neutronclient(request).update_fwaas_firewall_policy( + policy_id, body).get('firewall_policy') + return Policy(policy) + + +@profiler.trace +def policy_insert_rule(request, policy_id, **kwargs): + policy = neutronclient(request).insert_rule_fwaas_firewall_policy( + policy_id, kwargs) + return Policy(policy) + + +@profiler.trace +def policy_remove_rule(request, policy_id, **kwargs): + policy = neutronclient(request).remove_rule_fwaas_firewall_policy( + policy_id, kwargs) + return Policy(policy) + + +@profiler.trace +def firewall_group_create(request, **kwargs): + """Create a firewall group for specified policy + + :param request: request context + :param name: name for firewall group + :param description: description for firewall group + :param firewall_policy_id: policy id used by firewall group + :param shared: boolean (default false) + :param admin_state_up: boolean (default true) + :return: Firewall group object + """ + body = {'firewall_group': kwargs} + firewall_group = neutronclient(request).create_fwaas_firewall_group(body) + return FirewallGroup(firewall_group['firewall_group']) + + +@profiler.trace +def firewall_group_list(request, **kwargs): + return _firewall_group_list(request, **kwargs) + + +@profiler.trace +def firewall_group_list_for_tenant(request, tenant_id, **kwargs): + """Return a firewall group list available for the tenant. + + The list contains firewall groups owned by the tenant and shared firewall + groups. This is required because Neutron returns all resources including + all tenants if a user has admin role. + """ + fwg = firewall_group_list(request, tenant_id=tenant_id, + shared=False, **kwargs) + shared_fwg = firewall_group_list(request, shared=True, **kwargs) + return fwg + shared_fwg + + +# TODO(SarathMekala): Support expand_policy for _firewall_group_list +def _firewall_group_list(request, **kwargs): + firewall_groups = neutronclient(request).list_fwaas_firewall_groups( + **kwargs).get('firewall_groups') + return [FirewallGroup(f) for f in firewall_groups] + + +@profiler.trace +def firewall_group_get(request, firewallgroup_id): + return _firewall_group_get(request, firewallgroup_id, expand_policy=True) + + +def _firewall_group_get(request, firewallgroup_id, expand_policy): + firewall_group = neutronclient(request).show_fwaas_firewall_group( + firewallgroup_id).get('firewall_group') + if expand_policy: + ingress_policy_id = firewall_group['ingress_firewall_policy_id'] + if ingress_policy_id: + firewall_group['ingress_policy'] = _policy_get( + request, ingress_policy_id, expand_rule=False) + else: + firewall_group['ingress_policy'] = None + + egress_policy_id = firewall_group['egress_firewall_policy_id'] + if egress_policy_id: + firewall_group['egress_policy'] = _policy_get( + request, egress_policy_id, expand_rule=False) + else: + firewall_group['egress_policy'] = None + return FirewallGroup(firewall_group) + + +@profiler.trace +def firewall_group_delete(request, firewallgroup_id): + neutronclient(request).delete_fwaas_firewall_group(firewallgroup_id) + + +@profiler.trace +def firewall_group_update(request, firewallgroup_id, **kwargs): + body = {'firewall_group': kwargs} + firewall_group = neutronclient(request).update_fwaas_firewall_group( + firewallgroup_id, body).get('firewall_group') + return FirewallGroup(firewall_group) diff --git a/neutron_fwaas_dashboard/dashboards/__init__.py b/neutron_fwaas_dashboard/dashboards/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/neutron_fwaas_dashboard/dashboards/project/__init__.py b/neutron_fwaas_dashboard/dashboards/project/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/__init__.py b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/forms.py b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/forms.py new file mode 100644 index 0000000..a99a32a --- /dev/null +++ b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/forms.py @@ -0,0 +1,449 @@ +# Copyright 2017, Juniper Networks, 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 logging +from operator import attrgetter + +from django.urls import reverse +from django.utils.translation import ugettext_lazy as _ +from horizon import exceptions +from horizon import forms +from horizon import messages +from horizon.utils import validators + +from neutron_fwaas_dashboard.api import fwaas_v2 as api_fwaas_v2 +from neutron_fwaas_dashboard.dashboards.project.firewalls_v2 import widgets + +port_validator = validators.validate_port_or_colon_separated_port_range + +LOG = logging.getLogger(__name__) + + +class UpdateRule(forms.SelfHandlingForm): + name = forms.CharField(max_length=80, label=_("Name"), required=False) + description = forms.CharField( + required=False, + max_length=80, label=_("Description")) + protocol = forms.ThemableChoiceField( + label=_("Protocol"), + choices=[('tcp', _('TCP')), ('udp', _('UDP')), ('icmp', _('ICMP')), + ('any', _('ANY'))], + help_text=_('Protocol for the firewall rule')) + action = forms.ThemableChoiceField( + label=_("Action"), + choices=[('allow', _('ALLOW')), ('deny', _('DENY')), + ('reject', _('REJECT'))], + help_text=_('Action for the firewall rule')) + source_ip_address = forms.IPField( + label=_("Source IP Address/Subnet"), + version=forms.IPv4 | forms.IPv6, + required=False, mask=True, + help_text=_('Source IP address or subnet')) + destination_ip_address = forms.IPField( + label=_('Destination IP Address/Subnet'), + version=forms.IPv4 | forms.IPv6, + required=False, mask=True, + help_text=_('Destination IP address or subnet')) + source_port = forms.CharField( + max_length=80, + label=_("Source Port/Port Range"), + required=False, + validators=[port_validator], + help_text=_('Source port (integer in [1, 65535] or range in a:b)')) + destination_port = forms.CharField( + max_length=80, + label=_("Destination Port/Port Range"), + required=False, + validators=[port_validator], + help_text=_('Destination port (integer in [1, 65535] or range' + ' in a:b)')) + ip_version = forms.ThemableChoiceField( + label=_("IP Version"), + choices=[('4', '4'), ('6', '6')], + help_text=_('IP Version for Firewall Rule')) + shared = forms.BooleanField(label=_("Shared"), required=False) + enabled = forms.BooleanField(label=_("Enabled"), required=False) + + failure_url = 'horizon:project:firewalls_v2:index' + + def _convert_req_body(self, body): + for key in ['source_port', 'source_ip_address', + 'destination_port', 'destination_ip_address']: + if key in body and not body[key]: + body[key] = None + if body.get('protocol') == 'any': + body['protocol'] = None + return body + + def handle(self, request, context): + rule_id = self.initial['id'] + name_or_id = context.get('name') or rule_id + body = self._convert_req_body(_get_request_body(context, self.initial)) + try: + rule = api_fwaas_v2.rule_update(request, rule_id, **body) + msg = _('Rule %s was successfully updated.') % name_or_id + messages.success(request, msg) + return rule + except Exception as e: + msg = (_('Failed to update rule %(name)s: %(reason)s') % + {'name': name_or_id, 'reason': e}) + redirect = reverse(self.failure_url) + exceptions.handle(request, msg, redirect=redirect) + + +class UpdatePolicy(forms.SelfHandlingForm): + name = forms.CharField(max_length=80, label=_("Name"), required=False) + description = forms.CharField(required=False, + max_length=80, label=_("Description")) + shared = forms.BooleanField(label=_("Shared"), required=False) + audited = forms.BooleanField(label=_("Audited"), required=False) + + failure_url = 'horizon:project:firewalls_v2:index' + + def handle(self, request, context): + policy_id = self.initial['id'] + name_or_id = context.get('name') or policy_id + body = _get_request_body(context, self.initial) + try: + policy = api_fwaas_v2.policy_update(request, policy_id, **body) + msg = _('Policy %s was successfully updated.') % name_or_id + messages.success(request, msg) + return policy + except Exception as e: + msg = (_('Failed to update policy %(name)s: %(reason)s') % + {'name': name_or_id, 'reason': e}) + redirect = reverse(self.failure_url) + exceptions.handle(request, msg, redirect=redirect) + + +class UpdateFirewall(forms.SelfHandlingForm): + name = forms.CharField(max_length=80, + label=_("Name"), + required=False) + description = forms.CharField(max_length=80, + label=_("Description"), + required=False) + ingress_firewall_policy_id = forms.ThemableChoiceField( + label=_("Ingress Policy"), required=False) + egress_firewall_policy_id = forms.ThemableChoiceField( + label=_("Egress Policy"), required=False) + admin_state_up = forms.BooleanField(label=_("Admin State"), + required=False) + shared = forms.BooleanField(label=_("Shared"), required=False) + failure_url = 'horizon:project:firewalls_v2:index' + + def __init__(self, request, *args, **kwargs): + super(UpdateFirewall, self).__init__(request, *args, **kwargs) + + try: + tenant_id = self.request.user.tenant_id + policies = api_fwaas_v2.policy_list_for_tenant(request, tenant_id) + policies = sorted(policies, key=attrgetter('name')) + except Exception: + exceptions.handle(request, _('Unable to retrieve policy list.')) + policies = [] + + egress_policy_id_choices = [] + ingress_policy_id_choices = [] + ingress_policy_id = kwargs['initial']['ingress_firewall_policy_id'] + if ingress_policy_id: + ingress_policy_name = [ + p.name for p in policies if p.id == ingress_policy_id][0] + ingress_policy_id_choices.append( + (ingress_policy_id, ingress_policy_name)) + egress_policy_id = kwargs['initial']['egress_firewall_policy_id'] + if egress_policy_id: + egress_policy_name = [ + p.name for p in policies if p.id == egress_policy_id][0] + egress_policy_id_choices.append((egress_policy_id, + egress_policy_name)) + + ingress_policy_id_choices.append(('', _('None'))) + egress_policy_id_choices.append(('', _('None'))) + + for p in policies: + if p.id != ingress_policy_id: + ingress_policy_id_choices.append((p.id, p.name_or_id)) + if p.id != egress_policy_id: + egress_policy_id_choices.append((p.id, p.name_or_id)) + + self.fields['ingress_firewall_policy_id'].choices = \ + ingress_policy_id_choices + self.fields['egress_firewall_policy_id'].choices = \ + egress_policy_id_choices + + def _convert_req_body(self, body): + for key in ['ingress_firewall_policy_id', 'egress_firewall_policy_id']: + if key in body and not body[key]: + body[key] = None + return body + + def handle(self, request, context): + firewallgroup_id = self.initial['id'] + name_or_id = context.get('name') or firewallgroup_id + body = self._convert_req_body(_get_request_body(context, self.initial)) + try: + fwg = api_fwaas_v2.firewall_group_update(request, + firewallgroup_id, + **body) + msg = _('Firewall group %s was successfully updated.') % name_or_id + messages.success(request, msg) + return fwg + except Exception as e: + msg = (_('Failed to update firewall group %(name)s: %(reason)s') % + {'name': name_or_id, 'reason': e}) + redirect = reverse(self.failure_url) + exceptions.handle(request, msg, redirect=redirect) + + +class PortSelectionForm(forms.SelfHandlingForm): + port_id = forms.ThemableDynamicChoiceField( + label=_("Ports"), + required=False, + widget=widgets.TableSelectWidget( + columns=['Port', 'Network', 'Owner', 'Device'], + alternate_xs=True + ) + ) + + networks = {} + routers = {} + servers = {} + ports = {} + + def __init__(self, request, *args, **kwargs): + super(PortSelectionForm, self).__init__(request, *args, **kwargs) + + tenant_id = self.request.user.tenant_id + + self.ports = api_fwaas_v2.port_list(request, tenant_id, **kwargs) + self.networks = api_fwaas_v2.get_network_names(request) + self.routers = api_fwaas_v2.get_router_names(request) + self.servers = api_fwaas_v2.get_servers(request) + + self.fields['port_id'].widget.build_columns = self._build_col + self.fields['port_id'].choices = self.get_ports(request) + + def get_ports(self, request): + return [] + + def _build_col(self, option): + port = self.ports[option[0]] + columns = self._build_option(port) + return columns + + def _build_option(self, port): + network = self.networks.get(port.network_id) + + network_label = network.name_or_id if network else port.network_id + owner_label = '' + device_label = '' + + if port.device_owner.startswith('network'): + owner_label = 'network' + router = self.routers.get(port.device_id, None) + device_label = router.name_or_id if router else port.device_id + elif port.device_owner.startswith('compute'): + owner_label = 'compute' + server = self.servers.get(port.device_id, None) + device_label = server.name_or_id if server else port.device_id + + columns = (port.name_or_id, network_label, owner_label, device_label) + + # The return value works off of the original themeable select widget + # This needs to be maintained for the original javascript to work + return columns + + +class AddPort(PortSelectionForm): + failure_url = 'horizon:project:firewalls_v2:index' + + def get_ports(self, request): + used_ports = api_fwaas_v2.fwg_port_list(request) + ports = self.ports.values() + return [(p.id, p.id) for p in ports if p.id not in used_ports] + + def handle(self, request, context): + firewallgroup_id = self.initial['id'] + name_or_id = context.get('name') or firewallgroup_id + body = _get_request_body(context, self.initial) + add_port = context['port_id'] + if add_port: + ports = self.initial['ports'] + ports.append(add_port) + body['ports'] = ports + try: + firewallgroup = api_fwaas_v2.firewall_group_update( + request, firewallgroup_id, **body) + msg = (_('Added the port(s) to the firewall group %s ' + 'successfully.') % name_or_id) + messages.success(request, msg) + return firewallgroup + except Exception as e: + msg = (_('Failed to add the port(s) to the firewall group ' + '%(name)s: %(reason)s') % + {'name': name_or_id, 'reason': e}) + redirect = reverse(self.failure_url) + exceptions.handle(request, msg, redirect=redirect) + + +class RemovePort(PortSelectionForm): + failure_url = 'horizon:project:firewalls_v2:index' + + def get_ports(self, request): + ports = self.initial['ports'] + return [(p, p) for p in ports] + + def handle(self, request, context): + firewallgroup_id = self.initial['id'] + name_or_id = context.get('name') or firewallgroup_id + body = _get_request_body(context, self.initial) + remove_port = context['port_id'] + if remove_port: + ports = self.initial['ports'] + ports.remove(remove_port) + body['ports'] = ports + try: + firewallgroup = api_fwaas_v2.firewall_group_update( + request, firewallgroup_id, **body) + msg = _('Removed the port(s) from the firewall group %s ' + 'successfully.') % name_or_id + messages.success(request, msg) + return firewallgroup + except Exception as e: + msg = (_('Failed to remove the port(s) from the firewall group ' + '%(name)s: %(reason)s') % + {'name': name_or_id, 'reason': e}) + redirect = reverse(self.failure_url) + exceptions.handle(request, msg, redirect=redirect) + + +class InsertRuleToPolicy(forms.SelfHandlingForm): + firewall_rule_id = forms.ThemableChoiceField(label=_("Insert Rule")) + insert_before = forms.ThemableChoiceField(label=_("Before"), + required=False) + insert_after = forms.ThemableChoiceField(label=_("After"), + required=False) + + failure_url = 'horizon:project:firewalls_v2:index' + + def __init__(self, request, *args, **kwargs): + super(InsertRuleToPolicy, self).__init__(request, *args, **kwargs) + + try: + tenant_id = self.request.user.tenant_id + all_rules = api_fwaas_v2.rule_list_for_tenant(request, tenant_id) + all_rules = sorted(all_rules, key=attrgetter('name_or_id')) + + available_rules = [r for r in all_rules] + + current_rules = [] + for x in kwargs['initial']['firewall_rules']: + r_obj = [rule for rule in all_rules if x == rule.id][0] + current_rules.append(r_obj) + + available_choices = [(r.id, r.name_or_id) for r in available_rules] + current_choices = [(r.id, r.name_or_id) for r in current_rules] + + except Exception as e: + msg = _('Failed to retrieve available rules: %s') % e + redirect = reverse(self.failure_url) + exceptions.handle(request, msg, redirect=redirect) + + self.fields['firewall_rule_id'].choices = available_choices + self.fields['insert_before'].choices = [('', _('-'))] + current_choices + self.fields['insert_after'].choices = [('', _('-'))] + current_choices + + def handle(self, request, context): + policy_id = self.initial['id'] + policy_name_or_id = self.initial['name'] or policy_id + try: + insert_rule_id = context['firewall_rule_id'] + insert_rule = api_fwaas_v2.rule_get(request, insert_rule_id) + body = {'firewall_rule_id': insert_rule_id, + 'insert_before': context['insert_before'], + 'insert_after': context['insert_after']} + policy = api_fwaas_v2.policy_insert_rule(request, policy_id, + **body) + msg = (_('Rule %(rule)s was successfully inserted to policy ' + '%(policy)s.') % + {'rule': insert_rule.name or insert_rule.id, + 'policy': policy_name_or_id}) + messages.success(request, msg) + return policy + except Exception as e: + msg = (_('Failed to insert rule to policy %(name)s: %(reason)s') % + {'name': policy_id, 'reason': e}) + redirect = reverse(self.failure_url) + exceptions.handle(request, msg, redirect=redirect) + + +class RemoveRuleFromPolicy(forms.SelfHandlingForm): + firewall_rule_id = forms.ThemableChoiceField(label=_("Remove Rule")) + + failure_url = 'horizon:project:firewalls_v2:index' + + def __init__(self, request, *args, **kwargs): + super(RemoveRuleFromPolicy, self).__init__(request, *args, **kwargs) + + try: + tenant_id = request.user.tenant_id + all_rules = api_fwaas_v2.rule_list_for_tenant(request, tenant_id) + + current_rules = [] + for r in kwargs['initial']['firewall_rules']: + r_obj = [rule for rule in all_rules if r == rule.id][0] + current_rules.append(r_obj) + + current_choices = [(r.id, r.name_or_id) for r in current_rules] + except Exception as e: + msg = (_('Failed to retrieve current rules in policy %(name)s: ' + '%(reason)s') % + {'name': self.initial['name'], 'reason': e}) + redirect = reverse(self.failure_url) + exceptions.handle(request, msg, redirect=redirect) + + self.fields['firewall_rule_id'].choices = current_choices + + def handle(self, request, context): + policy_id = self.initial['id'] + policy_name_or_id = self.initial['name'] or policy_id + try: + remove_rule_id = context['firewall_rule_id'] + remove_rule = api_fwaas_v2.rule_get(request, remove_rule_id) + body = {'firewall_rule_id': remove_rule_id} + policy = api_fwaas_v2.policy_remove_rule(request, policy_id, + **body) + msg = (_('Rule %(rule)s was successfully removed from policy ' + '%(policy)s.') % + {'rule': remove_rule.name or remove_rule.id, + 'policy': policy_name_or_id}) + messages.success(request, msg) + return policy + except Exception as e: + msg = (_('Failed to remove rule from policy %(name)s: %(reason)s') + % {'name': self.initial['name'], 'reason': e}) + redirect = reverse(self.failure_url) + exceptions.handle(request, msg, redirect=redirect) + + +def _get_request_body(context, initial_values): + body = {} + for key, value in context.items(): + # TODO(yushiro): Refactor after Q-2. + if key == 'port_id': + continue + if value != initial_values[key]: + body[key] = value + return body diff --git a/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/panel.py b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/panel.py new file mode 100644 index 0000000..9186ecd --- /dev/null +++ b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/panel.py @@ -0,0 +1,43 @@ +# Copyright 2017, Juniper Networks, 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 logging + +from django.utils.translation import ugettext_lazy as _ +import horizon +from openstack_dashboard.api import neutron + +LOG = logging.getLogger(__name__) + + +class Firewall_V2(horizon.Panel): + name = _("Firewall Groups") + slug = "firewalls_v2" + permissions = ('openstack.services.network',) + + def allowed(self, context): + request = context['request'] + if not request.user.has_perms(self.permissions): + return False + try: + if not neutron.is_extension_supported(request, 'fwaas_v2'): + return False + except Exception: + LOG.error("Call to list enabled services failed. This is likely " + "due to a problem communicating with the Neutron " + "endpoint. Firewall Groups panel will not be displayed.") + return False + if not super(Firewall_V2, self).allowed(context): + return False + return True diff --git a/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/tables.py b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/tables.py new file mode 100644 index 0000000..0a90bc1 --- /dev/null +++ b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/tables.py @@ -0,0 +1,425 @@ +# Copyright 2017, Juniper Networks, 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 logging + +from django.template import defaultfilters as filters +from django.urls import reverse +from django.utils.translation import pgettext_lazy +from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import ungettext_lazy + +from horizon import exceptions +from horizon import tables +from openstack_dashboard.dashboards.project.networks.ports \ + import tables as port_tables +from openstack_dashboard import policy + +from neutron_fwaas_dashboard.api import fwaas_v2 as api_fwaas_v2 + + +LOG = logging.getLogger(__name__) + + +class AddRuleLink(tables.LinkAction): + name = "addrule" + verbose_name = _("Add Rule") + url = "horizon:project:firewalls_v2:addrule" + classes = ("ajax-modal",) + icon = "plus" + policy_rules = (("neutron-fwaas", "create_fwaas_firewall_rule"),) + + +class AddPolicyLink(tables.LinkAction): + name = "addpolicy" + verbose_name = _("Add Policy") + url = "horizon:project:firewalls_v2:addpolicy" + classes = ("ajax-modal", "btn-addpolicy",) + icon = "plus" + policy_rules = (("neutron-fwaas", "create_fwaas_firewall_policy"),) + + +class AddFirewallGroupLink(tables.LinkAction): + name = "addfirewallgroup" + verbose_name = _("Create Firewall Group") + url = "horizon:project:firewalls_v2:addfirewallgroup" + classes = ("ajax-modal",) + icon = "plus" + policy_rules = (("neutron-fwaas", "create_fwaas_firewall_group"),) + + +class DeleteRuleLink(policy.PolicyTargetMixin, tables.DeleteAction): + name = "deleterule" + policy_rules = (("neutron-fwaas", "delete_fwaas_firewall_rule"),) + + @staticmethod + def action_present(count): + return ungettext_lazy( + u"Delete Rule", + u"Delete Rules", + count + ) + + @staticmethod + def action_past(count): + return ungettext_lazy( + u"Scheduled deletion of Rule", + u"Scheduled deletion of Rules", + count + ) + + def allowed(self, request, datum=None): + # TODO(Sarath Mekala): If the rule is associated with a policy then + # return false. + return True + + def delete(self, request, obj_id): + try: + api_fwaas_v2.rule_delete(request, obj_id) + except Exception as e: + exceptions.handle(request, _('Unable to delete rule. %s') % e) + + +class DeletePolicyLink(policy.PolicyTargetMixin, tables.DeleteAction): + name = "deletepolicy" + policy_rules = (("neutron-fwaas", "delete_fwaas_firewall_policy"),) + + @staticmethod + def action_present(count): + return ungettext_lazy( + u"Delete Policy", + u"Delete Policies", + count + ) + + @staticmethod + def action_past(count): + return ungettext_lazy( + u"Scheduled deletion of Policy", + u"Scheduled deletion of Policies", + count + ) + + def delete(self, request, obj_id): + try: + api_fwaas_v2.policy_delete(request, obj_id) + except Exception as e: + exceptions.handle(request, _('Unable to delete policy. %s') % e) + + +class DeleteFirewallGroupLink(policy.PolicyTargetMixin, + tables.DeleteAction): + name = "deletefirewallgroup" + policy_rules = (("neutron-fwaas", "delete_fwaas_firewall_group"),) + + @staticmethod + def action_present(count): + return ungettext_lazy( + u"Delete Firewall Group", + u"Delete Firewall Groups", + count + ) + + @staticmethod + def action_past(count): + return ungettext_lazy( + u"Scheduled deletion of Firewall Group", + u"Scheduled deletion of Firewall Groups", + count + ) + + def delete(self, request, obj_id): + try: + api_fwaas_v2.firewall_group_delete(request, obj_id) + except Exception as e: + exceptions.handle(request, + _('Unable to delete firewall group. %s') % e) + + +class UpdateRuleLink(policy.PolicyTargetMixin, tables.LinkAction): + name = "updaterule" + verbose_name = _("Edit Rule") + classes = ("ajax-modal", "btn-update",) + policy_rules = (("neutron-fwaas", "update_fwaas_firewall_rule"),) + + def get_link_url(self, rule): + return reverse("horizon:project:firewalls_v2:updaterule", + kwargs={'rule_id': rule.id}) + + +class UpdatePolicyLink(policy.PolicyTargetMixin, tables.LinkAction): + name = "updatepolicy" + verbose_name = _("Edit Policy") + classes = ("ajax-modal", "btn-update",) + policy_rules = (("neutron-fwaas", "update_fwaas_firewall_policy"),) + + def get_link_url(self, policy): + return reverse("horizon:project:firewalls_v2:updatepolicy", + kwargs={'policy_id': policy.id}) + + +class UpdateFirewallGroupLink(policy.PolicyTargetMixin, tables.LinkAction): + name = "updatefirewall" + verbose_name = _("Edit Firewall Group") + classes = ("ajax-modal", "btn-update",) + policy_rules = (("neutron-fwaas", "update_firewall"),) + + def get_link_url(self, firewallgroup): + return reverse("horizon:project:firewalls_v2:updatefirewall", + kwargs={'firewall_id': firewallgroup.id}) + + def allowed(self, request, firewallgroup): + return firewallgroup.status not in ( + "PENDING_CREATE", + "PENDING_UPDATE", + "PENDING_DELETE") + + +class InsertRuleToPolicyLink(policy.PolicyTargetMixin, + tables.LinkAction): + name = "insertrule" + verbose_name = _("Insert Rule") + classes = ("ajax-modal", "btn-update",) + policy_rules = (("neutron-fwaas", "show_fwaas_firewall_policy"), + ("neutron-fwaas", "insert_rule_fwaas_firewall_policy"),) + + def get_link_url(self, policy): + return reverse("horizon:project:firewalls_v2:insertrule", + kwargs={'policy_id': policy.id}) + + +class RemoveRuleFromPolicyLink(policy.PolicyTargetMixin, + tables.LinkAction): + name = "removerule" + verbose_name = _("Remove Rule") + classes = ("ajax-modal",) + policy_rules = (("neutron-fwaas", "show_fwaas_firewall_policy"), + ("neutron-fwaas", "firewall_policy_remove_rule"),) + action_type = "danger" + + def get_link_url(self, policy): + return reverse("horizon:project:firewalls_v2:removerule", + kwargs={'policy_id': policy.id}) + + def allowed(self, request, policy): + return bool(policy.rules) + + +class AddPortToFirewallGroupLink(policy.PolicyTargetMixin, + tables.LinkAction): + name = "addport" + verbose_name = _("Add Port") + classes = ("ajax-modal", "btn-update",) + policy_rules = (("neutron-fwaas", "update_fwaas_firewall_group"),) + + def get_link_url(self, firewallgroup): + return reverse("horizon:project:firewalls_v2:addport", + kwargs={'firewallgroup_id': firewallgroup.id}) + + def allowed(self, request, firewallgroup): + return firewallgroup.status not in ( + "PENDING_CREATE", + "PENDING_UPDATE", + "PENDING_DELETE") + + +class RemovePortFromFirewallGroupLink(policy.PolicyTargetMixin, + tables.LinkAction): + name = "removeport" + verbose_name = _("Remove Port") + classes = ("ajax-modal", "btn-update",) + policy_rules = (("neutron-fwaas", "update_fwaas_firewall_group"),) + + def get_link_url(self, firewallgroup): + return reverse("horizon:project:firewalls_v2:removeport", + kwargs={'firewallgroup_id': firewallgroup.id}) + + def allowed(self, request, firewallgroup): + return firewallgroup.status not in ( + "PENDING_CREATE", + "PENDING_UPDATE", + "PENDING_DELETE") + + +def get_rules_name(datum): + return ', '.join([rule.name_or_id for rule in datum.rules]) + + +def get_ports_name(datum): + return len(datum.ports) + + +def get_ports_link(datum): + url = reverse("horizon:project:firewalls_v2:firewallgroupdetails", + args=(datum.id,)) + return '%s?tab=%s__%s' % (url, 'firewallgrouptabs', 'ports_tab') + + +def get_ingress_policy_link(datum): + if datum.ingress_firewall_policy_id: + return reverse('horizon:project:firewalls_v2:policydetails', + kwargs={'policy_id': datum.ingress_firewall_policy_id}) + + +def get_egress_policy_link(datum): + if datum.egress_firewall_policy_id: + return reverse('horizon:project:firewalls_v2:policydetails', + kwargs={'policy_id': datum.egress_firewall_policy_id}) + + +def get_ingress_policy_name(datum): + if datum.ingress_firewall_policy_id: + return datum.ingress_policy.name + + +def get_egress_policy_name(datum): + if datum.egress_firewall_policy_id: + return datum.egress_policy.name + + +class RulesTable(tables.DataTable): + ACTION_DISPLAY_CHOICES = ( + ("Allow", pgettext_lazy("Action Name of a Firewall Rule", u"ALLOW")), + ("Deny", pgettext_lazy("Action Name of a Firewall Rule", u"DENY")), + ("Reject", pgettext_lazy("Action Name of a Firewall Rule", u"REJECT")), + ) + name = tables.Column("name_or_id", + verbose_name=_("Name"), + link="horizon:project:firewalls_v2:ruledetails") + description = tables.Column('description', verbose_name=_('Description')) + ip_version = tables.Column('ip_version', verbose_name=('IP Version')) + protocol = tables.Column("protocol", + filters=(lambda v: filters.default(v, _("ANY")), + filters.upper,), + verbose_name=_("Protocol")) + source_ip_address = tables.Column("source_ip_address", + verbose_name=_("Source IP")) + source_port = tables.Column("source_port", + verbose_name=_("Source Port")) + destination_ip_address = tables.Column("destination_ip_address", + verbose_name=_("Destination IP")) + destination_port = tables.Column("destination_port", + verbose_name=_("Destination Port")) + action = tables.Column("action", + display_choices=ACTION_DISPLAY_CHOICES, + verbose_name=_("Action")) + shared = tables.Column("shared", + verbose_name=_("Shared"), + filters=(filters.yesno, filters.capfirst)) + enabled = tables.Column("enabled", + verbose_name=_("Enabled"), + filters=(filters.yesno, filters.capfirst)) + + class Meta(object): + name = "rulestable" + verbose_name = _("Rules") + table_actions = (AddRuleLink, + DeleteRuleLink, + tables.NameFilterAction) + row_actions = (UpdateRuleLink, DeleteRuleLink) + + +class PoliciesTable(tables.DataTable): + name = tables.Column("name_or_id", + verbose_name=_("Name"), + link="horizon:project:firewalls_v2:policydetails") + description = tables.Column('description', verbose_name=_('Description')) + firewall_rules = tables.Column(get_rules_name, + verbose_name=_("Rules")) + shared = tables.Column("shared", + verbose_name=_("Shared"), + filters=(filters.yesno, filters.capfirst)) + audited = tables.Column("audited", + verbose_name=_("Audited"), + filters=(filters.yesno, filters.capfirst)) + + class Meta(object): + name = "policiestable" + verbose_name = _("Policies") + table_actions = (AddPolicyLink, + DeletePolicyLink, + tables.NameFilterAction) + row_actions = (UpdatePolicyLink, InsertRuleToPolicyLink, + RemoveRuleFromPolicyLink, DeletePolicyLink) + + +class FirewallGroupsTable(tables.DataTable): + STATUS_DISPLAY_CHOICES = ( + ("Active", pgettext_lazy("Current status of a Firewall Group", + u"Active")), + ("Down", pgettext_lazy("Current status of a Firewall Group", + u"Down")), + ("Error", pgettext_lazy("Current status of a Firewall Group", + u"Error")), + ("Created", pgettext_lazy("Current status of a Firewall Group", + u"Created")), + ("Pending_Create", pgettext_lazy("Current status of a Firewall Group", + u"Pending Create")), + ("Pending_Update", pgettext_lazy("Current status of a Firewall Group", + u"Pending Update")), + ("Pending_Delete", pgettext_lazy("Current status of a Firewall Group", + u"Pending Delete")), + ("Inactive", pgettext_lazy("Current status of a Firewall Group", + u"Inactive")), + ) + ADMIN_STATE_DISPLAY_CHOICES = ( + ("UP", pgettext_lazy("Admin state of a Firewall Group", u"UP")), + ("DOWN", pgettext_lazy("Admin state of a Firewall Group", u"DOWN")), + ) + + name = tables.Column( + "name_or_id", + verbose_name=_("Name"), + link="horizon:project:firewalls_v2:firewallgroupdetails") + description = tables.Column('description', verbose_name=_('Description')) + ingress_firewall_policy_id = tables.Column( + get_ingress_policy_name, + link=get_ingress_policy_link, + verbose_name=_("Ingress Policy")) + egress_firewall_policy_id = tables.Column(get_egress_policy_name, + link=get_egress_policy_link, + verbose_name=_("Egress Policy")) + ports = tables.Column(get_ports_name, + link=get_ports_link, + verbose_name=_("Ports")) + + status = tables.Column("status", + verbose_name=_("Status"), + display_choices=STATUS_DISPLAY_CHOICES) + admin_state_up = tables.Column("admin_state_up", + verbose_name=_("Admin State")) + shared = tables.Column("shared", + verbose_name=_("Shared"), + filters=(filters.yesno, filters.capfirst)) + + class Meta(object): + name = "FirewallGroupsTable" + verbose_name = _("Firewall Groups") + table_actions = (AddFirewallGroupLink, + DeleteFirewallGroupLink, + tables.NameFilterAction) + row_actions = ( + UpdateFirewallGroupLink, + DeleteFirewallGroupLink, + AddPortToFirewallGroupLink, + RemovePortFromFirewallGroupLink) + + +class FirewallGroupPortsTable(port_tables.PortsTable): + + class Meta(object): + name = 'ports' + verbose_name = _('Ports') + table_actions = [] + row_actions = [] diff --git a/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/tabs.py b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/tabs.py new file mode 100644 index 0000000..f462e3b --- /dev/null +++ b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/tabs.py @@ -0,0 +1,165 @@ +# Copyright 2017, Juniper Networks, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from django.utils.translation import ugettext_lazy as _ + +from horizon import exceptions +from horizon import tabs +from openstack_dashboard.api import neutron as api_neutron + +from neutron_fwaas_dashboard.api import fwaas_v2 as api_fwaas_v2 +from neutron_fwaas_dashboard.dashboards.project.firewalls_v2 import tables + +FirewallGroupsTable = tables.FirewallGroupsTable +PoliciesTable = tables.PoliciesTable +RulesTable = tables.RulesTable + + +class RulesTab(tabs.TableTab): + table_classes = (RulesTable,) + name = _("Firewall Rules") + slug = "rules" + template_name = "horizon/common/_detail_table.html" + + def get_rulestable_data(self): + try: + tenant_id = self.request.user.tenant_id + request = self.tab_group.request + rules = api_fwaas_v2.rule_list_for_tenant(request, tenant_id) + except Exception: + rules = [] + exceptions.handle(self.tab_group.request, + _('Unable to retrieve rules list.')) + + return rules + + +class PoliciesTab(tabs.TableTab): + table_classes = (PoliciesTable,) + name = _("Firewall Policies") + slug = "policies" + template_name = "horizon/common/_detail_table.html" + + def get_policiestable_data(self): + try: + tenant_id = self.request.user.tenant_id + request = self.tab_group.request + policies = api_fwaas_v2.policy_list_for_tenant(request, tenant_id) + except Exception: + policies = [] + exceptions.handle(self.tab_group.request, + _('Unable to retrieve policies list.')) + + return policies + + +class FirewallGroupsTab(tabs.TableTab): + table_classes = (FirewallGroupsTable,) + name = _("Firewall Groups") + slug = "firewallgroups" + template_name = "horizon/common/_detail_table.html" + + def get_policy_dict(self, policies): + return dict((policy.id, policy) for policy in policies) + + def get_FirewallGroupsTable_data(self): + try: + tenant_id = self.request.user.tenant_id + request = self.tab_group.request + fw_groups = api_fwaas_v2.firewall_group_list_for_tenant(request, + tenant_id) + tenant_policies = api_fwaas_v2.policy_list_for_tenant( + request, tenant_id) + policy_dict = self.get_policy_dict(policies=tenant_policies) + for fw_group in fw_groups: + if fw_group['ingress_firewall_policy_id'] in policy_dict: + fw_group.ingress_policy = \ + policy_dict[fw_group['ingress_firewall_policy_id']] + if fw_group['egress_firewall_policy_id'] in policy_dict: + fw_group.egress_policy = \ + policy_dict[fw_group['egress_firewall_policy_id']] + except Exception: + fw_groups = [] + exceptions.handle(self.tab_group.request, + _('Unable to retrieve firewall group list.')) + + return fw_groups + + +class RuleDetailsTab(tabs.Tab): + name = _("Rule") + slug = "ruledetails" + template_name = "project/firewalls_v2/_rule_details.html" + + def get_context_data(self, request): + return {"rule": self.tab_group.kwargs['rule']} + + +class PolicyDetailsTab(tabs.Tab): + name = _("Policy") + slug = "policydetails" + template_name = "project/firewalls_v2/_policy_details.html" + + def get_context_data(self, request): + return {"policy": self.tab_group.kwargs['policy']} + + +class FirewallGroupDetailsTab(tabs.Tab): + name = _("FirewallGroup") + slug = "firewallgroupdetails" + template_name = "project/firewalls_v2/_firewallgroup_details.html" + + def get_context_data(self, request): + return {"firewall_group": self.tab_group.kwargs['firewallgroup']} + + +class FirewallGroupPortsTab(tabs.TableTab): + name = _("Ports") + slug = "ports_tab" + table_classes = (tables.FirewallGroupPortsTable,) + template_name = ("horizon/common/_detail_table.html") + preload = False + + def get_ports_data(self): + port_ids = self.tab_group.kwargs['firewallgroup']['ports'] + if not port_ids: + return [] + try: + ports = api_neutron.port_list(self.request, id=port_ids) + except Exception: + ports = [] + msg = _('Failed to retrieve port list of the firewall group.') + exceptions.handle(self.request, msg) + return ports + + +class FirewallGroupTabs(tabs.TabGroup): + slug = "fwtabs" + tabs = (FirewallGroupsTab, PoliciesTab, RulesTab) + sticky = True + + +class RuleDetailsTabs(tabs.DetailTabsGroup): + slug = "ruletabs" + tabs = (RuleDetailsTab,) + + +class PolicyDetailsTabs(tabs.DetailTabsGroup): + slug = "policytabs" + tabs = (PolicyDetailsTab,) + + +class FirewallGroupDetailsTabs(tabs.DetailTabsGroup): + slug = "firewallgrouptabs" + tabs = (FirewallGroupDetailsTab, FirewallGroupPortsTab) diff --git a/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_addport.html b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_addport.html new file mode 100644 index 0000000..95882c1 --- /dev/null +++ b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_addport.html @@ -0,0 +1,7 @@ +{% extends "horizon/common/_modal_form.html" %} +{% load i18n %} + +{% block modal-body-right %} +

{% trans "Description:" %}

+

{% trans "You may add ports to firewall group here." %}

+{% endblock %} diff --git a/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_firewallgroup_details.html b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_firewallgroup_details.html new file mode 100644 index 0000000..70ef47f --- /dev/null +++ b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_firewallgroup_details.html @@ -0,0 +1,45 @@ +{% load i18n sizeformat parse_date %} + +
+
+
{% trans "Name" %}
+
{{ firewall_group.name|default:_("-") }}
+ +
{% trans "Description" %}
+
{{ firewall_group.description|default:_("-") }}
+ +
{% trans "ID" %}
+
{{ firewall_group.id }}
+ +
{% trans "Project ID" %}
+
{{ firewall_group.tenant_id }}
+ +
{% trans "Ingress Policy ID" %}
+
+ {% if firewall_group.ingress_firewall_policy_id %} + {% url 'horizon:project:firewalls_v2:policydetails' firewall_group.ingress_firewall_policy_id as policy_url %} + {{firewall_group.ingress_policy.name|default:firewall_group.ingress_firewall_policy_id }} + {% else %} + {% trans "-" %} + {% endif %} +
+
{% trans "Egress Policy ID" %}
+
+ {% if firewall_group.egress_firewall_policy_id %} + {% url 'horizon:project:firewalls_v2:policydetails' firewall_group.egress_firewall_policy_id as policy_url %} + {{firewall_group.egress_policy.name|default:firewall_group.egress_firewall_policy_id }} + {% else %} + {% trans "-" %} + {% endif %} +
+ +
{% trans "Status" %}
+
{{ firewall_group.status }}
+ +
{% trans "Admin State Up" %}
+
{{ firewall_group.admin_state_up}}
+ +
{% trans "Shared" %}
+
{{ firewall_group.shared|yesno|capfirst }}
+
+
diff --git a/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_insert_rule_to_policy.html b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_insert_rule_to_policy.html new file mode 100644 index 0000000..85c27fe --- /dev/null +++ b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_insert_rule_to_policy.html @@ -0,0 +1,12 @@ +{% extends "horizon/common/_modal_form.html" %} +{% load i18n %} + +{% block modal-body-right %} +

{% trans "Description:" %}

+

{% blocktrans trimmed %} + Choose the rule you want to insert. + Specify either the rule you want to insert immediately before, + or the rule to insert immediately after. + If both are specified, the prior takes precedence. + {% endblocktrans %}

+{% endblock %} diff --git a/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_policy_details.html b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_policy_details.html new file mode 100644 index 0000000..da53c80 --- /dev/null +++ b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_policy_details.html @@ -0,0 +1,37 @@ +{% load i18n sizeformat parse_date %} + +
+
+
{% trans "Name" %}
+
{{ policy.name|default:_("-") }}
+ +
{% trans "Description" %}
+
{{ policy.description|default:_("-") }}
+ +
{% trans "ID" %}
+
{{ policy.id }}
+ +
{% trans "Project ID" %}
+
{{ policy.tenant_id }}
+ +
{% trans "Rules" %}
+
+ {% if policy.rules %} +
    + {% for rule in policy.rules %} + {% url 'horizon:project:firewalls_v2:ruledetails' rule.id as rule_url %} +
  1. {{ rule.name|default:rule.id }}
  2. + {% endfor %} +
+ {% else %} + {% trans "-" %} + {% endif %} +
+ +
{% trans "Shared" %}
+
{{ policy.shared|yesno|capfirst }}
+ +
{% trans "Audited" %}
+
{{ policy.audited|yesno|capfirst }}
+
+
diff --git a/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_remove_rule_from_policy.html b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_remove_rule_from_policy.html new file mode 100644 index 0000000..09a76d7 --- /dev/null +++ b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_remove_rule_from_policy.html @@ -0,0 +1,7 @@ +{% extends "horizon/common/_modal_form.html" %} +{% load i18n %} + +{% block modal-body-right %} +

{% trans "Description:" %}

+

{% trans "Choose the rule you want to remove." %}

+{% endblock %} diff --git a/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_removeport.html b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_removeport.html new file mode 100644 index 0000000..a115dc3 --- /dev/null +++ b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_removeport.html @@ -0,0 +1,7 @@ +{% extends "horizon/common/_modal_form.html" %} +{% load i18n %} + +{% block modal-body-right %} +

{% trans "Description:" %}

+

{% trans "You may remove ports from firewall group here." %}

+{% endblock %} diff --git a/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_rule_details.html b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_rule_details.html new file mode 100644 index 0000000..323f38e --- /dev/null +++ b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_rule_details.html @@ -0,0 +1,44 @@ +{% load i18n sizeformat parse_date %} + +
+
+
{% trans "Name" %}
+
{{ rule.name|default:_("-") }}
+ +
{% trans "Description" %}
+
{{ rule.description|default:_("-") }}
+ +
{% trans "ID" %}
+
{{ rule.id }}
+ +
{% trans "Project ID" %}
+
{{ rule.tenant_id }}
+ +
{% trans "Action" %}
+
{{ rule.action|upper }}
+ +
{% trans "IP Version" %}
+
{{ rule.ip_version }}
+ +
{% trans "Protocol" %}
+
{{ rule.protocol|default:_("ANY")|upper }}
+ +
{% trans "Source IP Address" %}
+
{{ rule.source_ip_address|default:_("ANY") }}
+ +
{% trans "Source Port" %}
+
{{ rule.source_port|default:_("ANY") }}
+ +
{% trans "Destination IP Address" %}
+
{{ rule.destination_ip_address|default:_("ANY") }}
+ +
{% trans "Destination Port"%}
+
{{ rule.destination_port|default:_("ANY") }}
+ +
{% trans "Shared" %}
+
{{ rule.shared|yesno|capfirst }}
+ +
{% trans "Enabled" %}
+
{{ rule.enabled|yesno|capfirst }}
+
+
diff --git a/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_update_port_help.html b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_update_port_help.html new file mode 100644 index 0000000..a648894 --- /dev/null +++ b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_update_port_help.html @@ -0,0 +1,3 @@ +{% load i18n %} + +

{% blocktrans %}Choose port(s) from Available Ports. {% endblocktrans %}

diff --git a/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_update_ports.html b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_update_ports.html new file mode 100644 index 0000000..c2f5dd9 --- /dev/null +++ b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_update_ports.html @@ -0,0 +1,35 @@ +{% load i18n %} + + +
+
+

{% trans "Selected Ports" %}

+
    +

    {% trans "Available Ports" %}

    +
      +
      +
      + {% include "project/firewalls_v2/_update_port_help.html" %} +
      +
      + +
      +
      +
      + {% include "horizon/common/_form_fields.html" %} +
      +
      +
      + {{ step.get_help_text }} +
      +
      + + diff --git a/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_update_rule_help.html b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_update_rule_help.html new file mode 100644 index 0000000..4a0b232 --- /dev/null +++ b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_update_rule_help.html @@ -0,0 +1,6 @@ +{% load i18n horizon %} + +

      {% blocktrans trimmed %} +Choose rule(s) from Available Rules to Selected Rule by push button or +drag and drop, you may change their order by drag and drop as well. +{% endblocktrans %}

      diff --git a/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_update_rules.html b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_update_rules.html new file mode 100644 index 0000000..371c081 --- /dev/null +++ b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_update_rules.html @@ -0,0 +1,35 @@ +{% load i18n %} + + +
      +
      +

      {% trans "Selected Rules" %}

      +
        +

        {% trans "Available Rules" %}

        +
          +
          +
          + {% include "project/firewalls_v2/_update_rule_help.html" %} +
          +
          + +
          +
          +
          + {% include "horizon/common/_form_fields.html" %} +
          +
          +
          + {{ step.get_help_text }} +
          +
          + + diff --git a/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_updatefirewall.html b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_updatefirewall.html new file mode 100644 index 0000000..af91d88 --- /dev/null +++ b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_updatefirewall.html @@ -0,0 +1,7 @@ +{% extends "horizon/common/_modal_form.html" %} +{% load i18n %} + +{% block modal-body-right %} +

          {% trans "Description:" %}

          +

          {% trans "You may update firewall group details here." %}

          +{% endblock %} diff --git a/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_updatepolicy.html b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_updatepolicy.html new file mode 100644 index 0000000..12d9692 --- /dev/null +++ b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_updatepolicy.html @@ -0,0 +1,10 @@ +{% extends "horizon/common/_modal_form.html" %} +{% load i18n %} + +{% block modal-body-right %} +

          {% trans "Description:" %}

          +

          {% blocktrans trimmed %} + You may update policy details here. + Use 'Insert Rule' or 'Remove Rule' links instead to insert or remove a rule. + {% endblocktrans %}

          +{% endblock %} diff --git a/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_updaterule.html b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_updaterule.html new file mode 100644 index 0000000..b2b8104 --- /dev/null +++ b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/_updaterule.html @@ -0,0 +1,7 @@ +{% extends "horizon/common/_modal_form.html" %} +{% load i18n %} + +{% block modal-body-right %} +

          {% trans "Description:" %}

          +

          {% trans "You may update rule details here." %}

          +{% endblock %} diff --git a/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/addfirewallgroup.html b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/addfirewallgroup.html new file mode 100644 index 0000000..194d419 --- /dev/null +++ b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/addfirewallgroup.html @@ -0,0 +1,7 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Add New Firewall Group" %}{% endblock %} + +{% block main %} + {% include 'horizon/common/_workflow.html' %} +{% endblock %} diff --git a/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/addpolicy.html b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/addpolicy.html new file mode 100644 index 0000000..24b1dfa --- /dev/null +++ b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/addpolicy.html @@ -0,0 +1,7 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Add New Policy" %}{% endblock %} + +{% block main %} + {% include 'horizon/common/_workflow.html' %} +{% endblock %} diff --git a/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/addport.html b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/addport.html new file mode 100644 index 0000000..35623a1 --- /dev/null +++ b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/addport.html @@ -0,0 +1,7 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Add Port to Firewall Group" %}{% endblock %} + +{% block main %} + {% include 'project/firewalls_v2/_addport.html' %} +{% endblock %} diff --git a/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/addrule.html b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/addrule.html new file mode 100644 index 0000000..4efa561 --- /dev/null +++ b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/addrule.html @@ -0,0 +1,7 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Add New Rule" %}{% endblock %} + +{% block main %} + {% include 'horizon/common/_workflow.html' %} +{% endblock %} diff --git a/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/details_tabs.html b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/details_tabs.html new file mode 100644 index 0000000..9eb9f71 --- /dev/null +++ b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/details_tabs.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Firewall Groups" %}{% endblock %} + +{% block main %} +
          +
          + {{ tab_group.render }} +
          +
          +{% endblock %} diff --git a/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/insert_rule_to_policy.html b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/insert_rule_to_policy.html new file mode 100644 index 0000000..5a5afdc --- /dev/null +++ b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/insert_rule_to_policy.html @@ -0,0 +1,7 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Insert Rule to Policy" %}{% endblock %} + +{% block main %} + {% include 'project/firewalls_v2/_insert_rule_to_policy.html' %} +{% endblock %} diff --git a/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/remove_rule_from_policy.html b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/remove_rule_from_policy.html new file mode 100644 index 0000000..fe17aa5 --- /dev/null +++ b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/remove_rule_from_policy.html @@ -0,0 +1,7 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Remove Rule from Policy" %}{% endblock %} + +{% block main %} + {% include 'project/firewalls_v2/_remove_rule_from_policy.html' %} +{% endblock %} diff --git a/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/removeport.html b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/removeport.html new file mode 100644 index 0000000..8bbb5d9 --- /dev/null +++ b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/removeport.html @@ -0,0 +1,7 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Remove Port from Firewall Group" %}{% endblock %} + +{% block main %} + {% include 'project/firewalls_v2/_removeport.html' %} +{% endblock %} diff --git a/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/table_select.html b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/table_select.html new file mode 100644 index 0000000..acf93e4 --- /dev/null +++ b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/table_select.html @@ -0,0 +1,94 @@ +{% load horizon %} + +{% minifyspace %} + +{% endminifyspace %} diff --git a/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/updatefirewall.html b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/updatefirewall.html new file mode 100644 index 0000000..f299315 --- /dev/null +++ b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/updatefirewall.html @@ -0,0 +1,7 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Edit Firewall Group" %}{% endblock %} + +{% block main %} + {% include 'project/firewalls_v2/_updatefirewall.html' %} +{% endblock %} diff --git a/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/updatepolicy.html b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/updatepolicy.html new file mode 100644 index 0000000..a3c69a4 --- /dev/null +++ b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/updatepolicy.html @@ -0,0 +1,7 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Edit Policy" %}{% endblock %} + +{% block main %} + {% include 'project/firewalls_v2/_updatepolicy.html' %} +{% endblock %} diff --git a/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/updaterule.html b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/updaterule.html new file mode 100644 index 0000000..d16c1e2 --- /dev/null +++ b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/templates/firewalls_v2/updaterule.html @@ -0,0 +1,7 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Edit Rule" %}{% endblock %} + +{% block main %} + {% include 'project/firewalls_v2/_updaterule.html' %} +{% endblock %} diff --git a/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/tests.py b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/tests.py new file mode 100644 index 0000000..605e06a --- /dev/null +++ b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/tests.py @@ -0,0 +1,798 @@ +# Copyright 2017, Juniper Networks, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from unittest import mock + +from django.urls import reverse + +from openstack_dashboard.test import helpers + +from neutron_fwaas_dashboard.api import fwaas_v2 as api_fwaas_v2 +from neutron_fwaas_dashboard.test import helpers as test + + +class FirewallTests(test.TestCase): + + INDEX_URL = reverse('horizon:project:firewalls_v2:index') + + ADDRULE_PATH = 'horizon:project:firewalls_v2:addrule' + ADDPOLICY_PATH = 'horizon:project:firewalls_v2:addpolicy' + ADDFIREWALLGROUP_PATH = 'horizon:project:firewalls_v2:addfirewallgroup' + + RULE_DETAIL_PATH = 'horizon:project:firewalls_v2:ruledetails' + POLICY_DETAIL_PATH = 'horizon:project:firewalls_v2:policydetails' + FIREWALLGROUP_DETAIL_PATH = \ + 'horizon:project:firewalls_v2:firewallgroupdetails' + + UPDATERULE_PATH = 'horizon:project:firewalls_v2:updaterule' + UPDATEPOLICY_PATH = 'horizon:project:firewalls_v2:updatepolicy' + UPDATEFIREWALLGROUP_PATH = 'horizon:project:firewalls_v2:updatefirewall' + + INSERTRULE_PATH = 'horizon:project:firewalls_v2:insertrule' + REMOVERULE_PATH = 'horizon:project:firewalls_v2:removerule' + + ADDPORT_PATH = 'horizon:project:firewalls_v2:addport' + REMOVEPORT_PATH = 'horizon:project:firewalls_v2:removeport' + + def setup_mocks(self): + firewallgroups = self.firewall_groups_v2.list() + self.mock_firewall_group_list_for_tenant.return_value = firewallgroups + policies = self.fw_policies_v2.list() + self.mock_policy_list_for_tenant.return_value = policies + self.mock_rule_list_for_tenant.return_value = self.fw_rules_v2.list() + + def check_mocks(self): + tenant_id = self.tenant.id + + self.mock_firewall_group_list_for_tenant.assert_called_once_with( + helpers.IsHttpRequest(), tenant_id) + # TODO(amotoki): get_firewallgroupstable_data() also calls + # policy_list_for_tenant(). This needs to be clean up. + self.assert_mock_multiple_calls_with_same_arguments( + self.mock_policy_list_for_tenant, 2, + mock.call(helpers.IsHttpRequest(), tenant_id)) + self.mock_rule_list_for_tenant.assert_called_once_with( + helpers.IsHttpRequest(), tenant_id) + + def setup_mocks_with_exception(self): + self.mock_rule_list_for_tenant.side_effect = self.exceptions.neutron + self.mock_policy_list_for_tenant.side_effect = self.exceptions.neutron + self.mock_firewall_group_list_for_tenant.side_effect = \ + self.exceptions.neutron + + def check_mocks_with_exception(self): + tenant_id = self.tenant.id + self.mock_rule_list_for_tenant.assert_called_once_with( + helpers.IsHttpRequest(), tenant_id) + self.mock_policy_list_for_tenant.assert_called_once_with( + helpers.IsHttpRequest(), tenant_id) + self.mock_firewall_group_list_for_tenant.assert_called_once_with( + helpers.IsHttpRequest(), tenant_id) + + @helpers.create_mocks({api_fwaas_v2: ('firewall_group_list_for_tenant', + 'policy_list_for_tenant', + 'rule_list_for_tenant',)}) + def test_index_firewallgroups(self): + self.setup_mocks() + + tenant_id = self.tenant.id + + res = self.client.get(self.INDEX_URL, tenant_id=tenant_id) + + self.assertTemplateUsed(res, 'project/firewalls_v2/details_tabs.html') + self.assertTemplateUsed(res, 'horizon/common/_detail_table.html') + self.assertEqual(len(res.context['table'].data), + len(self.firewall_groups_v2.list())) + self.check_mocks() + + @helpers.create_mocks({api_fwaas_v2: ('firewall_group_list_for_tenant', + 'policy_list_for_tenant', + 'rule_list_for_tenant',)}) + def test_index_policies(self): + self.setup_mocks() + + tenant_id = self.tenant.id + + res = self.client.get(self.INDEX_URL + '?tab=fwtabs__policies', + tenant_id=tenant_id) + + self.assertTemplateUsed(res, 'project/firewalls_v2/details_tabs.html') + self.assertTemplateUsed(res, 'horizon/common/_detail_table.html') + self.assertEqual(len(res.context['policiestable_table'].data), + len(self.fw_policies_v2.list())) + self.check_mocks() + + @helpers.create_mocks({api_fwaas_v2: ('firewall_group_list_for_tenant', + 'policy_list_for_tenant', + 'rule_list_for_tenant',)}) + def test_index_rules(self): + self.setup_mocks() + + tenant_id = self.tenant.id + + res = self.client.get(self.INDEX_URL + '?tab=fwtabs__rules', + tenant_id=tenant_id) + + self.assertTemplateUsed(res, 'project/firewalls_v2/details_tabs.html') + self.assertTemplateUsed(res, 'horizon/common/_detail_table.html') + self.assertEqual(len(res.context['rulestable_table'].data), + len(self.fw_rules_v2.list())) + self.check_mocks() + + @helpers.create_mocks({api_fwaas_v2: ('firewall_group_list_for_tenant', + 'policy_list_for_tenant', + 'rule_list_for_tenant')}) + def test_index_exception_firewallgroups(self): + self.setup_mocks_with_exception() + + tenant_id = self.tenant.id + + res = self.client.get(self.INDEX_URL, tenant_id=tenant_id) + + self.assertTemplateUsed(res, 'project/firewalls_v2/details_tabs.html') + self.assertTemplateUsed(res, 'horizon/common/_detail_table.html') + self.assertEqual(len(res.context['table'].data), 0) + + self.check_mocks_with_exception() + + @helpers.create_mocks({api_fwaas_v2: ('firewall_group_list_for_tenant', + 'policy_list_for_tenant', + 'rule_list_for_tenant')}) + def test_index_exception_policies(self): + self.setup_mocks_with_exception() + + tenant_id = self.tenant.id + + res = self.client.get(self.INDEX_URL + '?tab=fwtabs__policies', + tenant_id=tenant_id) + + self.assertTemplateUsed(res, 'project/firewalls_v2/details_tabs.html') + self.assertTemplateUsed(res, + 'horizon/common/_detail_table.html') + self.assertEqual(len(res.context['policiestable_table'].data), 0) + + self.check_mocks_with_exception() + + @helpers.create_mocks({api_fwaas_v2: ('firewall_group_list_for_tenant', + 'policy_list_for_tenant', + 'rule_list_for_tenant')}) + def test_index_exception_rules(self): + self.setup_mocks_with_exception() + + tenant_id = self.tenant.id + + res = self.client.get(self.INDEX_URL + '?tab=fwtabs__rules', + tenant_id=tenant_id) + + self.assertTemplateUsed(res, 'project/firewalls_v2/details_tabs.html') + self.assertTemplateUsed(res, 'horizon/common/_detail_table.html') + self.assertEqual(len(res.context['rulestable_table'].data), 0) + + self.check_mocks_with_exception() + + @helpers.create_mocks({api_fwaas_v2: ('rule_create',), }) + def test_add_rule_post(self): + rule1 = self.fw_rules_v2.first() + + form_data = {'name': rule1.name, + 'description': rule1.description, + 'protocol': rule1.protocol, + 'action': rule1.action, + 'source_ip_address': rule1.source_ip_address, + 'source_port': rule1.source_port, + 'destination_ip_address': rule1.destination_ip_address, + 'destination_port': rule1.destination_port, + 'shared': rule1.shared, + 'enabled': rule1.enabled, + 'ip_version': rule1.ip_version + } + + self.mock_rule_create.return_value = rule1 + + res = self.client.post(reverse(self.ADDRULE_PATH), form_data) + + self.assertNoFormErrors(res) + self.assertRedirectsNoFollow(res, str(self.INDEX_URL)) + + self.mock_rule_create.assert_called_once_with( + helpers.IsHttpRequest(), **form_data) + + @helpers.create_mocks({api_fwaas_v2: ('rule_create',), }) + def test_add_rule_post_src_None(self): + rule1 = self.fw_rules_v2.first() + form_data = {'name': rule1.name, + 'description': rule1.description, + 'protocol': rule1.protocol, + 'action': rule1.action, + 'source_ip_address': '', + 'destination_ip_address': rule1.destination_ip_address, + 'source_port': '', + 'destination_port': rule1.destination_port, + 'shared': rule1.shared, + 'enabled': rule1.enabled, + 'ip_version': rule1.ip_version + } + + self.mock_rule_create.return_value = rule1 + + res = self.client.post(reverse(self.ADDRULE_PATH), form_data) + + self.assertNoFormErrors(res) + self.assertRedirectsNoFollow(res, str(self.INDEX_URL)) + + data = form_data.copy() + data['source_ip_address'] = None + data['source_port'] = None + self.mock_rule_create.assert_called_once_with( + helpers.IsHttpRequest(), **data) + + @helpers.create_mocks({api_fwaas_v2: ('rule_create',), }) + def test_add_rule_post_dest_None(self): + rule1 = self.fw_rules_v2.first() + form_data = {'name': rule1.name, + 'description': rule1.description, + 'protocol': rule1.protocol, + 'action': rule1.action, + 'source_ip_address': rule1.source_ip_address, + 'destination_ip_address': '', + 'source_port': rule1.source_port, + 'destination_port': '', + 'shared': rule1.shared, + 'enabled': rule1.enabled, + 'ip_version': rule1.ip_version + } + + self.mock_rule_create.return_value = rule1 + + res = self.client.post(reverse(self.ADDRULE_PATH), form_data) + + self.assertNoFormErrors(res) + self.assertRedirectsNoFollow(res, str(self.INDEX_URL)) + + data = form_data.copy() + data['destination_ip_address'] = None + data['destination_port'] = None + self.mock_rule_create.assert_called_once_with( + helpers.IsHttpRequest(), **data) + + def test_add_rule_post_with_error(self): + rule1 = self.fw_rules_v2.first() + + form_data = {'name': rule1.name, + 'description': rule1.description, + 'protocol': 'abc', + 'action': 'pass', + 'source_ip_address': rule1.source_ip_address, + 'source_port': rule1.source_port, + 'destination_ip_address': rule1.destination_ip_address, + 'destination_port': rule1.destination_port, + 'shared': rule1.shared, + 'enabled': rule1.enabled, + 'ip_version': 6 + } + + res = self.client.post(reverse(self.ADDRULE_PATH), form_data) + + self.assertFormErrors(res, 3) + + @helpers.create_mocks({api_fwaas_v2: ('policy_create', + 'rule_list_for_tenant'), }) + def test_add_policy_post(self): + policy = self.fw_policies_v2.first() + rules = self.fw_rules_v2.list() + tenant_id = self.tenant.id + form_data = {'name': policy.name, + 'description': policy.description, + 'firewall_rules': policy.firewall_rules, + 'shared': policy.shared, + 'audited': policy.audited + } + post_data = {'name': policy.name, + 'description': policy.description, + 'rule': policy.firewall_rules, + 'shared': policy.shared, + 'audited': policy.audited + } + + # NOTE: SelectRulesAction.populate_rule_choices() lists rule not + # associated with any policy. We need to ensure that rules specified + # in policy.firewall_rules in post_data (above) are not associated + # with any policy. Test data in neutron_data is data in a stable state, + # so we need to modify here. + for rule in rules: + if rule.id in policy.firewall_rules: + rule.firewall_policy_id = rule.policy = None + self.mock_rule_list_for_tenant.return_value = rules + self.mock_policy_create.return_value = policy + + res = self.client.post(reverse(self.ADDPOLICY_PATH), post_data) + + self.assertNoFormErrors(res) + self.assertRedirectsNoFollow(res, str(self.INDEX_URL)) + + self.mock_rule_list_for_tenant.assert_called_once_with( + helpers.IsHttpRequest(), tenant_id) + self.mock_policy_create.assert_called_once_with( + helpers.IsHttpRequest(), **form_data) + + @helpers.create_mocks({api_fwaas_v2: ('rule_list_for_tenant',)}) + def test_add_policy_post_with_error(self): + policy = self.fw_policies_v2.first() + rules = self.fw_rules_v2.list() + tenant_id = self.tenant.id + form_data = {'description': policy.description, + 'firewall_rules': '', + 'shared': policy.shared, + 'audited': policy.audited + } + self.mock_rule_list_for_tenant.return_value = rules + + res = self.client.post(reverse(self.ADDPOLICY_PATH), form_data) + + self.assertFormErrors(res, 1) + + self.mock_rule_list_for_tenant.assert_called_once_with( + helpers.IsHttpRequest(), tenant_id) + + @helpers.create_mocks({api_fwaas_v2: ('firewall_group_create', + 'policy_list_for_tenant', + 'fwg_port_list_for_tenant',)}) + def test_add_firewall_group_post(self): + fwg = self.firewall_groups_v2.first() + policies = self.fw_policies_v2.list() + tenant_id = self.tenant.id + + form_data = { + 'name': fwg.name, + 'description': fwg.description, + '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': False, + 'port': [], + } + + self.mock_policy_list_for_tenant.return_value = policies + self.mock_fwg_port_list_for_tenant.return_value = [] + self.mock_firewall_group_create.return_value = fwg + + res = self.client.post(reverse(self.ADDFIREWALLGROUP_PATH), form_data) + self.assertNoFormErrors(res) + self.assertRedirectsNoFollow(res, str(self.INDEX_URL)) + + self.mock_policy_list_for_tenant.assert_called_once_with( + helpers.IsHttpRequest(), tenant_id) + self.mock_fwg_port_list_for_tenant.assert_called_once_with( + helpers.IsHttpRequest(), tenant_id) + data = form_data.copy() + data['ports'] = data.pop('port') + self.mock_firewall_group_create.assert_called_once_with( + helpers.IsHttpRequest(), **data) + + # TODO(SarathMekala) : Fix this test. + # @helpers.create_mocks({api_fwaas_v2: ('firewall_group_create', + # 'policy_list_for_tenant', + # 'fwg_port_list_for_tenant',)}) + # def test_add_firewall_post_with_error(self): + # firewall_groups = self.firewall_groups_v2.first() + # policies = self.fw_policies_v2.list() + # tenant_id = self.tenant.id + # form_data = {'name': firewall_groups.name, + # 'description': firewall_groups.description, + # 'admin_state_up': False + # } + # self.mock_policy_list_for_tenant( + # helpers.IsHttpRequest(), tenant_id).AndReturn(policies) + # + # self.mox.ReplayAll() + # res = self.client.post(reverse(self.ADDFIREWALLGROUP_PATH), form_data) + # + # self.assertFormErrors(res, 1) + + @helpers.create_mocks({api_fwaas_v2: ('rule_get',)}) + def test_update_rule_get(self): + rule = self.fw_rules_v2.first() + + self.mock_rule_get.return_value = rule + + res = self.client.get(reverse(self.UPDATERULE_PATH, args=(rule.id,))) + + self.assertTemplateUsed(res, 'project/firewalls_v2/updaterule.html') + self.mock_rule_get.assert_called_once_with(helpers.IsHttpRequest(), + rule.id) + + @helpers.create_mocks({api_fwaas_v2: ('rule_get', 'rule_update')}) + def test_update_rule_post(self): + rule = self.fw_rules_v2.first() + + self.mock_rule_get.return_value = rule + self.mock_rule_update.return_value = rule + + data = { + 'name': 'new name', + 'description': 'new desc', + 'protocol': 'icmp', + 'action': 'allow', + 'shared': False, + 'enabled': True, + 'ip_version': rule.ip_version, + 'source_ip_address': rule.source_ip_address, + 'destination_ip_address': None, + 'source_port': None, + 'destination_port': rule.destination_port, + } + expected_put_data = { + 'name': 'new name', + 'description': 'new desc', + 'protocol': 'icmp', + 'action': 'allow', + 'shared': False, + 'destination_ip_address': None, + 'source_port': None, + } + + form_data = data.copy() + form_data['destination_ip_address'] = '' + form_data['source_port'] = '' + + res = self.client.post( + reverse(self.UPDATERULE_PATH, args=(rule.id,)), form_data) + + self.assertNoFormErrors(res) + self.assertRedirectsNoFollow(res, str(self.INDEX_URL)) + + self.mock_rule_get.assert_called_once_with(helpers.IsHttpRequest(), + rule.id) + self.mock_rule_update.assert_called_once_with( + helpers.IsHttpRequest(), rule.id, **expected_put_data) + + @helpers.create_mocks({api_fwaas_v2: ('rule_get', 'rule_update')}) + def test_update_protocol_any_rule_post(self): + # protocol any means protocol == None in neutron context. + rule = self.fw_rules_v2.get(protocol=None) + + self.mock_rule_get.return_value = rule + self.mock_rule_update.return_value = rule + + data = { + 'name': 'new name', + 'description': 'new desc', + 'protocol': 'icmp', + 'action': 'allow', + 'shared': False, + 'enabled': True, + 'ip_version': rule.ip_version, + 'source_ip_address': rule.source_ip_address, + 'destination_ip_address': None, + 'source_port': None, + 'destination_port': rule.destination_port, + } + expected_put_data = { + 'name': 'new name', + 'description': 'new desc', + 'protocol': 'icmp', + 'action': 'allow', + 'shared': False, + 'destination_ip_address': None, + 'source_port': None, + } + + form_data = data.copy() + form_data['destination_ip_address'] = '' + form_data['source_port'] = '' + + res = self.client.post( + reverse(self.UPDATERULE_PATH, args=(rule.id,)), form_data) + + self.assertNoFormErrors(res) + self.assertRedirectsNoFollow(res, str(self.INDEX_URL)) + + self.mock_rule_get.assert_called_once_with(helpers.IsHttpRequest(), + rule.id) + self.mock_rule_update.assert_called_once_with( + helpers.IsHttpRequest(), rule.id, **expected_put_data) + + @helpers.create_mocks({api_fwaas_v2: ('rule_get', 'rule_update')}) + def test_update_rule_protocol_to_ANY_post(self): + rule = self.fw_rules_v2.first() + + self.mock_rule_get.return_value = rule + self.mock_rule_update.return_value = rule + + form_data = { + 'name': 'new name', + 'description': 'new desc', + 'protocol': 'any', + 'action': 'allow', + 'shared': False, + 'enabled': True, + 'ip_version': rule.ip_version, + 'source_ip_address': rule.source_ip_address, + 'destination_ip_address': '', + 'source_port': '', + 'destination_port': rule.destination_port, + } + + expected_put_data = { + 'name': 'new name', + 'description': 'new desc', + 'protocol': None, + 'action': 'allow', + 'shared': False, + 'destination_ip_address': None, + 'source_port': None, + } + + res = self.client.post( + reverse(self.UPDATERULE_PATH, args=(rule.id,)), form_data) + + self.assertNoFormErrors(res) + self.assertRedirectsNoFollow(res, str(self.INDEX_URL)) + + self.mock_rule_get.assert_called_once_with(helpers.IsHttpRequest(), + rule.id) + self.mock_rule_update.assert_called_once_with( + helpers.IsHttpRequest(), rule.id, **expected_put_data) + + @helpers.create_mocks({api_fwaas_v2: ('policy_get',)}) + def test_update_policy_get(self): + policy = self.fw_policies_v2.first() + + self.mock_policy_get.return_value = policy + + res = self.client.get( + reverse(self.UPDATEPOLICY_PATH, args=(policy.id,))) + + self.assertTemplateUsed(res, 'project/firewalls_v2/updatepolicy.html') + + self.mock_policy_get.assert_called_once_with(helpers.IsHttpRequest(), + policy.id) + + @helpers.create_mocks({api_fwaas_v2: ('policy_get', 'policy_update')}) + def test_update_policy_post(self): + policy = self.fw_policies_v2.first() + + self.mock_policy_get.return_value = policy + self.mock_policy_update.return_value = policy + + data = { + 'name': 'new name', + 'description': 'new desc', + 'shared': True, + 'audited': False + } + expected_put_data = { + 'name': 'new name', + 'description': 'new desc', + 'audited': False, + } + + res = self.client.post( + reverse(self.UPDATEPOLICY_PATH, args=(policy.id,)), data) + + self.assertNoFormErrors(res) + self.assertRedirectsNoFollow(res, str(self.INDEX_URL)) + + self.mock_policy_get.assert_called_once_with(helpers.IsHttpRequest(), + policy.id) + self.mock_policy_update.assert_called_once_with( + helpers.IsHttpRequest(), policy.id, **expected_put_data) + + @helpers.create_mocks({api_fwaas_v2: ('firewall_group_get', + 'policy_list_for_tenant')}) + def test_update_firewall_group_get(self): + firewall_group = self.firewall_groups_v2.first() + policies = self.fw_policies_v2.list() + tenant_id = self.tenant.id + + self.mock_policy_list_for_tenant.return_value = policies + self.mock_firewall_group_get.return_value = firewall_group + + res = self.client.get( + reverse(self.UPDATEFIREWALLGROUP_PATH, args=(firewall_group.id,))) + + self.assertTemplateUsed(res, + 'project/firewalls_v2/updatefirewall.html') + + self.mock_policy_list_for_tenant.assert_called_once_with( + helpers.IsHttpRequest(), tenant_id) + self.mock_firewall_group_get.assert_called_once_with( + helpers.IsHttpRequest(), firewall_group.id) + + @helpers.create_mocks({api_fwaas_v2: ('firewall_group_get', + 'policy_list_for_tenant', + 'firewall_group_update')}) + def test_update_firewall_post(self): + fwg = self.firewall_groups_v2.first() + tenant_id = self.tenant.id + policies = self.fw_policies_v2.list() + + data = { + 'name': 'new name', + 'description': 'new desc', + 'ingress_firewall_policy_id': fwg.ingress_firewall_policy_id, + 'egress_firewall_policy_id': '', + 'admin_state_up': False, + 'shared': False, + } + + expected_put_data = { + 'name': 'new name', + 'description': 'new desc', + 'egress_firewall_policy_id': None, + 'admin_state_up': False, + } + + self.mock_firewall_group_get.return_value = fwg + self.mock_policy_list_for_tenant.return_value = policies + self.mock_firewall_group_update.return_value = fwg + + res = self.client.post( + reverse( + self.UPDATEFIREWALLGROUP_PATH, + args=( + fwg.id, + )), + data) + + self.assertNoFormErrors(res) + self.assertRedirectsNoFollow(res, str(self.INDEX_URL)) + + self.mock_firewall_group_get.assert_called_once_with( + helpers.IsHttpRequest(), fwg.id) + self.mock_policy_list_for_tenant.assert_called_once_with( + helpers.IsHttpRequest(), tenant_id) + self.mock_firewall_group_update.assert_called_once_with( + helpers.IsHttpRequest(), fwg.id, **expected_put_data) + + @helpers.create_mocks({api_fwaas_v2: ('policy_get', 'policy_insert_rule', + 'rule_list_for_tenant', 'rule_get')}) + def test_policy_insert_rule(self): + policy = self.fw_policies_v2.first() + tenant_id = self.tenant.id + rules = self.fw_rules_v2.list() + + new_rule_id = rules[2].id + + data = {'firewall_rule_id': new_rule_id, + 'insert_before': rules[1].id, + 'insert_after': rules[0].id} + + self.mock_policy_get.return_value = policy + + policy.firewall_rules = [rules[0].id, + new_rule_id, + rules[1].id] + + self.mock_rule_list_for_tenant.return_value = rules + self.mock_rule_get.return_value = rules[2] + self.mock_policy_insert_rule.return_value = policy + + res = self.client.post( + reverse(self.INSERTRULE_PATH, args=(policy.id,)), data) + + self.assertNoFormErrors(res) + self.assertRedirectsNoFollow(res, str(self.INDEX_URL)) + + self.mock_policy_get.assert_called_once_with(helpers.IsHttpRequest(), + policy.id) + self.mock_rule_list_for_tenant.assert_called_once_with( + helpers.IsHttpRequest(), tenant_id) + self.mock_rule_get.assert_called_once_with( + helpers.IsHttpRequest(), new_rule_id) + self.mock_policy_insert_rule.assert_called_once_with( + helpers.IsHttpRequest(), policy.id, **data) + + @helpers.create_mocks({api_fwaas_v2: ('policy_get', 'policy_remove_rule', + 'rule_list_for_tenant', 'rule_get')}) + def test_policy_remove_rule(self): + policy = self.fw_policies_v2.first() + tenant_id = self.tenant.id + rules = self.fw_rules_v2.list() + + remove_rule_id = policy.firewall_rules[0] + left_rule_id = policy.firewall_rules[1] + + data = {'firewall_rule_id': remove_rule_id} + + after_remove_policy_dict = {'id': 'abcdef-c3eb-4fee-9763-12de3338041e', + 'tenant_id': '1', + 'name': 'policy1', + 'description': 'policy description', + 'firewall_rules': [left_rule_id], + 'audited': True, + 'shared': True} + after_remove_policy = api_fwaas_v2.Policy(after_remove_policy_dict) + + self.mock_policy_get.return_value = policy + self.mock_rule_list_for_tenant.return_value = rules + self.mock_rule_get.return_value = rules[0] + self.mock_policy_remove_rule.return_value = after_remove_policy + + res = self.client.post( + reverse(self.REMOVERULE_PATH, args=(policy.id,)), data) + + self.assertNoFormErrors(res) + self.assertRedirectsNoFollow(res, str(self.INDEX_URL)) + + self.mock_policy_get.assert_called_once_with(helpers.IsHttpRequest(), + policy.id) + self.mock_rule_list_for_tenant.assert_called_once_with( + helpers.IsHttpRequest(), tenant_id) + self.mock_rule_get.assert_called_once_with( + helpers.IsHttpRequest(), remove_rule_id) + self.mock_policy_remove_rule.assert_called_once_with( + helpers.IsHttpRequest(), policy.id, **data) + + @helpers.create_mocks({api_fwaas_v2: ('rule_list_for_tenant', + 'rule_delete')}) + def test_delete_rule(self): + rule = self.fw_rules_v2.list()[2] + + self.mock_rule_list_for_tenant.return_value = self.fw_rules_v2.list() + self.mock_rule_delete.return_value = None + + form_data = {"action": "rulestable__deleterule__%s" % rule.id} + res = self.client.post(self.INDEX_URL, form_data) + + self.assertNoFormErrors(res) + + self.mock_rule_list_for_tenant.assert_called_once_with( + helpers.IsHttpRequest(), self.tenant.id) + self.mock_rule_delete.assert_called_once_with( + helpers.IsHttpRequest(), rule.id) + + @helpers.create_mocks({api_fwaas_v2: ('policy_list_for_tenant', + 'policy_delete')}) + def test_delete_policy(self): + policy = self.fw_policies_v2.first() + + self.mock_policy_list_for_tenant.return_value = \ + self.fw_policies_v2.list() + self.mock_policy_delete.return_value = None + + form_data = {"action": "policiestable__deletepolicy__%s" % policy.id} + res = self.client.post(self.INDEX_URL, form_data) + + self.assertNoFormErrors(res) + + self.mock_policy_list_for_tenant.assert_called_once_with( + helpers.IsHttpRequest(), self.tenant.id) + self.mock_policy_delete.assert_called_once_with( + helpers.IsHttpRequest(), policy.id) + + @helpers.create_mocks({api_fwaas_v2: ('firewall_group_list_for_tenant', + 'policy_list_for_tenant', + 'firewall_group_delete',)}) + def test_delete_firewall_group(self): + fwl = self.firewall_groups_v2.first() + + self.mock_firewall_group_list_for_tenant.return_value = [fwl] + self.mock_policy_list_for_tenant.return_value = \ + self.fw_policies_v2.list() + self.mock_firewall_group_delete.return_value = None + + form_data = { + "action": "FirewallGroupsTable__deletefirewallgroup__%s" % + fwl.id} + res = self.client.post(self.INDEX_URL, form_data) + + self.assertNoFormErrors(res) + + self.mock_firewall_group_list_for_tenant.assert_called_once_with( + helpers.IsHttpRequest(), self.tenant.id) + self.mock_policy_list_for_tenant.assert_called_once_with( + helpers.IsHttpRequest(), self.tenant.id) + self.mock_firewall_group_delete.assert_called_once_with( + helpers.IsHttpRequest(), fwl.id) diff --git a/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/urls.py b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/urls.py new file mode 100644 index 0000000..e4363b0 --- /dev/null +++ b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/urls.py @@ -0,0 +1,53 @@ +# Copyright 2017, Juniper Networks, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from django.conf.urls import url + +from neutron_fwaas_dashboard.dashboards.project.firewalls_v2 import views + +# TODO(Sarath Mekala) : Fix 'firewall' --> 'firewallgroup' in URLs as +# well as in other places. +urlpatterns = [ + url(r'^$', views.IndexView.as_view(), name='index'), + url(r'^\?tab=fwtabs__firewalls$', + views.IndexView.as_view(), name='firewalls'), + url(r'^\?tab=fwtabs__rules$', views.IndexView.as_view(), name='rules'), + url(r'^\?tab=fwtabs__policies$', + views.IndexView.as_view(), name='policies'), + url(r'^addrule$', views.AddRuleView.as_view(), name='addrule'), + url(r'^addpolicy$', views.AddPolicyView.as_view(), name='addpolicy'), + url(r'^addfirewallgroup$', + views.AddFirewallGroupView.as_view(), + name='addfirewallgroup'), + url(r'^insertrule/(?P[^/]+)/$', + views.InsertRuleToPolicyView.as_view(), name='insertrule'), + url(r'^removerule/(?P[^/]+)/$', + views.RemoveRuleFromPolicyView.as_view(), name='removerule'), + url(r'^updaterule/(?P[^/]+)/$', + views.UpdateRuleView.as_view(), name='updaterule'), + url(r'^updatepolicy/(?P[^/]+)/$', + views.UpdatePolicyView.as_view(), name='updatepolicy'), + url(r'^updatefirewall/(?P[^/]+)/$', + views.UpdateFirewallView.as_view(), name='updatefirewall'), + url(r'^addport/(?P[^/]+)/$', + views.AddPortView.as_view(), name='addport'), + url(r'^removeport/(?P[^/]+)/$', + views.RemovePortView.as_view(), name='removeport'), + url(r'^rule/(?P[^/]+)/$', + views.RuleDetailsView.as_view(), name='ruledetails'), + url(r'^policy/(?P[^/]+)/$', + views.PolicyDetailsView.as_view(), name='policydetails'), + url(r'^firewallgroup/(?P[^/]+)/$', + views.FirewallGroupDetailsView.as_view(), name='firewallgroupdetails'), +] diff --git a/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/views.py b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/views.py new file mode 100644 index 0000000..58646d0 --- /dev/null +++ b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/views.py @@ -0,0 +1,444 @@ +# Copyright 2017, Juniper Networks, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from django.urls import reverse +from django.urls import reverse_lazy +from django.utils.translation import ugettext_lazy as _ + +from horizon import exceptions +from horizon import forms +from horizon import tabs +from horizon.utils import memoized +from horizon import workflows + +from neutron_fwaas_dashboard.api import fwaas_v2 as api_fwaas_v2 +from neutron_fwaas_dashboard.dashboards.project.firewalls_v2 \ + import forms as fw_forms +from neutron_fwaas_dashboard.dashboards.project.firewalls_v2 \ + import tabs as fw_tabs +from neutron_fwaas_dashboard.dashboards.project.firewalls_v2 \ + import workflows as fw_workflows + +InsertRuleToPolicy = fw_forms.InsertRuleToPolicy + +RemoveRuleFromPolicy = fw_forms.RemoveRuleFromPolicy +UpdateFirewall = fw_forms.UpdateFirewall +UpdatePolicy = fw_forms.UpdatePolicy +UpdateRule = fw_forms.UpdateRule +AddPort = fw_forms.AddPort +RemovePort = fw_forms.RemovePort + +FirewallGroupDetailsTabs = fw_tabs.FirewallGroupDetailsTabs +FirewallGroupTabs = fw_tabs.FirewallGroupTabs +PolicyDetailsTabs = fw_tabs.PolicyDetailsTabs +RuleDetailsTabs = fw_tabs.RuleDetailsTabs + +AddFirewallGroup = fw_workflows.AddFirewallGroup +AddPolicy = fw_workflows.AddPolicy +AddRule = fw_workflows.AddRule + + +class IndexView(tabs.TabbedTableView): + tab_group_class = FirewallGroupTabs + template_name = 'project/firewalls_v2/details_tabs.html' + page_title = _("Firewall Groups") + + +class AddRuleView(workflows.WorkflowView): + workflow_class = AddRule + template_name = "project/firewalls_v2/addrule.html" + page_title = _("Add New Rule") + + +class AddPolicyView(workflows.WorkflowView): + workflow_class = AddPolicy + template_name = "project/firewalls_v2/addpolicy.html" + page_title = _("Add New Policy") + + +class AddFirewallGroupView(workflows.WorkflowView): + workflow_class = AddFirewallGroup + template_name = "project/firewalls_v2/addfirewallgroup.html" + page_title = _("Add New Firewall Group") + + +class RuleDetailsView(tabs.TabView): + tab_group_class = (RuleDetailsTabs) + template_name = 'horizon/common/_detail.html' + page_title = "{{ rule.name|default:rule.id }}" + failure_url = reverse_lazy('horizon:project:firewalls_v2:index') + + def get_context_data(self, **kwargs): + context = super(RuleDetailsView, self).get_context_data(**kwargs) + rule = self.get_data() + table = fw_tabs.RulesTable(self.request) + breadcrumb = [ + (_("Rules"), reverse_lazy('horizon:project:firewalls_v2:rules'))] + context["custom_breadcrumb"] = breadcrumb + context["rule"] = rule + context["url"] = self.failure_url + context["actions"] = table.render_row_actions(rule) + return context + + @memoized.memoized_method + def get_data(self): + try: + rule_id = self.kwargs['rule_id'] + rule = api_fwaas_v2.rule_get(self.request, rule_id) + except Exception: + exceptions.handle(self.request, + _('Unable to retrieve rule details.'), + redirect=self.failure_url) + return rule + + def get_tabs(self, request, *args, **kwargs): + rule = self.get_data() + return self.tab_group_class(request, rule=rule, **kwargs) + + +class PolicyDetailsView(tabs.TabView): + tab_group_class = (PolicyDetailsTabs) + template_name = 'horizon/common/_detail.html' + page_title = "{{ policy.name|default:policy.id }}" + failure_url = reverse_lazy('horizon:project:firewalls_v2:index') + + def get_context_data(self, **kwargs): + context = super(PolicyDetailsView, self).get_context_data(**kwargs) + policy = self.get_data() + table = fw_tabs.PoliciesTable(self.request) + breadcrumb = [ + (_("Policies"), + reverse_lazy('horizon:project:firewalls_v2:policies'))] + context["custom_breadcrumb"] = breadcrumb + context["policy"] = policy + context["url"] = self.failure_url + context["actions"] = table.render_row_actions(policy) + return context + + @memoized.memoized_method + def get_data(self): + try: + policy_id = self.kwargs['policy_id'] + policy = api_fwaas_v2.policy_get(self.request, policy_id) + except Exception: + exceptions.handle(self.request, + _('Unable to retrieve policy details.'), + redirect=self.failure_url) + return policy + + def get_tabs(self, request, *args, **kwargs): + policy = self.get_data() + return self.tab_group_class(request, policy=policy, **kwargs) + + +class FirewallGroupDetailsView(tabs.TabView): + tab_group_class = (FirewallGroupDetailsTabs) + template_name = 'horizon/common/_detail.html' + page_title = "{{ firewall_group.name|default:firewall_group.id }}" + failure_url = reverse_lazy('horizon:project:firewalls_v2:index') + + def get_context_data(self, **kwargs): + context = super(FirewallGroupDetailsView, self) \ + .get_context_data(**kwargs) + firewall_group = self.get_data() + table = fw_tabs.FirewallGroupsTable(self.request) + context["firewall_group"] = firewall_group + context["url"] = self.failure_url + context["actions"] = table.render_row_actions(firewall_group) + return context + + @memoized.memoized_method + def get_data(self): + try: + firewallgroup_id = self.kwargs['firewallgroup_id'] + firewall_group = api_fwaas_v2.firewall_group_get(self.request, + firewallgroup_id) + except Exception: + exceptions.handle(self.request, + _('Unable to retrieve firewall group details.'), + redirect=self.failure_url) + return firewall_group + + def get_tabs(self, request, *args, **kwargs): + firewall_group = self.get_data() + return self.tab_group_class(request, firewallgroup=firewall_group, + **kwargs) + + +class UpdateRuleView(forms.ModalFormView): + form_class = UpdateRule + form_id = "update_rule_form" + template_name = "project/firewalls_v2/updaterule.html" + context_object_name = 'rule' + submit_label = _("Save Changes") + submit_url = "horizon:project:firewalls_v2:updaterule" + success_url = reverse_lazy("horizon:project:firewalls_v2:index") + page_title = _("Edit Rule {{ name }}") + + def get_context_data(self, **kwargs): + context = super(UpdateRuleView, self).get_context_data(**kwargs) + context['rule_id'] = self.kwargs['rule_id'] + args = (self.kwargs['rule_id'],) + context['submit_url'] = reverse(self.submit_url, args=args) + obj = self._get_object() + if obj: + context['name'] = obj.name_or_id + return context + + @memoized.memoized_method + def _get_object(self, *args, **kwargs): + rule_id = self.kwargs['rule_id'] + try: + rule = api_fwaas_v2.rule_get(self.request, rule_id) + return rule + except Exception: + redirect = self.success_url + msg = _('Unable to retrieve rule details.') + exceptions.handle(self.request, msg, redirect=redirect) + + def get_initial(self): + rule = self._get_object() + initial = rule.to_dict() + protocol = initial['protocol'] + initial['protocol'] = protocol.upper() if protocol else 'ANY' + initial['action'] = initial['action'].upper() + return initial + + +class UpdatePolicyView(forms.ModalFormView): + form_class = UpdatePolicy + form_id = "update_policy_form" + template_name = "project/firewalls_v2/updatepolicy.html" + context_object_name = 'policy' + submit_label = _("Save Changes") + submit_url = "horizon:project:firewalls_v2:updatepolicy" + success_url = reverse_lazy("horizon:project:firewalls_v2:index") + page_title = _("Edit Policy {{ name }}") + + def get_context_data(self, **kwargs): + context = super(UpdatePolicyView, self).get_context_data(**kwargs) + context["policy_id"] = self.kwargs['policy_id'] + args = (self.kwargs['policy_id'],) + context['submit_url'] = reverse(self.submit_url, args=args) + obj = self._get_object() + if obj: + context['name'] = obj.name_or_id + return context + + @memoized.memoized_method + def _get_object(self, *args, **kwargs): + policy_id = self.kwargs['policy_id'] + try: + policy = api_fwaas_v2.policy_get(self.request, policy_id) + return policy + except Exception: + redirect = self.success_url + msg = _('Unable to retrieve policy details.') + exceptions.handle(self.request, msg, redirect=redirect) + + def get_initial(self): + policy = self._get_object() + initial = policy.to_dict() + return initial + + +class UpdateFirewallView(forms.ModalFormView): + form_class = UpdateFirewall + form_id = "update_firewall_form" + template_name = "project/firewalls_v2/updatefirewall.html" + context_object_name = 'firewall' + submit_label = _("Save Changes") + submit_url = "horizon:project:firewalls_v2:updatefirewall" + success_url = reverse_lazy("horizon:project:firewalls_v2:index") + page_title = _("Edit FirewallGroup {{ name }}") + + def get_context_data(self, **kwargs): + context = super(UpdateFirewallView, self).get_context_data(**kwargs) + context["firewall_id"] = self.kwargs['firewall_id'] + args = (self.kwargs['firewall_id'],) + context['submit_url'] = reverse(self.submit_url, args=args) + obj = self._get_object() + if obj: + context['name'] = obj.name + return context + + @memoized.memoized_method + def _get_object(self, *args, **kwargs): + fwg_id = self.kwargs['firewall_id'] + try: + fwg = api_fwaas_v2.firewall_group_get(self.request, fwg_id) + return fwg + except Exception: + redirect = self.success_url + msg = _('Unable to retrieve firewall group details.') + exceptions.handle(self.request, msg, redirect=redirect) + + def get_initial(self): + fwg = self._get_object() + initial = fwg.to_dict() + return initial + + +class AddPortView(forms.ModalFormView): + form_class = AddPort + form_id = "update_firewall_port_form" + template_name = "project/firewalls_v2/addport.html" + context_object_name = 'firewallgroup' + submit_label = _("Save Changes") + submit_url = "horizon:project:firewalls_v2:addport" + success_url = reverse_lazy("horizon:project:firewalls_v2:index") + page_title = _("Add port to Firewall Group {{ name }}") + + def get_context_data(self, **kwargs): + context = super(AddPortView, self).get_context_data(**kwargs) + context["firewallgroup_id"] = self.kwargs['firewallgroup_id'] + args = (self.kwargs['firewallgroup_id'],) + context['submit_url'] = reverse(self.submit_url, args=args) + obj = self._get_object() + if obj: + context['name'] = obj.name + return context + + @memoized.memoized_method + def _get_object(self, *args, **kwargs): + firewallgroup_id = self.kwargs['firewallgroup_id'] + try: + firewallgroup = api_fwaas_v2.firewall_group_get(self.request, + firewallgroup_id) + return firewallgroup + except Exception: + redirect = self.success_url + msg = _('Unable to retrieve firewallgroup details.') + exceptions.handle(self.request, msg, redirect=redirect) + + def get_initial(self): + firewallgroup = self._get_object() + initial = firewallgroup.to_dict() + return initial + + +class RemovePortView(forms.ModalFormView): + form_class = RemovePort + form_id = "update_firewall_port_form" + template_name = "project/firewalls_v2/removeport.html" + context_object_name = 'firewallgroup' + submit_label = _("Save Changes") + submit_url = "horizon:project:firewalls_v2:removeport" + success_url = reverse_lazy("horizon:project:firewalls_v2:index") + page_title = _("Remove port from FirewallGroup {{ name }}") + + def get_context_data(self, **kwargs): + context = super(RemovePortView, self).get_context_data(**kwargs) + context["firewallgroup_id"] = self.kwargs['firewallgroup_id'] + args = (self.kwargs['firewallgroup_id'],) + context['submit_url'] = reverse(self.submit_url, args=args) + obj = self._get_object() + if obj: + context['name'] = obj.name + return context + + @memoized.memoized_method + def _get_object(self, *args, **kwargs): + firewallgroup_id = self.kwargs['firewallgroup_id'] + try: + firewallgroup = api_fwaas_v2.firewall_group_get(self.request, + firewallgroup_id) + return firewallgroup + except Exception: + redirect = self.success_url + msg = _('Unable to retrieve firewall group details.') + exceptions.handle(self.request, msg, redirect=redirect) + + def get_initial(self): + firewallgroup = self._get_object() + initial = firewallgroup.to_dict() + return initial + + +class InsertRuleToPolicyView(forms.ModalFormView): + form_class = InsertRuleToPolicy + form_id = "update_policy_form" + template_name = "project/firewalls_v2/insert_rule_to_policy.html" + context_object_name = 'policy' + submit_url = "horizon:project:firewalls_v2:insertrule" + submit_label = _("Save Changes") + success_url = reverse_lazy("horizon:project:firewalls_v2:index") + page_title = _("Insert Rule to Policy") + + def get_context_data(self, **kwargs): + context = super(InsertRuleToPolicyView, + self).get_context_data(**kwargs) + context["policy_id"] = self.kwargs['policy_id'] + args = (self.kwargs['policy_id'],) + context['submit_url'] = reverse(self.submit_url, args=args) + obj = self._get_object() + if obj: + context['name'] = obj.name_or_id + return context + + @memoized.memoized_method + def _get_object(self, *args, **kwargs): + policy_id = self.kwargs['policy_id'] + try: + policy = api_fwaas_v2.policy_get(self.request, policy_id) + return policy + except Exception: + redirect = self.success_url + msg = _('Unable to retrieve policy details.') + exceptions.handle(self.request, msg, redirect=redirect) + + def get_initial(self): + policy = self._get_object() + initial = policy.to_dict() + initial['policy_id'] = initial['id'] + return initial + + +class RemoveRuleFromPolicyView(forms.ModalFormView): + form_class = RemoveRuleFromPolicy + form_id = "update_policy_form" + template_name = "project/firewalls_v2/remove_rule_from_policy.html" + context_object_name = 'policy' + submit_label = _("Save Changes") + submit_url = "horizon:project:firewalls_v2:removerule" + success_url = reverse_lazy("horizon:project:firewalls_v2:index") + page_title = _("Remove Rule from Policy") + + def get_context_data(self, **kwargs): + context = super(RemoveRuleFromPolicyView, + self).get_context_data(**kwargs) + context["policy_id"] = self.kwargs['policy_id'] + args = (self.kwargs['policy_id'],) + context['submit_url'] = reverse(self.submit_url, args=args) + obj = self._get_object() + if obj: + context['name'] = obj.name_or_id + return context + + @memoized.memoized_method + def _get_object(self, *args, **kwargs): + policy_id = self.kwargs['policy_id'] + try: + policy = api_fwaas_v2.policy_get(self.request, policy_id) + return policy + except Exception: + redirect = self.success_url + msg = _('Unable to retrieve policy details.') + exceptions.handle(self.request, msg, redirect=redirect) + + def get_initial(self): + policy = self._get_object() + initial = policy.to_dict() + initial['policy_id'] = initial['id'] + return initial diff --git a/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/widgets.py b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/widgets.py new file mode 100644 index 0000000..675b0b8 --- /dev/null +++ b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/widgets.py @@ -0,0 +1,260 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import itertools + +from django.template.loader import get_template +from django.utils.translation import ugettext_lazy as _ +from horizon.forms import fields + +"""A custom Horizon Forms Select widget that displays select choices as a table + +The widgets is meant as an optional replacement for the existing Horizon +ThemableDynamicSelectWidget which it extends and is compatible with. + + +Columns +------- +Columns are defined by setting the widgets 'column' attribute, which is +expected to be an iterable of strings, each one corresponding to one column and +used for that columns heading. + + +Rows +---- +Each row corresponds to one choice/select option with a defined value in +each column. + +The values displayed in each column are derived using the 'build_columns' +attribute, which is expected to be a function that: + +- takes a choice tuple of the form (value, label) as defined + for the Django SelectField instances as it's only parameter +- returns an iterable of Strings which are rendered as column + values for the given choice row in the same order as in the + iterable + +The default implementation simply uses the provided value and label as separate +column values. + +See the default implementation and example bellow for more details. + + +Condensed values +---------------- +To maintain visual consistency, the currently selected value is displayed in +the 'standard' ThemableDynamicSelectWidget HTML setup. To accommodate this, a +condensed, single string value is created from the individual columns and +displayed in the select box. + +This behavior can be modified by setting the 'condense' attribute. This is +expected to be a function that: + +- Takes the column iterable returned by 'build_columns' function +- Returns a single string representation of the choice + +By default, the condensed value is created by joining all of the provided +columns and joining them using commas as a delimiter. + +See the default implementation and example bellow for more details. + + +Small screen reactivity +----------------------- +Support for small screens (< 768px) is turned on by setting the attribute +'alternate_xs' to True. When on, a condesned version of the popup table +us used for small screens, where a single column is used with the condensed +row values used instead of the full table rows. + +The 'condense' function described above is used to construct this table. + + +Example +------- + +port_id = forms.ThemableDynamicChoiceField( + label=_("Ports"), + widget=TableSelectWidget( + columns=[ + 'ID', + 'Name' + ], + build_columns=lambda choice: return (choice[1], choice[0]), + choices=[ + ('port 1', 'id1'), + ('port 2', 'id2') + ], + alternate_xs=True, + condense=lambda columns: return ",".join(columns) + ) +) + +Produces: + ++------+--------+ +| ID | Name | ++------+--------+ +| id1 | port 1 | +| id2 | port 2 | ++------+--------+ + +on normal screens and + ++-------------+ +| ID, Name | ++-------------+ +| id1, port 1 | +| id2, port 2 | ++-------------+ + +on xs screens. + +""" + + +class TableSelectWidget(fields.ThemableDynamicSelectWidget): + def __init__(self, + attrs=None, + columns=None, + alternate_xs=False, + empty_text=_("No options available"), + other_html=None, + condense=None, + build_columns=None, *args, **kwargs + ): + """Initializer for TableSelectWidget + + :param attrs: A { attribute: value } dictionary which is attached to + the hidden select element; see + ThemableDynamicSelectWidget for further information + :param columns: An iterable of column headers/names + :param alternate_xs: A truth-y value which enables/disables an + alternate rendering method for small screens + :param empty_text: The text to be displayed in case no options are + available + :param other_html: A method for adding custom HTML to the hidden option + HTML. + NOTE: This mimics the behavior of + ThemableDynamicSelectWidget and is retained to + maintain compatibility with any related, potential + functionality + :param condense: A function callback that produces a condensed label + for each option + :param build_columns: A function used to populate the individual + columns in the pop up table for each option + """ + super(TableSelectWidget, self).__init__(attrs, *args, **kwargs) + self.columns = columns or [_('Label'), _('Value'), 'Nothing'] + + self.alternate_xs = alternate_xs + self.empty_text = empty_text + + if other_html: + self.other_html = other_html + + if condense: + self.condense = condense + + if build_columns: + self.build_columns = build_columns + + @staticmethod + def build_columns(choice): + """Default column building method + + Overwrite this method when initializing this widget or using + self.fields[name].widget.build_columns in a parent form initialization + to customize the behavior (see above for details) + + :param choice: + :return: + """ + return choice + + @staticmethod + def condense(choice_columns): + """The default condense method + + Overwrite this method when initializing this widget or using + self.fields[name].widget.condense in a parent form initialization to + customize the behavior (see above for details) + + :param choice_columns: + :return: + """ + return " / ".join([str(c) for c in choice_columns]) + + # Implements the parent 'other_html' construction for compatibility reasons + # Can be set in initializer to change the behavior as needed + def other_html(self, choice): + opt_label = choice[1] + + other_html = self.transform_option_html_attrs(opt_label) + data_attr_html = self.get_data_attrs(opt_label) + + if data_attr_html: + other_html += ' ' + data_attr_html + + return other_html + + def render(self, name, value, attrs=None, choices=None, renderer=None): + new_choices = [] + initial_value = value + + choices = choices or [] + + for opt in itertools.chain(self.choices, choices): + other_html = self.other_html(opt) + choice_columns = self.build_columns(opt) + condensed_label = self.condense(choice_columns) + + built_choice = ( + opt[0], condensed_label, choice_columns, other_html + ) + + new_choices.append(built_choice) + + # Initial selection + if opt[0] == value: + initial_value = built_choice + + if not initial_value and new_choices: + initial_value = new_choices[0] + + element_id = attrs.pop('id', 'id_%s' % name) + + # Size of individual columns in terms of the bootstrap grid - used + # for styling purposes + column_size = 12 // len(self.columns) + + # Creates a single string label for all columns for use with small + # screens + condensed_headers = self.condense(self.columns) + + template = get_template('project/firewalls_v2/table_select.html') + + select_attrs = self.build_attrs(attrs) + + context = { + 'name': name, + 'options': new_choices, + 'id': element_id, + 'value': value, + 'initial_value': initial_value, + 'select_attrs': select_attrs, + 'column_size': column_size, + 'columns': self.columns, + 'condensed_headers': condensed_headers, + 'alternate_xs': self.alternate_xs, + 'empty_text': self.empty_text + } + return template.render(context) diff --git a/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/workflows.py b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/workflows.py new file mode 100644 index 0000000..d7eb73c --- /dev/null +++ b/neutron_fwaas_dashboard/dashboards/project/firewalls_v2/workflows.py @@ -0,0 +1,388 @@ +# Copyright 2017, Juniper Networks, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +from operator import attrgetter + +from django.utils.translation import ugettext_lazy as _ +from horizon import exceptions +from horizon import forms +from horizon.utils import validators +from horizon import workflows +import netaddr + +from neutron_fwaas_dashboard.api import fwaas_v2 as api_fwaas_v2 + +port_validator = validators.validate_port_or_colon_separated_port_range + + +class AddRuleAction(workflows.Action): + name = forms.CharField( + max_length=80, + label=_("Name"), + required=False) + description = forms.CharField( + max_length=80, + label=_("Description"), + required=False) + protocol = forms.ThemableChoiceField( + label=_("Protocol"), + choices=[('tcp', _('TCP')), + ('udp', _('UDP')), + ('icmp', _('ICMP')), + ('any', _('ANY'))],) + action = forms.ThemableChoiceField( + label=_("Action"), + choices=[('allow', _('ALLOW')), + ('deny', _('DENY')), + ('reject', _('REJECT'))],) + source_ip_address = forms.IPField( + label=_("Source IP Address/Subnet"), + version=forms.IPv4 | forms.IPv6, + required=False, mask=True) + destination_ip_address = forms.IPField( + label=_("Destination IP Address/Subnet"), + version=forms.IPv4 | forms.IPv6, + required=False, mask=True) + source_port = forms.CharField( + max_length=80, + label=_("Source Port/Port Range"), + required=False, + validators=[port_validator]) + destination_port = forms.CharField( + max_length=80, + label=_("Destination Port/Port Range"), + required=False, + validators=[port_validator]) + ip_version = forms.ThemableChoiceField( + label=_("IP Version"), required=False, + choices=[('4', '4'), ('6', '6')]) + shared = forms.BooleanField( + label=_("Shared"), initial=False, required=False) + enabled = forms.BooleanField( + label=_("Enabled"), initial=True, required=False) + + def _check_ip_addr_and_ip_version(self, cleaned_data): + ip_version = int(str(cleaned_data.get('ip_version'))) + src_ip = cleaned_data.get('source_ip_address') + dst_ip = cleaned_data.get('destination_ip_address') + msg = _('Source/Destination Network Address and IP version ' + 'are inconsistent. Please make them consistent.') + if (src_ip and + netaddr.IPNetwork(src_ip).version != ip_version): + self._errors['ip_version'] = self.error_class([msg]) + + elif (dst_ip and + netaddr.IPNetwork(dst_ip).version != ip_version): + self._errors['ip_version'] = self.error_class([msg]) + + def clean(self): + cleaned_data = super(AddRuleAction, self).clean() + self._check_ip_addr_and_ip_version(cleaned_data) + + class Meta(object): + name = _("Rule") + permissions = ('openstack.services.network',) + help_text = _("Create a firewall rule.\n\n" + "A Firewall rule is an association of the following " + "attributes:\n\n" + "
        • IP Addresses: The addresses from/to which the " + "traffic filtration needs to be applied.
        • " + "
        • IP Version: The type of IP packets (IP V4/V6) " + "that needs to be filtered.
        • " + "
        • Protocol: Type of packets (UDP, ICMP, TCP, Any) " + "that needs to be checked.
        • " + "
        • Action: Action is the type of filtration " + "required, it can be Reject/Deny/Allow data " + "packets.
        • \n" + "The protocol and action fields are required, all " + "others are optional.") + + +class AddRuleStep(workflows.Step): + action_class = AddRuleAction + contributes = ("name", "description", "protocol", "action", + "source_ip_address", "source_port", + "destination_ip_address", "destination_port", + "enabled", "shared", "ip_version") + + def contribute(self, data, context): + context = super(AddRuleStep, self).contribute(data, context) + if data: + if context['protocol'] == 'any': + del context['protocol'] + for field in ['source_port', + 'destination_port', + 'source_ip_address', + 'destination_ip_address']: + if not context[field]: + del context[field] + return context + + +class AddRule(workflows.Workflow): + slug = "addrule" + name = _("Add Rule") + finalize_button_name = _("Add") + success_message = _('Added Rule "%s".') + failure_message = _('Unable to add Rule "%s".') + success_url = "horizon:project:firewalls_v2:index" + default_steps = (AddRuleStep,) + + def format_status_message(self, message): + return message % self.context.get('name') + + def handle(self, request, context): + try: + api_fwaas_v2.rule_create(request, **context) + return True + except Exception as e: + msg = self.format_status_message(self.failure_message) + str(e) + exceptions.handle(request, msg) + return False + + +class SelectRulesAction(workflows.Action): + rule = forms.MultipleChoiceField( + label=_("Rules"), + required=False, + widget=forms.ThemableCheckboxSelectMultiple(), + help_text=_("Create a policy with selected rules.")) + + class Meta(object): + name = _("Rules") + permissions = ('openstack.services.network',) + help_text = _("Select rules for your policy.") + + def populate_rule_choices(self, request, context): + try: + tenant_id = self.request.user.tenant_id + rules = api_fwaas_v2.rule_list_for_tenant(request, tenant_id) + rules = sorted(rules, + key=attrgetter('name_or_id')) + rule_list = [(rule.id, rule.name_or_id) for rule in rules] + except Exception as e: + rule_list = [] + exceptions.handle(request, _('Unable to retrieve rules (%s).') % e) + return rule_list + + +class SelectRulesStep(workflows.Step): + action_class = SelectRulesAction + template_name = "project/firewalls_v2/_update_rules.html" + contributes = ("firewall_rules",) + + def contribute(self, data, context): + if data: + rules = self.workflow.request.POST.getlist("rule") + if rules: + rules = [r for r in rules if r] + context['firewall_rules'] = rules + return context + + +class AddPolicyAction(workflows.Action): + name = forms.CharField(max_length=80, + label=_("Name")) + description = forms.CharField(max_length=80, + label=_("Description"), + required=False) + shared = forms.BooleanField(label=_("Shared"), + initial=False, + required=False) + audited = forms.BooleanField(label=_("Audited"), + initial=False, + required=False) + + class Meta(object): + name = _("Policy") + permissions = ('openstack.services.network',) + help_text = _("Create a firewall policy with an ordered list " + "of firewall rules.\n\n" + "A firewall policy is an ordered collection of firewall " + "rules. So if the traffic matches the first rule, the " + "other rules are not executed. If the traffic does not " + "match the current rule, then the next rule is " + "executed. A firewall policy has the following " + "attributes:\n\n" + "
        • Shared: A firewall policy can be shared across " + "tenants. Thus it can also be made part of an audit " + "workflow wherein the firewall policy can be audited " + "by the relevant entity that is authorized.
        • " + "
        • Audited: When audited is set to True, it indicates " + "that the firewall policy has been audited. " + "Each time the firewall policy or the associated " + "firewall rules are changed, this attribute will be " + "set to False and will have to be explicitly set to " + "True through an update operation.
        • \n" + "The name field is required, all others are optional.") + + +class AddPolicyStep(workflows.Step): + action_class = AddPolicyAction + contributes = ("name", "description", "shared", "audited") + + def contribute(self, data, context): + context = super(AddPolicyStep, self).contribute(data, context) + if data: + return context + + +class AddPolicy(workflows.Workflow): + slug = "addpolicy" + name = _("Add Policy") + finalize_button_name = _("Add") + success_message = _('Added Policy "%s".') + failure_message = _('Unable to add Policy "%s".') + success_url = "horizon:project:firewalls_v2:index" + default_steps = (AddPolicyStep, SelectRulesStep) + + def format_status_message(self, message): + return message % self.context.get('name') + + def handle(self, request, context): + try: + api_fwaas_v2.policy_create(request, **context) + return True + except Exception as e: + msg = self.format_status_message(self.failure_message) + str(e) + exceptions.handle(request, msg) + return False + + +class AddFWGPortsAction(workflows.Action): + port = forms.MultipleChoiceField( + label=_("Ports"), + required=False, + widget=forms.ThemableCheckboxSelectMultiple(), + help_text=_("Create a Firewall Group with selected ports.")) + + class Meta(object): + name = _("Ports") + permissions = ('openstack.services.network',) + help_text = _("Select ports for your firewall group.") + + def populate_port_choices(self, request, context): + try: + tenant_id = self.request.user.tenant_id + ports = api_fwaas_v2.fwg_port_list_for_tenant(request, tenant_id) + ports = sorted(ports, + key=attrgetter('name_or_id')) + port_list = [(port.id, port.name_or_id) for port in ports] + except Exception as e: + port_list = [] + exceptions.handle(request, _('Unable to retrieve ports (%s).') % e) + return port_list + + +class AddFWGPortsStep(workflows.Step): + action_class = AddFWGPortsAction + template_name = "project/firewalls_v2/_update_ports.html" + contributes = ("ports") + + def contribute(self, data, context): + if data: + ports = self.workflow.request.POST.getlist("port") + if ports: + ports = [r for r in ports if r != ''] + context['ports'] = ports + else: + context['ports'] = [] + return context + + +class AddFirewallGroupAction(workflows.Action): + name = forms.CharField(max_length=80, + label=_("Name"), + required=False) + description = forms.CharField(max_length=80, + label=_("Description"), + required=False) + ingress_firewall_policy_id = forms.ThemableChoiceField( + label=_("Ingress Policy"), + required=False) + egress_firewall_policy_id = forms.ThemableChoiceField( + label=_("Egress Policy"), + required=False) + admin_state_up = forms.BooleanField( + label=_("Admin State"), initial=True, required=False) + shared = forms.BooleanField( + label=_("Shared"), initial=False, required=False) + + def __init__(self, request, *args, **kwargs): + super(AddFirewallGroupAction, self).__init__(request, *args, **kwargs) + + firewall_policy_id_choices = [('', _("Select a Policy"))] + try: + tenant_id = self.request.user.tenant_id + policies = api_fwaas_v2.policy_list_for_tenant(request, tenant_id) + policies = sorted(policies, key=attrgetter('name')) + except Exception as e: + exceptions.handle(request, + _('Unable to retrieve policy list (%s).') % e) + policies = [] + for p in policies: + firewall_policy_id_choices.append((p.id, p.name_or_id)) + self.fields['ingress_firewall_policy_id'].choices = \ + firewall_policy_id_choices + self.fields['egress_firewall_policy_id'].choices = \ + firewall_policy_id_choices + + def clean(self): + cleaned_data = super(AddFirewallGroupAction, self).clean() + for field in ('ingress_firewall_policy_id', + 'egress_firewall_policy_id'): + if not cleaned_data[field]: + cleaned_data[field] = None + return cleaned_data + + class Meta(object): + name = _("FirewallGroup") + permissions = ('openstack.services.network',) + help_text = _("Create a firewall group based on a policy.\n\n" + "A firewall group represents a logical firewall " + "resource that a tenant can instantiate and manage. " + "A firewall group must be associated with one policy, " + "all other fields are optional.") + + +class AddFirewallGroupStep(workflows.Step): + action_class = AddFirewallGroupAction + contributes = ("name", "description", "admin_state_up", "shared", + "ingress_firewall_policy_id", + "egress_firewall_policy_id") + + def contribute(self, data, context): + context = super(AddFirewallGroupStep, self).contribute(data, context) + return context + + +class AddFirewallGroup(workflows.Workflow): + slug = "addfirewallgroup" + name = _("Add Firewall Group") + finalize_button_name = _("Add") + success_message = _('Added Firewall Group"%s".') + failure_message = _('Unable to add Firewall Group "%s".') + success_url = "horizon:project:firewalls_v2:index" + default_steps = (AddFirewallGroupStep, AddFWGPortsStep) + + def format_status_message(self, message): + return message % self.context.get('name') + + def handle(self, request, context): + try: + api_fwaas_v2.firewall_group_create(request, **context) + return True + except Exception as e: + msg = self.format_status_message(self.failure_message) + str(e) + exceptions.handle(request, msg) + return False diff --git a/neutron_fwaas_dashboard/enabled/_7010_project_firewalls_common.py b/neutron_fwaas_dashboard/enabled/_7010_project_firewalls_common.py new file mode 100644 index 0000000..e8b0cfa --- /dev/null +++ b/neutron_fwaas_dashboard/enabled/_7010_project_firewalls_common.py @@ -0,0 +1,20 @@ +# 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. + +# FEATURE just declares this enabled file needs to be loaded. +# This is required for ADD_INSTALLED_APPS to be processed. +# The value itself has no meaning in the current horizon. +FEATURE = 'neutron-fwaas-dashboard' + +ADD_INSTALLED_APPS = ['neutron_fwaas_dashboard'] +AUTO_DISCOVER_STATIC_FILES = True +ADD_SCSS_FILES = ['neutron_fwaas_dashboard/scss/firewalls.scss'] diff --git a/neutron_fwaas_dashboard/enabled/_7012_project_firewalls_v2_panel.py b/neutron_fwaas_dashboard/enabled/_7012_project_firewalls_v2_panel.py new file mode 100644 index 0000000..1393ecc --- /dev/null +++ b/neutron_fwaas_dashboard/enabled/_7012_project_firewalls_v2_panel.py @@ -0,0 +1,22 @@ +# 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. + +# The slug of the panel to be added to HORIZON_CONFIG. Required. +PANEL = 'firewalls_v2' +# The slug of the dashboard the PANEL associated with. Required. +PANEL_DASHBOARD = 'project' +# The slug of the panel group the PANEL is associated with. +PANEL_GROUP = 'network' + +# Python panel class of the PANEL to be added. +ADD_PANEL = ('neutron_fwaas_dashboard.dashboards.project.' + 'firewalls_v2.panel.Firewall_V2') diff --git a/neutron_fwaas_dashboard/enabled/__init__.py b/neutron_fwaas_dashboard/enabled/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/neutron_fwaas_dashboard/local_settings.d/_7000_neutron_fwaas.py b/neutron_fwaas_dashboard/local_settings.d/_7000_neutron_fwaas.py new file mode 100644 index 0000000..5ddb37f --- /dev/null +++ b/neutron_fwaas_dashboard/local_settings.d/_7000_neutron_fwaas.py @@ -0,0 +1,16 @@ +# 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 django.conf import settings + + +settings.POLICY_FILES['neutron-fwaas'] = 'neutron-fwaas-policy.json' diff --git a/neutron_fwaas_dashboard/locale/.placeholder b/neutron_fwaas_dashboard/locale/.placeholder new file mode 100644 index 0000000..e69de29 diff --git a/neutron_fwaas_dashboard/locale/de/LC_MESSAGES/django.po b/neutron_fwaas_dashboard/locale/de/LC_MESSAGES/django.po new file mode 100644 index 0000000..a697cf7 --- /dev/null +++ b/neutron_fwaas_dashboard/locale/de/LC_MESSAGES/django.po @@ -0,0 +1,649 @@ +# Frank Kloeker , 2016. #zanata +# Robert Simai , 2016. #zanata +# Andreas Jaeger , 2017. #zanata +# Frank Kloeker , 2017. #zanata +# Robert Simai , 2017. #zanata +# Andreas Jaeger , 2018. #zanata +# Frank Kloeker , 2018. #zanata +# Reik Keutterling , 2018. #zanata +# Robert Simai , 2018. #zanata +# Andreas Jaeger , 2019. #zanata +# Robert Simai , 2019. #zanata +msgid "" +msgstr "" +"Project-Id-Version: neutron-fwaas-dashboard VERSION\n" +"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" +"POT-Creation-Date: 2019-04-25 13:36+0000\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"PO-Revision-Date: 2019-05-03 02:14+0000\n" +"Last-Translator: Robert Simai \n" +"Language-Team: German\n" +"Language: de\n" +"X-Generator: Zanata 4.3.3\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" + +msgid "-" +msgstr "-" + +msgid "ALLOW" +msgstr "Erlauben" + +msgctxt "Action Name of a Firewall Rule" +msgid "ALLOW" +msgstr "Erlauben" + +msgid "ANY" +msgstr "ALLE" + +msgid "Action" +msgstr "Aktion" + +msgid "Action for the firewall rule" +msgstr "Aktion für die Firewallregel" + +msgctxt "Current status of a Firewall Group" +msgid "Active" +msgstr "Aktiv" + +msgid "Add" +msgstr "Hinzufügen" + +msgid "Add Firewall Group" +msgstr "Firewall-Gruppe hinzufügen" + +msgid "Add New Firewall Group" +msgstr "Neue Firewall-Gruppe hinzufügen" + +msgid "Add New Policy" +msgstr "Neue Richtlinie hinzufügen" + +msgid "Add New Rule" +msgstr "Neue Regel hinzufügen" + +msgid "Add Policy" +msgstr "Richtlinie hinzufügen" + +msgid "Add Port" +msgstr "Port hinzufügen" + +msgid "Add Port to Firewall Group" +msgstr "Port zur Firewallgruppe hinzufügen" + +msgid "Add Rule" +msgstr "Regel hinzufügen" + +msgid "Add port to Firewall Group {{ name }}" +msgstr "Port zur Firewallgruppe {{ name }} hinzufügen" + +#, python-format +msgid "Added Firewall Group\"%s\"." +msgstr "Firewall-Gruppe \"%s\" hinzugefügt." + +#, python-format +msgid "Added Policy \"%s\"." +msgstr "Richtlinie \"%s\" hinzugefügt." + +#, python-format +msgid "Added Rule \"%s\"." +msgstr "Regel \"%s\" wurde hinzugefügt." + +#, python-format +msgid "Added the port(s) to the firewall group %s successfully." +msgstr "Port(s) wurde(n) zur Firewall-Gruppe %s erfolgreich hinzugefügt." + +msgid "Admin State" +msgstr "Adminstatus" + +msgid "Admin State Up" +msgstr "Adminstatus aktiv" + +msgid "After" +msgstr "Nachher" + +msgid "Audited" +msgstr "Geprüft" + +msgid "Available Ports" +msgstr "Verfügbare Ports" + +msgid "Available Rules" +msgstr "Verfügbare Regeln" + +msgid "Before" +msgstr "Vorher" + +msgid "Choose port(s) from Available Ports. " +msgstr "Wählen Sie aus den verfügbaren Ports aus." + +msgid "" +"Choose rule(s) from Available Rules to Selected Rule by push button or drag " +"and drop, you may change their order by drag and drop as well." +msgstr "" +"Wählen Sie aus den \"Verfügbaren Regeln\" Regel(n) aus, indem Sie den Knopf " +"drücken oder mit Ziehen und Ablegen. Sie können auch die Reihenfolge mit " +"Ziehen und Ablegen ändern." + +msgid "" +"Choose the rule you want to insert. Specify either the rule you want to " +"insert immediately before, or the rule to insert immediately after. If both " +"are specified, the prior takes precedence." +msgstr "" +"Wählen Sie die Regel aus, die Sie einfügen wollen. Spezifizieren Sie " +"entweder die Regel unmittelbar vor der Sie einfügen wollen oder die Regel " +"unmittelbar nach der Sie einfügen wollen. Wenn beide angegeben werden, hat " +"die vorherige Vorrang. " + +msgid "Choose the rule you want to remove." +msgstr "Zu entfernende Richtlinie auswählen." + +msgid "Create Firewall Group" +msgstr "Firewall-Gruppe erstellen" + +msgid "Create a Firewall Group with selected ports." +msgstr "Erstellen einer Firewall-Gruppe mit den ausgewählten Ports." + +msgid "" +"Create a firewall group based on a policy.\n" +"\n" +"A firewall group represents a logical firewall resource that a tenant can " +"instantiate and manage. A firewall group must be associated with one policy, " +"all other fields are optional." +msgstr "" +"Erzeugen einer Firewall-Gruppe auf Basis einer Richtlinie\n" +"\n" +"Eine Firewall-Gruppe repräsentiert eine logische Firewall-Ressource, die ein " +"Mandant instanzieren und verwalten kann. Eine Firewall-Gruppe muss mit einer " +"Richtlinie verbunden werden. Alle anderen Felder sind optional." + +msgid "" +"Create a firewall policy with an ordered list of firewall rules.\n" +"\n" +"A firewall policy is an ordered collection of firewall rules. So if the " +"traffic matches the first rule, the other rules are not executed. If the " +"traffic does not match the current rule, then the next rule is executed. A " +"firewall policy has the following attributes:\n" +"\n" +"
        • Shared: A firewall policy can be shared across tenants. Thus it can also " +"be made part of an audit workflow wherein the firewall policy can be audited " +"by the relevant entity that is authorized.
        • Audited: When audited is " +"set to True, it indicates that the firewall policy has been audited. Each " +"time the firewall policy or the associated firewall rules are changed, this " +"attribute will be set to False and will have to be explicitly set to True " +"through an update operation.
        • \n" +"The name field is required, all others are optional." +msgstr "" +"Erzeugen einer Firewall-Richtlinie mit einer geordneten Liste von Regeln.\n" +"\n" +"Eine Firewall-Richtlinie ist eine geordnete Sammlung von Firewall-Regeln. " +"Sobald eine Regel auf den Datenverkehr zutrifft, werden die weiteren Regeln " +"nicht mehr ausgeführt. Solange die Regeln nicht zutreffen, werden weitere " +"Regeln ausgeführt. Eine Firewall-Richtlinie hat die folgenden Attribute:\n" +"\n" +"
        • Geteilt/Shared: eine Firewall-Richtlinie kann mit mehreren Mandanten " +"geteilt werden. Dadurch kann sie auch als Teil eines Audit-Workflows " +"verwendet werden, um von einer berechtigten Einheit/Organisation/Person " +"geprüft zu werden.
        • Auditiert/Audited: wenn dies auf wahr/true " +"gesetzt ist, zeigt es an, dass die Richtlinie geprüft wurde. Jedes Mal, wenn " +"eine Richtlinie oder eine der Regeln darin geändert wird, wird der Indikator " +"wieder auf falsch/false gesetzt und muss durch eine Update-Operation " +"explizit wieder auf wahr/true gesetzt werden.
        • \n" +"Das Namensfeld ist erforderlich, alle anderen sind optional." + +msgid "" +"Create a firewall rule.\n" +"\n" +"A Firewall rule is an association of the following attributes:\n" +"\n" +"
        • IP Addresses: The addresses from/to which the traffic filtration needs " +"to be applied.
        • IP Version: The type of IP packets (IP V4/V6) that " +"needs to be filtered.
        • Protocol: Type of packets (UDP, ICMP, TCP, " +"Any) that needs to be checked.
        • Action: Action is the type of " +"filtration required, it can be Reject/Deny/Allow data packets.
        • \n" +"The protocol and action fields are required, all others are optional." +msgstr "" +"Firewall-Regel anlegen\n" +"\n" +"Eine Firewall-Regel bezieht sich auf die folgenden Attribute:\n" +"\n" +"
        • IP-Adresse: die Adresse, auf zu/von der der Datenverkehr gefiltert wird." +"
        • IP-Version: der IP Pakettyp (IPV4/6) der gefiltert wird.
        • Protokoll: der Pakettyp (UDP, ICMP, TCP, Any) der kontrolliert wird.
        • Aktion: die vorhergesehene Filteraktion, möglich ist Abweisen/" +"Verweigern/Erlauben für Datenpakete.
        • \n" +"Die Protokoll- und Aktionsfelder sind erforderlich, andere optional." + +msgid "Create a policy with selected rules." +msgstr "Richtlinie mit ausgewählten Regeln erstellen." + +msgctxt "Current status of a Firewall Group" +msgid "Created" +msgstr "Erstellt" + +msgid "DENY" +msgstr "Verweigern" + +msgctxt "Action Name of a Firewall Rule" +msgid "DENY" +msgstr "Verweigern" + +msgctxt "Admin state of a Firewall Group" +msgid "DOWN" +msgstr "INAKTIV" + +msgid "Delete Firewall Group" +msgid_plural "Delete Firewall Groups" +msgstr[0] "Firewall-Gruppe löschen" +msgstr[1] "Firewall-Gruppen löschen" + +msgid "Delete Policy" +msgid_plural "Delete Policies" +msgstr[0] "Richtlinie löschen" +msgstr[1] "Richtlinien löschen" + +msgid "Delete Rule" +msgid_plural "Delete Rules" +msgstr[0] "Regel löschen" +msgstr[1] "Regeln löschen" + +msgid "Description" +msgstr "Beschreibung" + +msgid "Description:" +msgstr "Beschreibung:" + +msgid "Destination IP" +msgstr "Ziel-IP" + +msgid "Destination IP Address" +msgstr "Ziel-IP-Adresse" + +msgid "Destination IP Address/Subnet" +msgstr "Ziel-IP-Adresse oder Subnetz" + +msgid "Destination IP address or subnet" +msgstr "Ziel-IP-Adresse oder Subnetz" + +msgid "Destination Port" +msgstr "Ziel-Port" + +msgid "Destination Port/Port Range" +msgstr "Ziel-Port/-Portbereich" + +msgid "Destination port (integer in [1, 65535] or range in a:b)" +msgstr "Ziel-Port (Ganzzahl aus [1, 65535] oder Bereich als a:b)" + +msgctxt "Current status of a Firewall Group" +msgid "Down" +msgstr "Inaktiv" + +msgid "Edit Firewall Group" +msgstr "Firewall-Gruppe bearbeiten" + +msgid "Edit FirewallGroup {{ name }}" +msgstr "Firewall-Gruppe bearbeiten {{ name }}" + +msgid "Edit Policy" +msgstr "Richtlinie bearbeiten" + +msgid "Edit Policy {{ name }}" +msgstr "Richtlinie bearbeiten {{ name }}" + +msgid "Edit Rule" +msgstr "Regel bearbeiten" + +msgid "Edit Rule {{ name }}" +msgstr "Regel bearbeiten {{ name }}" + +msgid "Egress Policy" +msgstr "Austrittsrichtlinie" + +msgid "Egress Policy ID" +msgstr "Ausgang Richtlinien-ID" + +msgid "Enabled" +msgstr "Aktiviert" + +msgctxt "Current status of a Firewall Group" +msgid "Error" +msgstr "Fehler" + +#, python-format +msgid "Failed to add the port(s) to the firewall group %(name)s: %(reason)s" +msgstr "" +"Port konnte zur der Firewallgruppe %(name)s nicht hinzugefügt werden: " +"%(reason)s" + +#, python-format +msgid "Failed to insert rule to policy %(name)s: %(reason)s" +msgstr "Regel konnte nicht eingefügt werden in Richtlinie %(name)s: %(reason)s" + +#, python-format +msgid "Failed to remove rule from policy %(name)s: %(reason)s" +msgstr "Entfernen der Regel von Richtlinie %(name)s fehlgeschlagen: %(reason)s" + +#, python-format +msgid "" +"Failed to remove the port(s) from the firewall group %(name)s: %(reason)s" +msgstr "" +"Port konnte von der Firewallgruppe %(name)s nicht entfernt werden: %(reason)s" + +#, python-format +msgid "Failed to retrieve available rules: %s" +msgstr "Verfügbare Regeln konnten nicht abgerufen werden: %s" + +#, python-format +msgid "Failed to retrieve current rules in policy %(name)s: %(reason)s" +msgstr "" +"Aktuelle Regeln in Richtline %(name)s konnten nicht abgerufen werden: " +"%(reason)s" + +msgid "Failed to retrieve port list of the firewall group." +msgstr "Abruf der Portliste der Firewall-Gruppe ist fehlgeschlagen." + +#, python-format +msgid "Failed to update firewall group %(name)s: %(reason)s" +msgstr "Firewallgruppe %(name)s konnte nicht aktualisiert werden: %(reason)s" + +#, python-format +msgid "Failed to update policy %(name)s: %(reason)s" +msgstr "Fehler beim Aktualisieren der Richtlinie %(name)s: %(reason)s" + +#, python-format +msgid "Failed to update rule %(name)s: %(reason)s" +msgstr "Regel %(name)s konnte nicht aktualisiert werden: %(reason)s" + +msgid "Firewall Groups" +msgstr "Firewall-Gruppe" + +msgid "Firewall Policies" +msgstr "Firewallrichtlinien" + +msgid "Firewall Rules" +msgstr "Firewallregeln" + +#, python-format +msgid "Firewall group %s was successfully updated." +msgstr "Firewallgruppe %s wurde erfolgreich aktualisiert." + +msgid "FirewallGroup" +msgstr "Firewall-Gruppe" + +msgid "ICMP" +msgstr "ICMP" + +msgid "ID" +msgstr "ID" + +msgid "IP Version" +msgstr "IP Version" + +msgid "IP Version for Firewall Rule" +msgstr "IP-Version der Firewall-Regel" + +msgctxt "Current status of a Firewall Group" +msgid "Inactive" +msgstr "Inaktiv" + +msgid "Ingress Policy" +msgstr "Eingangsrichtlinie" + +msgid "Ingress Policy ID" +msgstr "Eingang Richtlinien-ID" + +msgid "Insert Rule" +msgstr "Regel einfügen" + +msgid "Insert Rule to Policy" +msgstr "Regel in Richtlinie hinzufügen" + +msgid "Label" +msgstr "Beschreibung" + +msgid "Name" +msgstr "Name" + +msgid "No options available" +msgstr "Keine Optionen verfügbar" + +msgid "None" +msgstr "Keine" + +msgctxt "Current status of a Firewall Group" +msgid "Pending Create" +msgstr "Geplante Erstellung" + +msgctxt "Current status of a Firewall Group" +msgid "Pending Delete" +msgstr "Geplante Löschung" + +msgctxt "Current status of a Firewall Group" +msgid "Pending Update" +msgstr "Geplante Aktualisierung" + +msgid "Policies" +msgstr "Richtlinien" + +msgid "Policy" +msgstr "Richtlinie" + +#, python-format +msgid "Policy %s was successfully updated." +msgstr "Richtlinie %s wurde wurde erfolgreich aktualisiert." + +msgid "Ports" +msgstr "Ports" + +msgid "Project ID" +msgstr "Projekt-ID" + +msgid "Protocol" +msgstr "Protokoll" + +msgid "Protocol for the firewall rule" +msgstr "Protokoll für die Firewallregel" + +msgid "REJECT" +msgstr "Abweisen" + +msgctxt "Action Name of a Firewall Rule" +msgid "REJECT" +msgstr "Abweisen" + +msgid "Remove Port" +msgstr "Port entfernen" + +msgid "Remove Port from Firewall Group" +msgstr "Port von der Firewallgruppe entfernen" + +msgid "Remove Rule" +msgstr "Regel entfernen" + +msgid "Remove Rule from Policy" +msgstr "Regel aus Richtlinie entfernen" + +msgid "Remove port from FirewallGroup {{ name }}" +msgstr "Port aus Firewall-Gruppe entfernen {{ name }}" + +#, python-format +msgid "Removed the port(s) from the firewall group %s successfully." +msgstr "Port(s) wurde(n) von der Firewall-Gruppe %s erfolgreich entfernt." + +msgid "Rule" +msgstr "Regel" + +#, python-format +msgid "Rule %(rule)s was successfully inserted to policy %(policy)s." +msgstr "Regel %(rule)s wurde erfolgreich in Richtlinie %(policy)s eingefügt ." + +#, python-format +msgid "Rule %(rule)s was successfully removed from policy %(policy)s." +msgstr "Regel %(rule)s wurde erfolgreich aus Richtlinie %(policy)s entfernt." + +#, python-format +msgid "Rule %s was successfully updated." +msgstr "Regel %s wurde erfolgreich aktualisiert." + +msgid "Rules" +msgstr "Regeln" + +msgid "Save Changes" +msgstr "Änderungen speichern" + +msgid "Scheduled deletion of Firewall Group" +msgid_plural "Scheduled deletion of Firewall Groups" +msgstr[0] "Geplante Löschung der Firewall-Gruppe" +msgstr[1] "Geplante Löschung der Firewall-Gruppen" + +msgid "Scheduled deletion of Policy" +msgid_plural "Scheduled deletion of Policies" +msgstr[0] "Geplante Löschung der Richtlinie" +msgstr[1] "Geplante Löschung von Richtlinien" + +msgid "Scheduled deletion of Rule" +msgid_plural "Scheduled deletion of Rules" +msgstr[0] "Geplante Löschung der Regel" +msgstr[1] "Geplante Löschung von Regeln" + +msgid "Select a Policy" +msgstr "Richtlinie auswählen" + +msgid "Select ports for your firewall group." +msgstr "Wählen Sie Ports für Ihre Firewall-Gruppe." + +msgid "Select rules for your policy." +msgstr "Regeln für Richtlinie auswählen." + +msgid "Selected Ports" +msgstr "Ausgewählte Ports" + +msgid "Selected Rules" +msgstr "Ausgewählte Regeln" + +msgid "Shared" +msgstr "Geteilt" + +msgid "Source IP" +msgstr "Quell-IP" + +msgid "Source IP Address" +msgstr "Quell-IP-Adresse" + +msgid "Source IP Address/Subnet" +msgstr "Quell-IP-Adresse oder Subnetz" + +msgid "Source IP address or subnet" +msgstr "Quell-IP-Adresse oder Subnetz" + +msgid "Source Port" +msgstr "Quell-Port" + +msgid "Source Port/Port Range" +msgstr "Quell-Port/-Portbereich" + +msgid "Source port (integer in [1, 65535] or range in a:b)" +msgstr "Quell-Port (Ganzzahl aus [1, 65535] oder Bereich als a:b)" + +msgid "" +"Source/Destination Network Address and IP version are inconsistent. Please " +"make them consistent." +msgstr "" +"Quelle/Ziel Netzwekadresse und IP-Version sind nicht konsistent. Bitte " +"ändern Sie dies." + +msgid "Status" +msgstr "Status" + +msgid "TCP" +msgstr "TCP" + +msgid "UDP" +msgstr "UDP" + +msgctxt "Admin state of a Firewall Group" +msgid "UP" +msgstr "AKTIV" + +#, python-format +msgid "Unable to add Firewall Group \"%s\"." +msgstr "Firewall-Gruppe \"%s\" konnte nicht hinzugefügt werden." + +#, python-format +msgid "Unable to add Policy \"%s\"." +msgstr "Richtlinie \"%s\" konnte nicht hinzugefügt werden." + +#, python-format +msgid "Unable to add Rule \"%s\"." +msgstr "Regel \"%s\" kann nicht hinzugefügt werden" + +#, python-format +msgid "Unable to delete firewall group. %s" +msgstr "Firewall-Gruppe kann nicht gelöscht werden: %s" + +#, python-format +msgid "Unable to delete policy. %s" +msgstr "Richtlinie %s konnte nicht gelöscht werden." + +#, python-format +msgid "Unable to delete rule. %s" +msgstr "Regel %s kann nicht gelöscht werden." + +msgid "Unable to retrieve firewall group details." +msgstr "Firewall-Gruppendetails können nicht abgerufen werden." + +msgid "Unable to retrieve firewall group list." +msgstr "Firewall-Gruppenliste kann nicht abgerufen werden." + +msgid "Unable to retrieve firewallgroup details." +msgstr "Konnte Firewall-Gruppendetails nicht abrufen." + +msgid "Unable to retrieve policies list." +msgstr "Liste der Richtlinien kann nicht abgerufen werden." + +msgid "Unable to retrieve policy details." +msgstr "Richtliniendetails können nicht abgerufen werden." + +#, python-format +msgid "Unable to retrieve policy list (%s)." +msgstr "Richtlinienliste kann nicht abgerufen werden (%s)." + +msgid "Unable to retrieve policy list." +msgstr "Liste der Richtlinien kann nicht abgerufen werden." + +#, python-format +msgid "Unable to retrieve ports (%s)." +msgstr "Ports können nicht abgerufen werden (%s)." + +msgid "Unable to retrieve rule details." +msgstr "Details der Regeln können nicht abgerufen werden. " + +#, python-format +msgid "Unable to retrieve rules (%s)." +msgstr "Regeln können nicht abgerufen werden (%s)." + +msgid "Unable to retrieve rules list." +msgstr "Liste der Regeln kann nicht abgerufen werden." + +msgid "Value" +msgstr "Wert" + +msgid "You may add ports to firewall group here." +msgstr "Hier können Sie Ports zur Firewallgruppe hinzufügen." + +msgid "You may remove ports from firewall group here." +msgstr "Hier können Sie Ports von der Firewallgruppe entfernen." + +msgid "You may update firewall group details here." +msgstr "Hier können Sie die Firewallgruppendetails bearbeiten." + +msgid "" +"You may update policy details here. Use 'Insert Rule' or 'Remove Rule' links " +"instead to insert or remove a rule." +msgstr "" +"Hier können Sie Richtliniendetails aktualisieren. Wählen Sie die " +"Verknüpfungen 'Regel einfügen' oder 'Regel entfernen', um eine Regel " +"einzufügen oder zu entfernen." + +msgid "You may update rule details here." +msgstr "Hier können Sie die Details der Regeln aktualisieren." diff --git a/neutron_fwaas_dashboard/locale/en_GB/LC_MESSAGES/django.po b/neutron_fwaas_dashboard/locale/en_GB/LC_MESSAGES/django.po new file mode 100644 index 0000000..caeb378 --- /dev/null +++ b/neutron_fwaas_dashboard/locale/en_GB/LC_MESSAGES/django.po @@ -0,0 +1,633 @@ +# Andi Chandler , 2017. #zanata +# Andi Chandler , 2018. #zanata +# Andi Chandler , 2019. #zanata +msgid "" +msgstr "" +"Project-Id-Version: neutron-fwaas-dashboard VERSION\n" +"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" +"POT-Creation-Date: 2019-02-19 16:16+0000\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"PO-Revision-Date: 2019-01-26 03:30+0000\n" +"Last-Translator: Andi Chandler \n" +"Language-Team: English (United Kingdom)\n" +"Language: en_GB\n" +"X-Generator: Zanata 4.3.3\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" + +msgid "-" +msgstr "-" + +msgid "ALLOW" +msgstr "ALLOW" + +msgctxt "Action Name of a Firewall Rule" +msgid "ALLOW" +msgstr "ALLOW" + +msgid "ANY" +msgstr "ANY" + +msgid "Action" +msgstr "Action" + +msgid "Action for the firewall rule" +msgstr "Action for the firewall rule" + +msgctxt "Current status of a Firewall Group" +msgid "Active" +msgstr "Active" + +msgid "Add" +msgstr "Add" + +msgid "Add Firewall Group" +msgstr "Add Firewall Group" + +msgid "Add New Firewall Group" +msgstr "Add New Firewall Group" + +msgid "Add New Policy" +msgstr "Add New Policy" + +msgid "Add New Rule" +msgstr "Add New Rule" + +msgid "Add Policy" +msgstr "Add Policy" + +msgid "Add Port" +msgstr "Add Port" + +msgid "Add Port to Firewall Group" +msgstr "Add Port to Firewall Group" + +msgid "Add Rule" +msgstr "Add Rule" + +msgid "Add port to Firewall Group {{ name }}" +msgstr "Add port to Firewall Group {{ name }}" + +#, python-format +msgid "Added Firewall Group\"%s\"." +msgstr "Added Firewall Group\"%s\"." + +#, python-format +msgid "Added Policy \"%s\"." +msgstr "Added Policy \"%s\"." + +#, python-format +msgid "Added Rule \"%s\"." +msgstr "Added Rule \"%s\"." + +#, python-format +msgid "Added the port(s) to the firewall group %s successfully." +msgstr "Added the port(s) to the firewall group %s successfully." + +msgid "Admin State" +msgstr "Admin State" + +msgid "Admin State Up" +msgstr "Admin State Up" + +msgid "After" +msgstr "After" + +msgid "Audited" +msgstr "Audited" + +msgid "Available Ports" +msgstr "Available Ports" + +msgid "Available Rules" +msgstr "Available Rules" + +msgid "Before" +msgstr "Before" + +msgid "Choose port(s) from Available Ports. " +msgstr "Choose port(s) from Available Ports. " + +msgid "" +"Choose rule(s) from Available Rules to Selected Rule by push button or drag " +"and drop, you may change their order by drag and drop as well." +msgstr "" +"Choose rule(s) from Available Rules to Selected Rule by push button or drag " +"and drop, you may change their order by drag and drop as well." + +msgid "" +"Choose the rule you want to insert. Specify either the rule you want to " +"insert immediately before, or the rule to insert immediately after. If both " +"are specified, the prior takes precedence." +msgstr "" +"Choose the rule you want to insert. Specify either the rule you want to " +"insert immediately before, or the rule to insert immediately after. If both " +"are specified, the prior takes precedence." + +msgid "Choose the rule you want to remove." +msgstr "Choose the rule you want to remove." + +msgid "Create Firewall Group" +msgstr "Create Firewall Group" + +msgid "Create a Firewall Group with selected ports." +msgstr "Create a Firewall Group with selected ports." + +msgid "" +"Create a firewall group based on a policy.\n" +"\n" +"A firewall group represents a logical firewall resource that a tenant can " +"instantiate and manage. A firewall group must be associated with one policy, " +"all other fields are optional." +msgstr "" +"Create a firewall group based on a policy.\n" +"\n" +"A firewall group represents a logical firewall resource that a tenant can " +"instantiate and manage. A firewall group must be associated with one policy, " +"all other fields are optional." + +msgid "" +"Create a firewall policy with an ordered list of firewall rules.\n" +"\n" +"A firewall policy is an ordered collection of firewall rules. So if the " +"traffic matches the first rule, the other rules are not executed. If the " +"traffic does not match the current rule, then the next rule is executed. A " +"firewall policy has the following attributes:\n" +"\n" +"
        • Shared: A firewall policy can be shared across tenants. Thus it can also " +"be made part of an audit workflow wherein the firewall policy can be audited " +"by the relevant entity that is authorized.
        • Audited: When audited is " +"set to True, it indicates that the firewall policy has been audited. Each " +"time the firewall policy or the associated firewall rules are changed, this " +"attribute will be set to False and will have to be explicitly set to True " +"through an update operation.
        • \n" +"The name field is required, all others are optional." +msgstr "" +"Create a firewall policy with an ordered list of firewall rules.\n" +"\n" +"A firewall policy is an ordered collection of firewall rules. So if the " +"traffic matches the first rule, the other rules are not executed. If the " +"traffic does not match the current rule, then the next rule is executed. A " +"firewall policy has the following attributes:\n" +"\n" +"
        • Shared: A firewall policy can be shared across tenants. Thus it can also " +"be made part of an audit workflow wherein the firewall policy can be audited " +"by the relevant entity that is authorized.
        • Audited: When audited is " +"set to True, it indicates that the firewall policy has been audited. Each " +"time the firewall policy or the associated firewall rules are changed, this " +"attribute will be set to False and will have to be explicitly set to True " +"through an update operation.
        • \n" +"The name field is required, all others are optional." + +msgid "" +"Create a firewall rule.\n" +"\n" +"A Firewall rule is an association of the following attributes:\n" +"\n" +"
        • IP Addresses: The addresses from/to which the traffic filtration needs " +"to be applied.
        • IP Version: The type of IP packets (IP V4/V6) that " +"needs to be filtered.
        • Protocol: Type of packets (UDP, ICMP, TCP, " +"Any) that needs to be checked.
        • Action: Action is the type of " +"filtration required, it can be Reject/Deny/Allow data packets.
        • \n" +"The protocol and action fields are required, all others are optional." +msgstr "" +"Create a firewall rule.\n" +"\n" +"A Firewall rule is an association of the following attributes:\n" +"\n" +"
        • IP Addresses: The addresses from/to which the traffic filtration needs " +"to be applied.
        • IP Version: The type of IP packets (IP V4/V6) that " +"needs to be filtered.
        • Protocol: Type of packets (UDP, ICMP, TCP, " +"Any) that needs to be checked.
        • Action: Action is the type of " +"filtration required, it can be Reject/Deny/Allow data packets.
        • \n" +"The protocol and action fields are required, all others are optional." + +msgid "Create a policy with selected rules." +msgstr "Create a policy with selected rules." + +msgctxt "Current status of a Firewall Group" +msgid "Created" +msgstr "Created" + +msgid "DENY" +msgstr "DENY" + +msgctxt "Action Name of a Firewall Rule" +msgid "DENY" +msgstr "DENY" + +msgctxt "Admin state of a Firewall Group" +msgid "DOWN" +msgstr "DOWN" + +msgid "Delete Firewall Group" +msgid_plural "Delete Firewall Groups" +msgstr[0] "Delete Firewall Group" +msgstr[1] "Delete Firewall Groups" + +msgid "Delete Policy" +msgid_plural "Delete Policies" +msgstr[0] "Delete Policy" +msgstr[1] "Delete Policies" + +msgid "Delete Rule" +msgid_plural "Delete Rules" +msgstr[0] "Delete Rule" +msgstr[1] "Delete Rules" + +msgid "Description" +msgstr "Description" + +msgid "Description:" +msgstr "Description:" + +msgid "Destination IP" +msgstr "Destination IP" + +msgid "Destination IP Address" +msgstr "Destination IP Address" + +msgid "Destination IP Address/Subnet" +msgstr "Destination IP Address/Subnet" + +msgid "Destination IP address or subnet" +msgstr "Destination IP address or subnet" + +msgid "Destination Port" +msgstr "Destination Port" + +msgid "Destination Port/Port Range" +msgstr "Destination Port/Port Range" + +msgid "Destination port (integer in [1, 65535] or range in a:b)" +msgstr "Destination port (integer in [1, 65535] or range in a:b)" + +msgctxt "Current status of a Firewall Group" +msgid "Down" +msgstr "Down" + +msgid "Edit Firewall Group" +msgstr "Edit Firewall Group" + +msgid "Edit FirewallGroup {{ name }}" +msgstr "Edit FirewallGroup {{ name }}" + +msgid "Edit Policy" +msgstr "Edit Policy" + +msgid "Edit Policy {{ name }}" +msgstr "Edit Policy {{ name }}" + +msgid "Edit Rule" +msgstr "Edit Rule" + +msgid "Edit Rule {{ name }}" +msgstr "Edit Rule {{ name }}" + +msgid "Egress Policy" +msgstr "Egress Policy" + +msgid "Egress Policy ID" +msgstr "Egress Policy ID" + +msgid "Enabled" +msgstr "Enabled" + +msgctxt "Current status of a Firewall Group" +msgid "Error" +msgstr "Error" + +#, python-format +msgid "Failed to add the port(s) to the firewall group %(name)s: %(reason)s" +msgstr "Failed to add the port(s) to the firewall group %(name)s: %(reason)s" + +#, python-format +msgid "Failed to insert rule to policy %(name)s: %(reason)s" +msgstr "Failed to insert rule to policy %(name)s: %(reason)s" + +#, python-format +msgid "Failed to remove rule from policy %(name)s: %(reason)s" +msgstr "Failed to remove rule from policy %(name)s: %(reason)s" + +#, python-format +msgid "" +"Failed to remove the port(s) from the firewall group %(name)s: %(reason)s" +msgstr "" +"Failed to remove the port(s) from the firewall group %(name)s: %(reason)s" + +#, python-format +msgid "Failed to retrieve available rules: %s" +msgstr "Failed to retrieve available rules: %s" + +#, python-format +msgid "Failed to retrieve current rules in policy %(name)s: %(reason)s" +msgstr "Failed to retrieve current rules in policy %(name)s: %(reason)s" + +msgid "Failed to retrieve port list of the firewall group." +msgstr "Failed to retrieve port list of the firewall group." + +#, python-format +msgid "Failed to update firewall group %(name)s: %(reason)s" +msgstr "Failed to update firewall group %(name)s: %(reason)s" + +#, python-format +msgid "Failed to update policy %(name)s: %(reason)s" +msgstr "Failed to update policy %(name)s: %(reason)s" + +#, python-format +msgid "Failed to update rule %(name)s: %(reason)s" +msgstr "Failed to update rule %(name)s: %(reason)s" + +msgid "Firewall Groups" +msgstr "Firewall Groups" + +msgid "Firewall Policies" +msgstr "Firewall Policies" + +msgid "Firewall Rules" +msgstr "Firewall Rules" + +#, python-format +msgid "Firewall group %s was successfully updated." +msgstr "Firewall group %s was successfully updated." + +msgid "FirewallGroup" +msgstr "FirewallGroup" + +msgid "ICMP" +msgstr "ICMP" + +msgid "ID" +msgstr "ID" + +msgid "IP Version" +msgstr "IP Version" + +msgid "IP Version for Firewall Rule" +msgstr "IP Version for Firewall Rule" + +msgctxt "Current status of a Firewall Group" +msgid "Inactive" +msgstr "Inactive" + +msgid "Ingress Policy" +msgstr "Ingress Policy" + +msgid "Ingress Policy ID" +msgstr "Ingress Policy ID" + +msgid "Insert Rule" +msgstr "Insert Rule" + +msgid "Insert Rule to Policy" +msgstr "Insert Rule to Policy" + +msgid "Label" +msgstr "Label" + +msgid "Name" +msgstr "Name" + +msgid "No options available" +msgstr "No options available" + +msgid "None" +msgstr "None" + +msgctxt "Current status of a Firewall Group" +msgid "Pending Create" +msgstr "Pending Create" + +msgctxt "Current status of a Firewall Group" +msgid "Pending Delete" +msgstr "Pending Delete" + +msgctxt "Current status of a Firewall Group" +msgid "Pending Update" +msgstr "Pending Update" + +msgid "Policies" +msgstr "Policies" + +msgid "Policy" +msgstr "Policy" + +#, python-format +msgid "Policy %s was successfully updated." +msgstr "Policy %s was successfully updated." + +msgid "Ports" +msgstr "Ports" + +msgid "Project ID" +msgstr "Project ID" + +msgid "Protocol" +msgstr "Protocol" + +msgid "Protocol for the firewall rule" +msgstr "Protocol for the firewall rule" + +msgid "REJECT" +msgstr "REJECT" + +msgctxt "Action Name of a Firewall Rule" +msgid "REJECT" +msgstr "REJECT" + +msgid "Remove Port" +msgstr "Remove Port" + +msgid "Remove Port from Firewall Group" +msgstr "Remove Port from Firewall Group" + +msgid "Remove Rule" +msgstr "Remove Rule" + +msgid "Remove Rule from Policy" +msgstr "Remove Rule from Policy" + +msgid "Remove port from FirewallGroup {{ name }}" +msgstr "Remove port from FirewallGroup {{ name }}" + +#, python-format +msgid "Removed the port(s) from the firewall group %s successfully." +msgstr "Removed the port(s) from the firewall group %s successfully." + +msgid "Rule" +msgstr "Rule" + +#, python-format +msgid "Rule %(rule)s was successfully inserted to policy %(policy)s." +msgstr "Rule %(rule)s was successfully inserted to policy %(policy)s." + +#, python-format +msgid "Rule %(rule)s was successfully removed from policy %(policy)s." +msgstr "Rule %(rule)s was successfully removed from policy %(policy)s." + +#, python-format +msgid "Rule %s was successfully updated." +msgstr "Rule %s was successfully updated." + +msgid "Rules" +msgstr "Rules" + +msgid "Save Changes" +msgstr "Save Changes" + +msgid "Scheduled deletion of Firewall Group" +msgid_plural "Scheduled deletion of Firewall Groups" +msgstr[0] "Scheduled deletion of Firewall Group" +msgstr[1] "Scheduled deletion of Firewall Groups" + +msgid "Scheduled deletion of Policy" +msgid_plural "Scheduled deletion of Policies" +msgstr[0] "Scheduled deletion of Policy" +msgstr[1] "Scheduled deletion of Policies" + +msgid "Scheduled deletion of Rule" +msgid_plural "Scheduled deletion of Rules" +msgstr[0] "Scheduled deletion of Rule" +msgstr[1] "Scheduled deletion of Rules" + +msgid "Select a Policy" +msgstr "Select a Policy" + +msgid "Select ports for your firewall group." +msgstr "Select ports for your firewall group." + +msgid "Select rules for your policy." +msgstr "Select rules for your policy." + +msgid "Selected Ports" +msgstr "Selected Ports" + +msgid "Selected Rules" +msgstr "Selected Rules" + +msgid "Shared" +msgstr "Shared" + +msgid "Source IP" +msgstr "Source IP" + +msgid "Source IP Address" +msgstr "Source IP Address" + +msgid "Source IP Address/Subnet" +msgstr "Source IP Address/Subnet" + +msgid "Source IP address or subnet" +msgstr "Source IP address or subnet" + +msgid "Source Port" +msgstr "Source Port" + +msgid "Source Port/Port Range" +msgstr "Source Port/Port Range" + +msgid "Source port (integer in [1, 65535] or range in a:b)" +msgstr "Source port (integer in [1, 65535] or range in a:b)" + +msgid "" +"Source/Destination Network Address and IP version are inconsistent. Please " +"make them consistent." +msgstr "" +"Source/Destination Network Address and IP version are inconsistent. Please " +"make them consistent." + +msgid "Status" +msgstr "Status" + +msgid "TCP" +msgstr "TCP" + +msgid "UDP" +msgstr "UDP" + +msgctxt "Admin state of a Firewall Group" +msgid "UP" +msgstr "UP" + +#, python-format +msgid "Unable to add Firewall Group \"%s\"." +msgstr "Unable to add Firewall Group \"%s\"." + +#, python-format +msgid "Unable to add Policy \"%s\"." +msgstr "Unable to add Policy \"%s\"." + +#, python-format +msgid "Unable to add Rule \"%s\"." +msgstr "Unable to add Rule \"%s\"." + +#, python-format +msgid "Unable to delete firewall group. %s" +msgstr "Unable to delete firewall group. %s" + +#, python-format +msgid "Unable to delete policy. %s" +msgstr "Unable to delete policy. %s" + +#, python-format +msgid "Unable to delete rule. %s" +msgstr "Unable to delete rule. %s" + +msgid "Unable to retrieve firewall group details." +msgstr "Unable to retrieve firewall group details." + +msgid "Unable to retrieve firewall group list." +msgstr "Unable to retrieve firewall group list." + +msgid "Unable to retrieve firewallgroup details." +msgstr "Unable to retrieve Firewallgroup details." + +msgid "Unable to retrieve policies list." +msgstr "Unable to retrieve policies list." + +msgid "Unable to retrieve policy details." +msgstr "Unable to retrieve policy details." + +#, python-format +msgid "Unable to retrieve policy list (%s)." +msgstr "Unable to retrieve policy list (%s)." + +msgid "Unable to retrieve policy list." +msgstr "Unable to retrieve policy list." + +#, python-format +msgid "Unable to retrieve ports (%s)." +msgstr "Unable to retrieve ports (%s)." + +msgid "Unable to retrieve rule details." +msgstr "Unable to retrieve rule details." + +#, python-format +msgid "Unable to retrieve rules (%s)." +msgstr "Unable to retrieve rules (%s)." + +msgid "Unable to retrieve rules list." +msgstr "Unable to retrieve rules list." + +msgid "Value" +msgstr "Value" + +msgid "You may add ports to firewall group here." +msgstr "You may add ports to firewall group here." + +msgid "You may remove ports from firewall group here." +msgstr "You may remove ports from firewall group here." + +msgid "You may update firewall group details here." +msgstr "You may update firewall group details here." + +msgid "" +"You may update policy details here. Use 'Insert Rule' or 'Remove Rule' links " +"instead to insert or remove a rule." +msgstr "" +"You may update policy details here. Use 'Insert Rule' or 'Remove Rule' links " +"instead to insert or remove a rule." + +msgid "You may update rule details here." +msgstr "You may update rule details here." diff --git a/neutron_fwaas_dashboard/locale/fr/LC_MESSAGES/django.po b/neutron_fwaas_dashboard/locale/fr/LC_MESSAGES/django.po new file mode 100644 index 0000000..ca987a1 --- /dev/null +++ b/neutron_fwaas_dashboard/locale/fr/LC_MESSAGES/django.po @@ -0,0 +1,501 @@ +# Adrien Cunin , 2016. #zanata +# Gérald LONLAS , 2016. #zanata +# Sylvie Chesneau , 2016. #zanata +# Cédric Savignan , 2017. #zanata +# JF Taltavull , 2017. #zanata +# Loic Nicolle , 2017. #zanata +# Pascal Larivée , 2017. #zanata +# Loic Nicolle , 2018. #zanata +msgid "" +msgstr "" +"Project-Id-Version: neutron-fwaas-dashboard VERSION\n" +"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" +"POT-Creation-Date: 2019-02-19 16:16+0000\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"PO-Revision-Date: 2018-03-27 03:09+0000\n" +"Last-Translator: Loic Nicolle \n" +"Language-Team: French\n" +"Language: fr\n" +"X-Generator: Zanata 4.3.3\n" +"Plural-Forms: nplurals=2; plural=(n > 1)\n" + +msgid "-" +msgstr "-" + +msgid "ALLOW" +msgstr "AUTORISER" + +msgctxt "Action Name of a Firewall Rule" +msgid "ALLOW" +msgstr "AUTORISER" + +msgid "ANY" +msgstr "TOUT" + +msgid "Action" +msgstr "Action" + +msgid "Action for the firewall rule" +msgstr "Action pour la règle de pare-feu" + +msgctxt "Current status of a Firewall Group" +msgid "Active" +msgstr "Actif" + +msgid "Add" +msgstr "Ajouter" + +msgid "Add Firewall Group" +msgstr "Ajouter un groupe de pare-feux" + +msgid "Add New Policy" +msgstr "Ajouter une nouvelle stratégie" + +msgid "Add New Rule" +msgstr "Ajouter une nouvelle règle" + +msgid "Add Policy" +msgstr "Ajouter une stratégie" + +msgid "Add Rule" +msgstr "Ajouter une règle" + +#, python-format +msgid "Added Policy \"%s\"." +msgstr "Stratégie \"%s\" ajoutée." + +#, python-format +msgid "Added Rule \"%s\"." +msgstr "Règle \"%s\" ajoutée." + +msgid "Admin State" +msgstr "État Administateur" + +msgid "Admin State Up" +msgstr "Etat Admin activé" + +msgid "After" +msgstr "Après" + +msgid "Audited" +msgstr "Audité" + +msgid "Available Ports" +msgstr "Ports disponibles" + +msgid "Available Rules" +msgstr "Règles disponibles" + +msgid "Before" +msgstr "Avant" + +msgid "" +"Choose rule(s) from Available Rules to Selected Rule by push button or drag " +"and drop, you may change their order by drag and drop as well." +msgstr "" +"Choisissez une ou plusieurs règles en les faisant passer des règles " +"disponibles aux règles sélectionnées en cliquant sur le bouton ou par " +"glisser-déposer. Vous pouvez aussi changer l'ordre des règles par glisser-" +"déposer." + +msgid "" +"Choose the rule you want to insert. Specify either the rule you want to " +"insert immediately before, or the rule to insert immediately after. If both " +"are specified, the prior takes precedence." +msgstr "" +"Choisissez la règle que vous souhaitez insérer. Spécifiez la règle qui doit " +"précéder immédiatement votre nouvelle règle, ou la règle qui doit suivre " +"immédiatement votre nouvelle règle. Si vous spécifiez les deux, le premier " +"critère est prioritaire." + +msgid "Choose the rule you want to remove." +msgstr "Choisissez la règle que vous voulez enlever." + +msgid "Create a Firewall Group with selected ports." +msgstr "Créer un groupe de pare-feux avec les ports sélectionnés." + +msgid "" +"Create a firewall policy with an ordered list of firewall rules.\n" +"\n" +"A firewall policy is an ordered collection of firewall rules. So if the " +"traffic matches the first rule, the other rules are not executed. If the " +"traffic does not match the current rule, then the next rule is executed. A " +"firewall policy has the following attributes:\n" +"\n" +"
        • Shared: A firewall policy can be shared across tenants. Thus it can also " +"be made part of an audit workflow wherein the firewall policy can be audited " +"by the relevant entity that is authorized.
        • Audited: When audited is " +"set to True, it indicates that the firewall policy has been audited. Each " +"time the firewall policy or the associated firewall rules are changed, this " +"attribute will be set to False and will have to be explicitly set to True " +"through an update operation.
        • \n" +"The name field is required, all others are optional." +msgstr "" +"Créer une stratégie de pare-feu avec une liste ordonnée de règles de pare-" +"feu.\n" +"\n" +"Une stratégie de pare-feu est un ensemble ordonné de règles de pare-feu. Si " +"le trafic correspond à la première règle, les autres règles ne sont pas " +"exécutées. Si le trafic ne correspond pas à la règle en cours, la règle " +"suivante est exécutée. Une stratégie de pare-feu comporte les attributs " +"suivants :\n" +"\n" +"
        • Partagé (shared) : Une stratégie de pare-feu peut être partagée entre " +"locataires. Elle peut ainsi faire partie d'un flux de travaux d'audit au " +"sein duquel la stratégie de pare-feu peut être auditée par l'entité " +"appropriée autorisée.
        • Audité (audited) : Lorsque l'attribut est " +"défini sur True, la stratégie de pare-feu a été auditée. Chaque fois qu'une " +"stratégie de pare-feu ou les règles de pare-feu associées sont modifiées, " +"cet attribut est défini sur False et devra être explicitement défini avec la " +"valeur True via une opération de mise à jour.
        • \n" +"La zone de nom est obligatoire, toutes les autres sont facultatives." + +msgid "" +"Create a firewall rule.\n" +"\n" +"A Firewall rule is an association of the following attributes:\n" +"\n" +"
        • IP Addresses: The addresses from/to which the traffic filtration needs " +"to be applied.
        • IP Version: The type of IP packets (IP V4/V6) that " +"needs to be filtered.
        • Protocol: Type of packets (UDP, ICMP, TCP, " +"Any) that needs to be checked.
        • Action: Action is the type of " +"filtration required, it can be Reject/Deny/Allow data packets.
        • \n" +"The protocol and action fields are required, all others are optional." +msgstr "" +"Créer une règle de pare-feu.\n" +"\n" +"Une règle de pare-feu est une association des attributs suivants :\n" +"\n" +"
        • Adresses IP : Adresses depuis/vers lesquelles le filtrage du trafic doit " +"s'appliquer.
        • Version d'IP : Type des paquets IP (IP V4/V6) à filtrer." +"
        • Protocole : Type des paquets (UDP, ICMP, TCP, Tout) à vérifier.
        • Action : Type de filtrage requis ; Il peut s'agir de Rejeter/Refuser/" +"Autoriser les paquets de données.
        • \n" +"Les zones de protocole et d'action sont obligatoires, toutes les autres sont " +"facultatives." + +msgid "Create a policy with selected rules." +msgstr "Créer une stratégie avec les règles sélectionnées." + +msgctxt "Current status of a Firewall Group" +msgid "Created" +msgstr "Créé" + +msgid "DENY" +msgstr "REFUSER" + +msgctxt "Action Name of a Firewall Rule" +msgid "DENY" +msgstr "REFUSER" + +msgctxt "Admin state of a Firewall Group" +msgid "DOWN" +msgstr "INACTIF" + +msgid "Delete Policy" +msgid_plural "Delete Policies" +msgstr[0] "Supprimer la stratégie" +msgstr[1] "Supprimer les stratégies" + +msgid "Delete Rule" +msgid_plural "Delete Rules" +msgstr[0] "Supprimer la règle" +msgstr[1] "Supprimer les règles" + +msgid "Description" +msgstr "Description" + +msgid "Description:" +msgstr "Description:" + +msgid "Destination IP" +msgstr "IP de destination" + +msgid "Destination IP Address" +msgstr "Adresse IP de destination" + +msgid "Destination IP Address/Subnet" +msgstr "Adresse IP/Sous-réseau de destination" + +msgid "Destination IP address or subnet" +msgstr "Adresse IP ou sous-réseau de destination" + +msgid "Destination Port" +msgstr "Port de destination" + +msgid "Destination Port/Port Range" +msgstr "Port/Plage de ports de destination" + +msgid "Destination port (integer in [1, 65535] or range in a:b)" +msgstr "Port de destination (entier compris dans [1, 65535] ou plage a:b)" + +msgctxt "Current status of a Firewall Group" +msgid "Down" +msgstr "Inactif" + +msgid "Edit Policy" +msgstr "Editer la stratégie" + +msgid "Edit Policy {{ name }}" +msgstr "Editer la stratégie {{ name }}" + +msgid "Edit Rule" +msgstr "Éditer la règle" + +msgid "Edit Rule {{ name }}" +msgstr "Editer la règle {{ name }}" + +msgid "Egress Policy" +msgstr "Stratégie sortante" + +msgid "Enabled" +msgstr "Activé" + +msgctxt "Current status of a Firewall Group" +msgid "Error" +msgstr "Erreur" + +#, python-format +msgid "Failed to insert rule to policy %(name)s: %(reason)s" +msgstr "Echec d'insertion de la règle dans la stratégie %(name)s : %(reason)s" + +#, python-format +msgid "Failed to remove rule from policy %(name)s: %(reason)s" +msgstr "Echec de retrait de la règle de la stratégie %(name)s : %(reason)s" + +#, python-format +msgid "Failed to retrieve available rules: %s" +msgstr "Impossible de récupérer les règles disponibles : %s" + +#, python-format +msgid "Failed to retrieve current rules in policy %(name)s: %(reason)s" +msgstr "" +"Échec de la récupération des règles courantes de la stratégie %(name)s : " +"%(reason)s" + +#, python-format +msgid "Failed to update policy %(name)s: %(reason)s" +msgstr "Echec de mise à jour de la stratégie %(name)s : %(reason)s" + +#, python-format +msgid "Failed to update rule %(name)s: %(reason)s" +msgstr "Échec de la mise à jour de la règle %(name)s : %(reason)s" + +msgid "Firewall Policies" +msgstr "Stratégies de pare-feu" + +msgid "Firewall Rules" +msgstr "Règles de pare-feu" + +msgid "ICMP" +msgstr "ICMP" + +msgid "ID" +msgstr "ID" + +msgid "IP Version" +msgstr "Version d'IP" + +msgid "IP Version for Firewall Rule" +msgstr "Version d'IP pour la règle de pare-feu" + +msgctxt "Current status of a Firewall Group" +msgid "Inactive" +msgstr "Inactif" + +msgid "Ingress Policy" +msgstr "Stratégie entrante" + +msgid "Insert Rule" +msgstr "Insérer une règle" + +msgid "Insert Rule to Policy" +msgstr "Insérer une règle dans la stratégie" + +msgid "Name" +msgstr "Nom" + +msgid "None" +msgstr "Aucun" + +msgctxt "Current status of a Firewall Group" +msgid "Pending Create" +msgstr "Création en attente" + +msgctxt "Current status of a Firewall Group" +msgid "Pending Delete" +msgstr "Suppression en attente" + +msgctxt "Current status of a Firewall Group" +msgid "Pending Update" +msgstr "Mise à jour en attente" + +msgid "Policies" +msgstr "Stratégies" + +msgid "Policy" +msgstr "Stratégie" + +#, python-format +msgid "Policy %s was successfully updated." +msgstr "La stratégie %s a été mise à jour." + +msgid "Ports" +msgstr "Ports" + +msgid "Project ID" +msgstr "Project ID" + +msgid "Protocol" +msgstr "Protocole" + +msgid "Protocol for the firewall rule" +msgstr "Protocole pour la règle de pare-feu" + +msgid "REJECT" +msgstr "REJETER" + +msgctxt "Action Name of a Firewall Rule" +msgid "REJECT" +msgstr "REJETER" + +msgid "Remove Port" +msgstr "Supprimer le port" + +msgid "Remove Rule" +msgstr "Retirer la règle" + +msgid "Remove Rule from Policy" +msgstr "Retirer une règle de la stratégie" + +msgid "Rule" +msgstr "Règle" + +#, python-format +msgid "Rule %(rule)s was successfully inserted to policy %(policy)s." +msgstr "La règle %(rule)s a été insérée dans la stratégie %(policy)s." + +#, python-format +msgid "Rule %(rule)s was successfully removed from policy %(policy)s." +msgstr "La règle %(rule)s a été retirée de la stratégie %(policy)s." + +#, python-format +msgid "Rule %s was successfully updated." +msgstr "La règle %s a été mise à jour." + +msgid "Rules" +msgstr "Règles" + +msgid "Save Changes" +msgstr "Enregistrer les changements" + +msgid "Scheduled deletion of Policy" +msgid_plural "Scheduled deletion of Policies" +msgstr[0] "Suppression planifiée de la stratégie" +msgstr[1] "Suppression planifiée des stratégies" + +msgid "Scheduled deletion of Rule" +msgid_plural "Scheduled deletion of Rules" +msgstr[0] "Suppression planifiée de la règle" +msgstr[1] "Suppression planifiée des règles" + +msgid "Select a Policy" +msgstr "Sélectionner une stratégie" + +msgid "Select rules for your policy." +msgstr "Sélectionnez des règles pour votre stratégie." + +msgid "Selected Rules" +msgstr "Règles sélectionnées" + +msgid "Shared" +msgstr "Partagé" + +msgid "Source IP" +msgstr "IP source" + +msgid "Source IP Address" +msgstr "Adresse IP source" + +msgid "Source IP Address/Subnet" +msgstr "Adresse IP/Sous-réseau source" + +msgid "Source IP address or subnet" +msgstr "Adresse IP ou sous-réseau source" + +msgid "Source Port" +msgstr "Port source" + +msgid "Source Port/Port Range" +msgstr "Port/plage de ports source" + +msgid "Source port (integer in [1, 65535] or range in a:b)" +msgstr "Port source (entier compris dans [1,65535] ou plage a:b)" + +msgid "" +"Source/Destination Network Address and IP version are inconsistent. Please " +"make them consistent." +msgstr "" +"La version d'IP et l'adresse réseau source/de destination ne sont pas " +"cohérentes. Corrigez-les en conséquence." + +msgid "Status" +msgstr "Statut" + +msgid "TCP" +msgstr "TCP" + +msgid "UDP" +msgstr "UDP" + +msgctxt "Admin state of a Firewall Group" +msgid "UP" +msgstr "ACTIF" + +#, python-format +msgid "Unable to add Policy \"%s\"." +msgstr "Impossible d'ajouter la stratégie \"%s\"." + +#, python-format +msgid "Unable to add Rule \"%s\"." +msgstr "Impossible d'ajouter la règle \"%s\"." + +#, python-format +msgid "Unable to delete policy. %s" +msgstr "Impossible de supprimer la stratégie. %s" + +#, python-format +msgid "Unable to delete rule. %s" +msgstr "Impossible de supprimer la règle %s" + +msgid "Unable to retrieve policies list." +msgstr "Impossible de récupérer la liste des stratégies." + +msgid "Unable to retrieve policy details." +msgstr "Impossible de récupérer les détails de la stratégie." + +#, python-format +msgid "Unable to retrieve policy list (%s)." +msgstr "Impossible de récupérer la liste des stratégies (%s)." + +msgid "Unable to retrieve policy list." +msgstr "Impossible de récupérer la liste des stratégies." + +#, python-format +msgid "Unable to retrieve ports (%s)." +msgstr "Impossible de récupérer les ports (%s)." + +msgid "Unable to retrieve rule details." +msgstr "Impossible de récupérer les détails de la règle." + +#, python-format +msgid "Unable to retrieve rules (%s)." +msgstr "Impossible de récupérer les règles (%s)." + +msgid "Unable to retrieve rules list." +msgstr "Impossible de récupérer la liste des règles." + +msgid "You may update rule details here." +msgstr "Ici, vous pouvez mettre à jour les détails de la règle." diff --git a/neutron_fwaas_dashboard/locale/id/LC_MESSAGES/django.po b/neutron_fwaas_dashboard/locale/id/LC_MESSAGES/django.po new file mode 100644 index 0000000..76314cc --- /dev/null +++ b/neutron_fwaas_dashboard/locale/id/LC_MESSAGES/django.po @@ -0,0 +1,637 @@ +# OpenStack Infra , 2015. #zanata +# Andreas Jaeger , 2016. #zanata +# suhartono , 2016. #zanata +# suhartono , 2017. #zanata +# suhartono , 2018. #zanata +# Frank Kloeker , 2019. #zanata +# suhartono , 2019. #zanata +msgid "" +msgstr "" +"Project-Id-Version: neutron-fwaas-dashboard VERSION\n" +"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" +"POT-Creation-Date: 2019-03-27 14:25+0000\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"PO-Revision-Date: 2019-04-03 11:53+0000\n" +"Last-Translator: Frank Kloeker \n" +"Language-Team: Indonesian\n" +"Language: id\n" +"X-Generator: Zanata 4.3.3\n" +"Plural-Forms: nplurals=1; plural=0\n" + +msgid "-" +msgstr "-" + +msgid "ALLOW" +msgstr "ALLOW" + +msgctxt "Action Name of a Firewall Rule" +msgid "ALLOW" +msgstr "ALLOW (mengizinkan)" + +msgid "ANY" +msgstr "ANY" + +msgid "Action" +msgstr "Action" + +msgid "Action for the firewall rule" +msgstr "Aksi aturan firewall" + +msgctxt "Current status of a Firewall Group" +msgid "Active" +msgstr "Active" + +msgid "Add" +msgstr "Menambahkan" + +msgid "Add Firewall Group" +msgstr "Menambahkan Firewall Group" + +msgid "Add New Firewall Group" +msgstr "Add New Firewall Group" + +msgid "Add New Policy" +msgstr "Menambah kebijakan baru" + +msgid "Add New Rule" +msgstr "Menambah aturan baru" + +msgid "Add Policy" +msgstr "Add Policy (tambahkan kebiijakan)" + +msgid "Add Port" +msgstr "Add Port" + +msgid "Add Port to Firewall Group" +msgstr "Tambahkan Port ke Firewall Group" + +msgid "Add Rule" +msgstr "Tambahkan Aturan" + +msgid "Add port to Firewall Group {{ name }}" +msgstr "Tambahkan port ke Firewall Group {{ name }}" + +#, python-format +msgid "Added Firewall Group\"%s\"." +msgstr "Ditambahkan Firewall Group\"%s\"." + +#, python-format +msgid "Added Policy \"%s\"." +msgstr "Kebijakan ditambahkan \"%s\"." + +#, python-format +msgid "Added Rule \"%s\"." +msgstr "Aturan ditambahkan \"%s\"." + +#, python-format +msgid "Added the port(s) to the firewall group %s successfully." +msgstr "Berhasil menambahkan port ke grup firewall %s." + +msgid "Admin State" +msgstr "Admin State" + +msgid "Admin State Up" +msgstr "Admin State Up" + +msgid "After" +msgstr "After (sesudah)" + +msgid "Audited" +msgstr "Audited" + +msgid "Available Ports" +msgstr "Available Ports" + +msgid "Available Rules" +msgstr "Available Rules" + +msgid "Before" +msgstr "Before (sebelum)" + +msgid "Choose port(s) from Available Ports. " +msgstr "Pilih port dari Available Ports. " + +msgid "" +"Choose rule(s) from Available Rules to Selected Rule by push button or drag " +"and drop, you may change their order by drag and drop as well." +msgstr "" +"Pilih aturan dari Available Rules ke Selected Rule dengan menekan tombol " +"atau seret dan lepaskan, Anda dapat mengubah pesanan mereka dengan drag dan " +"drop juga." + +msgid "" +"Choose the rule you want to insert. Specify either the rule you want to " +"insert immediately before, or the rule to insert immediately after. If both " +"are specified, the prior takes precedence." +msgstr "" +"Pilih aturan yang ingin Anda sisipkan. Tentukan baik aturan yang ingin Anda " +"masukkan segera sebelumnya, atau aturan untuk memasukkan segera setelahnya. " +"Jika keduanya ditentukan, sebelumnya diutamakan." + +msgid "Choose the rule you want to remove." +msgstr "Pilih aturan yang ingin Anda hapus." + +msgid "Create Firewall Group" +msgstr "Membuat Firewall Group" + +msgid "Create a Firewall Group with selected ports." +msgstr "Buat Firewall Group dengan port yang dipilih." + +msgid "" +"Create a firewall group based on a policy.\n" +"\n" +"A firewall group represents a logical firewall resource that a tenant can " +"instantiate and manage. A firewall group must be associated with one policy, " +"all other fields are optional." +msgstr "" +"Buat grup firewall berdasarkan kebijakan.\n" +"\n" +"Grup firewall mewakili sumber daya firewall logis dimana tenant dapat " +"instantiate dan kelola. Grup firewall harus dikaitkan dengan satu kebijakan, " +"semua bidang lainnya opsional." + +msgid "" +"Create a firewall policy with an ordered list of firewall rules.\n" +"\n" +"A firewall policy is an ordered collection of firewall rules. So if the " +"traffic matches the first rule, the other rules are not executed. If the " +"traffic does not match the current rule, then the next rule is executed. A " +"firewall policy has the following attributes:\n" +"\n" +"
        • Shared: A firewall policy can be shared across tenants. Thus it can also " +"be made part of an audit workflow wherein the firewall policy can be audited " +"by the relevant entity that is authorized.
        • Audited: When audited is " +"set to True, it indicates that the firewall policy has been audited. Each " +"time the firewall policy or the associated firewall rules are changed, this " +"attribute will be set to False and will have to be explicitly set to True " +"through an update operation.
        • \n" +"The name field is required, all others are optional." +msgstr "" +"Membuat kebijakan firewall dengan daftar aturan firewall tersusun " +"berurutan.\n" +"\n" +"Sebuah kebijakan firewall adalah koleksi aturan firewall berurutan. Jadi " +"jika lalu lintas sesuai dengan aturan pertama, maka aturan lain tidak " +"dieksekusi. Jika lalu lintas tidak sesuai dengan aturan saat ini, maka " +"aturan berikutnya dieksekusi. Sebuah kebijakan firewall memiliki atribut " +"sebagai berikut:\n" +"\n" +"
        • Shared: Sebuah kebijakan firewall dapat digunakan bersama seluruh " +"penyewa (tenant). Dengan demikian kebijakan itu dapat juga menjadi bagian " +"dari alur kerja pemeriksaan dimana kebijakan firewall dapat diaudit oleh " +"entitas terkait yang berwenang.
        • Audited: Ketika diaudit diatur ke " +"True, ini menunjukkan bahwa kebijakan firewall telah diaudit. Setiap kali " +"kebijakan firewall atau aturan firewall yang terkait berubah, atribut ini " +"akan diatur ke False dan harus secara eksplisit diatur ke True melalui " +"operasi update.
        • \n" +"Kolom (field) nama diperlukan, semua yang lain adalah opsional." + +msgid "" +"Create a firewall rule.\n" +"\n" +"A Firewall rule is an association of the following attributes:\n" +"\n" +"
        • IP Addresses: The addresses from/to which the traffic filtration needs " +"to be applied.
        • IP Version: The type of IP packets (IP V4/V6) that " +"needs to be filtered.
        • Protocol: Type of packets (UDP, ICMP, TCP, " +"Any) that needs to be checked.
        • Action: Action is the type of " +"filtration required, it can be Reject/Deny/Allow data packets.
        • \n" +"The protocol and action fields are required, all others are optional." +msgstr "" +"Buat aturan firewall\n" +"\n" +"Aturan firewall adalah sebuah asosiasi atribut berikut:\n" +"\n" +"
        • IP Addresses: Alamat dari/ke dimana filtrasi lalu lintas perlu " +"diterapkan.
        • IP Version: Tipe paket IP (IP V4/V6) yang perlu disaring." +"
        • Protocol: Tipe paket (UDP, ICMP, TCP, lainnya) yang perlu diperiksa." +"
        • Action: Action adalah tipe filtrasi diperlukan, dapat Reject/Deny/" +"Allow paket data.
        • \n" +"Protokol dan action field (kolom aksi) yang diperlukan, semua yang lain " +"adalah opsional." + +msgid "Create a policy with selected rules." +msgstr "Membuat kebijakan dengan aturan yang dipilih." + +msgctxt "Current status of a Firewall Group" +msgid "Created" +msgstr "Created" + +msgid "DENY" +msgstr "DENY (menyangkal)" + +msgctxt "Action Name of a Firewall Rule" +msgid "DENY" +msgstr "DENY (menyangkal)" + +msgctxt "Admin state of a Firewall Group" +msgid "DOWN" +msgstr "DOWN" + +msgid "Delete Firewall Group" +msgid_plural "Delete Firewall Groups" +msgstr[0] "Delete Firewall Group" + +msgid "Delete Policy" +msgid_plural "Delete Policies" +msgstr[0] "Delete Policy" + +msgid "Delete Rule" +msgid_plural "Delete Rules" +msgstr[0] "Delete Rule" + +msgid "Description" +msgstr "Deskripsi" + +msgid "Description:" +msgstr "Deskripsi:" + +msgid "Destination IP" +msgstr "IP Tujuan" + +msgid "Destination IP Address" +msgstr "Destination IP Address" + +msgid "Destination IP Address/Subnet" +msgstr "Tujuan subnet/alamat IP" + +msgid "Destination IP address or subnet" +msgstr "Tujuan subnet atau alamat IP" + +msgid "Destination Port" +msgstr "Port Tujuan" + +msgid "Destination Port/Port Range" +msgstr "Destination Port/Port Range (tujuan port/ kisaran port)" + +msgid "Destination port (integer in [1, 65535] or range in a:b)" +msgstr "" +"Destination (tujuan) port (integer dalam [1, 65535] atau dalam kisaran a:b)" + +msgctxt "Current status of a Firewall Group" +msgid "Down" +msgstr "Down" + +msgid "Edit Firewall Group" +msgstr "Edit Firewall Group" + +msgid "Edit FirewallGroup {{ name }}" +msgstr "Edit FirewallGroup {{ name }}" + +msgid "Edit Policy" +msgstr "Ubah Kebijakan" + +msgid "Edit Policy {{ name }}" +msgstr "Edit Policy {{ name }}" + +msgid "Edit Rule" +msgstr "Ubah Aturan" + +msgid "Edit Rule {{ name }}" +msgstr "Edit Rule {{ name }}" + +msgid "Egress Policy" +msgstr "Egress Policy" + +msgid "Egress Policy ID" +msgstr "Egress Policy ID" + +msgid "Enabled" +msgstr "Enabled (aktif)" + +msgctxt "Current status of a Firewall Group" +msgid "Error" +msgstr "Error" + +#, python-format +msgid "Failed to add the port(s) to the firewall group %(name)s: %(reason)s" +msgstr "Gagal menambahkan port(s) ke firewall group %(name)s: %(reason)s" + +#, python-format +msgid "Failed to insert rule to policy %(name)s: %(reason)s" +msgstr "Gagal untuk memasukkan aturan kebijakan %(name)s: %(reason)s" + +#, python-format +msgid "Failed to remove rule from policy %(name)s: %(reason)s" +msgstr "Gagal menghapus aturan dari kebijakan %(name)s: %(reason)s" + +#, python-format +msgid "" +"Failed to remove the port(s) from the firewall group %(name)s: %(reason)s" +msgstr "Gagal menghapus port(s) dari firewall group %(name)s: %(reason)s" + +#, python-format +msgid "Failed to retrieve available rules: %s" +msgstr "Gagal mengambil aturan yang tersedia: %s" + +#, python-format +msgid "Failed to retrieve current rules in policy %(name)s: %(reason)s" +msgstr "Gagal mengambil aturan saat ini dalam kebijakan %(name)s: %(reason)s" + +msgid "Failed to retrieve port list of the firewall group." +msgstr "Gagal mengambil daftar port dari grup firewall." + +#, python-format +msgid "Failed to update firewall group %(name)s: %(reason)s" +msgstr "Gagal memperbarui grup firewall %(name)s: %(reason)s" + +#, python-format +msgid "Failed to update policy %(name)s: %(reason)s" +msgstr "Gagal memperbarui kebijakan %(name)s: %(reason)s" + +#, python-format +msgid "Failed to update rule %(name)s: %(reason)s" +msgstr "Gagal memperbarui aturan %(name)s: %(reason)s" + +msgid "Firewall Groups" +msgstr "Firewall Groups" + +msgid "Firewall Policies" +msgstr "Firewall Policies (kebijakan firewall)" + +msgid "Firewall Rules" +msgstr " Firewall Rules (aturan firewall)" + +#, python-format +msgid "Firewall group %s was successfully updated." +msgstr "Grup firewall %s berhasil diperbarui." + +msgid "FirewallGroup" +msgstr "FirewallGroup" + +msgid "ICMP" +msgstr "ICMP" + +msgid "ID" +msgstr "ID" + +msgid "IP Version" +msgstr "IP Version" + +msgid "IP Version for Firewall Rule" +msgstr "IP Version untuk Firewall Rule" + +msgctxt "Current status of a Firewall Group" +msgid "Inactive" +msgstr "Inactive" + +msgid "Ingress Policy" +msgstr "Ingress Policy" + +msgid "Ingress Policy ID" +msgstr "Ingress Policy ID" + +msgid "Insert Rule" +msgstr "Insert Rule (menyisipkan aturan)" + +msgid "Insert Rule to Policy" +msgstr "Sisipkan aturan ke kebijakan" + +msgid "Label" +msgstr "Label" + +msgid "Name" +msgstr "Name" + +msgid "No options available" +msgstr "Tidak ada opsi yang tersedia" + +msgid "None" +msgstr "None" + +msgctxt "Current status of a Firewall Group" +msgid "Pending Create" +msgstr "Pending Create" + +msgctxt "Current status of a Firewall Group" +msgid "Pending Delete" +msgstr "Pending Delete" + +msgctxt "Current status of a Firewall Group" +msgid "Pending Update" +msgstr "Pending Update" + +msgid "Policies" +msgstr "Kebijakan" + +msgid "Policy" +msgstr "Policy (kebijakan)" + +#, python-format +msgid "Policy %s was successfully updated." +msgstr "Kebijakan %s telah berhasil diperbarui." + +msgid "Ports" +msgstr "Ports" + +msgid "Project ID" +msgstr "Project ID" + +msgid "Protocol" +msgstr "Protocol" + +msgid "Protocol for the firewall rule" +msgstr "Protokol untuk aturan firewall" + +msgid "REJECT" +msgstr "REJECT (menolak)" + +msgctxt "Action Name of a Firewall Rule" +msgid "REJECT" +msgstr "REJECT (menolak)" + +msgid "Remove Port" +msgstr "Remove Port" + +msgid "Remove Port from Firewall Group" +msgstr "Hapus Port dari Firewall Group" + +msgid "Remove Rule" +msgstr "Remove Rule (hapus aturan)" + +msgid "Remove Rule from Policy" +msgstr "Buang aturan dari kebijakan" + +msgid "Remove port from FirewallGroup {{ name }}" +msgstr "Hapus port dari FirewallGroup {{ name }}" + +#, python-format +msgid "Removed the port(s) from the firewall group %s successfully." +msgstr "Port berhasil dihapus dari firewall group %s ." + +msgid "Rule" +msgstr "Aturan" + +#, python-format +msgid "Rule %(rule)s was successfully inserted to policy %(policy)s." +msgstr "Aturan %(rule)s berhasil dimasukkan ke kebijakan %(policy)s." + +#, python-format +msgid "Rule %(rule)s was successfully removed from policy %(policy)s." +msgstr "Aturan %(rule)s berhasil dihapus dari kebijakan %(policy)s." + +#, python-format +msgid "Rule %s was successfully updated." +msgstr "Aturan %s telah berhasil diperbarui." + +msgid "Rules" +msgstr "Aturan" + +msgid "Save Changes" +msgstr "Simpan perubahan" + +msgid "Scheduled deletion of Firewall Group" +msgid_plural "Scheduled deletion of Firewall Groups" +msgstr[0] "Scheduled deletion of Firewall Group" + +msgid "Scheduled deletion of Policy" +msgid_plural "Scheduled deletion of Policies" +msgstr[0] "Scheduled deletion of Policy" + +msgid "Scheduled deletion of Rule" +msgid_plural "Scheduled deletion of Rules" +msgstr[0] "Scheduled deletion of Rule" + +msgid "Select a Policy" +msgstr "Pilih kebijakan " + +msgid "Select ports for your firewall group." +msgstr "Pilih port untuk grup firewall Anda." + +msgid "Select rules for your policy." +msgstr "Pilih aturan untuk kebijakan Anda." + +msgid "Selected Ports" +msgstr "Selected Ports" + +msgid "Selected Rules" +msgstr "Selected Rules" + +msgid "Shared" +msgstr "Shared" + +msgid "Source IP" +msgstr "Source IP (IP sumber)" + +msgid "Source IP Address" +msgstr "Source IP Address" + +msgid "Source IP Address/Subnet" +msgstr "Source (asal) subnet/alamat IP" + +msgid "Source IP address or subnet" +msgstr "Source (asal) subnet atau alamat IP" + +msgid "Source Port" +msgstr "Port Sumber" + +msgid "Source Port/Port Range" +msgstr "Source Port/Port Range (asal port/ kisaran port)" + +msgid "Source port (integer in [1, 65535] or range in a:b)" +msgstr "Source (asal) port (integer dalam [1, 65535] atau dalam kisaran a:b)" + +msgid "" +"Source/Destination Network Address and IP version are inconsistent. Please " +"make them consistent." +msgstr "" +"Sumber/tujuan alamat jaringan dan IP versi tidak konsisten. Harap membuat " +"mereka konsisten." + +msgid "Status" +msgstr "Status" + +msgid "TCP" +msgstr "TCP" + +msgid "UDP" +msgstr "UDP" + +msgctxt "Admin state of a Firewall Group" +msgid "UP" +msgstr "UP" + +#, python-format +msgid "Unable to add Firewall Group \"%s\"." +msgstr "Tidak dapat menambahkan Firewall Group \"%s\"." + +#, python-format +msgid "Unable to add Policy \"%s\"." +msgstr "Tidak dapat menambahkan kebijakan \"%s\"." + +#, python-format +msgid "Unable to add Rule \"%s\"." +msgstr "Tidak dapat menambahkan Aturan \"%s\"." + +#, python-format +msgid "Unable to delete firewall group. %s" +msgstr "Tidak dapat menghapus firewall group. %s" + +#, python-format +msgid "Unable to delete policy. %s" +msgstr "Tidak dapat menghapus kebijakan. %s" + +#, python-format +msgid "Unable to delete rule. %s" +msgstr "Tidak dapat menghapus aturan. %s" + +msgid "Unable to retrieve firewall group details." +msgstr "Tidak dapat mengambil detail grup firewall." + +msgid "Unable to retrieve firewall group list." +msgstr "Tidak dapat mengambil daftar grup firewall." + +msgid "Unable to retrieve firewallgroup details." +msgstr "Tidak dapat mengambil rincian firewallgroup." + +msgid "Unable to retrieve policies list." +msgstr "Tidak dapat mengambil daftar kebijakan." + +msgid "Unable to retrieve policy details." +msgstr "Tidak dapat mengambil rincian kebijakan." + +#, python-format +msgid "Unable to retrieve policy list (%s)." +msgstr "Tidak dapat mengambil daftar kebijakan (%s)." + +msgid "Unable to retrieve policy list." +msgstr "Tidak dapat mengambil daftar kebijakan." + +#, python-format +msgid "Unable to retrieve ports (%s)." +msgstr "Tidak dapat mengambil port (%s)." + +msgid "Unable to retrieve rule details." +msgstr "Tidak dapat mengambil rincian aturan." + +#, python-format +msgid "Unable to retrieve rules (%s)." +msgstr "Tidak dapat mengambil peraturan (%s)." + +msgid "Unable to retrieve rules list." +msgstr "Tidak dapat mengambil daftar aturan." + +msgid "Value" +msgstr "Value" + +msgid "You may add ports to firewall group here." +msgstr "Anda dapat menambahkan port ke grup firewall di sini." + +msgid "You may remove ports from firewall group here." +msgstr "Anda dapat menghapus port dari grup firewall di sini." + +msgid "You may update firewall group details here." +msgstr "Anda dapat memperbarui rincian grup firewall di sini." + +msgid "" +"You may update policy details here. Use 'Insert Rule' or 'Remove Rule' links " +"instead to insert or remove a rule." +msgstr "" +"Anda dapat memperbarui rincian kebijakan di sini. Gunakan tautan 'Insert " +"Rule' atau 'Remove Rule' sebagai gantinya untuk memasukkan atau menghapus " +"aturan." + +msgid "You may update rule details here." +msgstr "Anda dapat memperbarui detail aturan di sini." diff --git a/neutron_fwaas_dashboard/locale/ja/LC_MESSAGES/django.po b/neutron_fwaas_dashboard/locale/ja/LC_MESSAGES/django.po new file mode 100644 index 0000000..0692566 --- /dev/null +++ b/neutron_fwaas_dashboard/locale/ja/LC_MESSAGES/django.po @@ -0,0 +1,633 @@ +# Akihiro Motoki , 2017. #zanata +# Yuko Katabami , 2018. #zanata +# Takashi Kuroda , 2019. #zanata +msgid "" +msgstr "" +"Project-Id-Version: neutron-fwaas-dashboard VERSION\n" +"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" +"POT-Creation-Date: 2019-03-19 10:41+0000\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"PO-Revision-Date: 2019-03-20 03:16+0000\n" +"Last-Translator: Takashi Kuroda \n" +"Language-Team: Japanese\n" +"Language: ja\n" +"X-Generator: Zanata 4.3.3\n" +"Plural-Forms: nplurals=1; plural=0\n" + +msgid "-" +msgstr "-" + +msgid "ALLOW" +msgstr "ALLOW" + +msgctxt "Action Name of a Firewall Rule" +msgid "ALLOW" +msgstr "ALLOW" + +msgid "ANY" +msgstr "ANY" + +msgid "Action" +msgstr "アクション" + +msgid "Action for the firewall rule" +msgstr "ファイアウォールルールのアクション" + +msgctxt "Current status of a Firewall Group" +msgid "Active" +msgstr "稼働中" + +msgid "Add" +msgstr "追加" + +msgid "Add Firewall Group" +msgstr "ファイアウォールグループの追加" + +msgid "Add New Firewall Group" +msgstr "ファイアウォールグループの追加" + +msgid "Add New Policy" +msgstr "ポリシーの追加" + +msgid "Add New Rule" +msgstr "ルールの追加" + +msgid "Add Policy" +msgstr "ポリシーの追加" + +msgid "Add Port" +msgstr "ポートの追加" + +msgid "Add Port to Firewall Group" +msgstr "ファイアウォールグループへのポートの追加" + +msgid "Add Rule" +msgstr "ルールの追加" + +msgid "Add port to Firewall Group {{ name }}" +msgstr "ファイアウォールグループ {{ name }} へのポートの追加" + +#, python-format +msgid "Added Firewall Group\"%s\"." +msgstr "ファイアウォールグループ \"%s\" を追加しました。" + +#, python-format +msgid "Added Policy \"%s\"." +msgstr "ポリシー \"%s\" を追加しました。" + +#, python-format +msgid "Added Rule \"%s\"." +msgstr "ルール \"%s\" を追加しました。" + +#, python-format +msgid "Added the port(s) to the firewall group %s successfully." +msgstr "ポートを正常にファイアウォールグループ %s に追加しました。" + +msgid "Admin State" +msgstr "管理状態" + +msgid "Admin State Up" +msgstr "管理状態有効" + +msgid "After" +msgstr "後に挿入" + +msgid "Audited" +msgstr "監査対象 (Audited)" + +msgid "Available Ports" +msgstr "利用可能なポート" + +msgid "Available Rules" +msgstr "利用可能なルール" + +msgid "Before" +msgstr "前に挿入" + +msgid "Choose port(s) from Available Ports. " +msgstr "以下の利用可能なポートからポートを選択してください。" + +msgid "" +"Choose rule(s) from Available Rules to Selected Rule by push button or drag " +"and drop, you may change their order by drag and drop as well." +msgstr "" +"利用可能なルールから選択済みルールに、ボタンを押すかドラッグアンドドロップす" +"ることでルールを選択します。ドラッグアンドドロップでルールの順番を変更するこ" +"ともできます。" + +msgid "" +"Choose the rule you want to insert. Specify either the rule you want to " +"insert immediately before, or the rule to insert immediately after. If both " +"are specified, the prior takes precedence." +msgstr "" +"挿入するルールを選択してください。ルールをどのルールの直前、または直後に挿入" +"するかを指定してください。両方指定した場合は指定したルールの直前に挿入されま" +"す。" + +msgid "Choose the rule you want to remove." +msgstr "削除するルールを選択してください。" + +msgid "Create Firewall Group" +msgstr "ファイアウォールグループの作成" + +msgid "Create a Firewall Group with selected ports." +msgstr "ポートを選択してファイアウォールグループを作成します。" + +msgid "" +"Create a firewall group based on a policy.\n" +"\n" +"A firewall group represents a logical firewall resource that a tenant can " +"instantiate and manage. A firewall group must be associated with one policy, " +"all other fields are optional." +msgstr "" +"ポリシーに基づいてファイアウォールグループを作成します。\n" +"\n" +"ファイアウォールグループは、テナントがインスタンス化して管理することのできる" +"論理的なファイアウォールリソースを表します。1 つのファイアウォールグループは " +"1 つのポリシーと関連付けられる必要があり、他のフィールドはすべて省略可能で" +"す。" + +msgid "" +"Create a firewall policy with an ordered list of firewall rules.\n" +"\n" +"A firewall policy is an ordered collection of firewall rules. So if the " +"traffic matches the first rule, the other rules are not executed. If the " +"traffic does not match the current rule, then the next rule is executed. A " +"firewall policy has the following attributes:\n" +"\n" +"
        • Shared: A firewall policy can be shared across tenants. Thus it can also " +"be made part of an audit workflow wherein the firewall policy can be audited " +"by the relevant entity that is authorized.
        • Audited: When audited is " +"set to True, it indicates that the firewall policy has been audited. Each " +"time the firewall policy or the associated firewall rules are changed, this " +"attribute will be set to False and will have to be explicitly set to True " +"through an update operation.
        • \n" +"The name field is required, all others are optional." +msgstr "" +"ファイアウォールルールの順序付きリストでファイアウォールポリシーを作成しま" +"す。\n" +"\n" +"ファイアウォールポリシーは、ファイアウォールルールの順序付きコレクションなの" +"で、トラフィックが最初のルールに一致した場合には、他のルールは実行されませ" +"ん。トラフィックが現在のルールに一致しない場合には、次のルールが実行されま" +"す。ファイアウォールポリシーには、以下の属性があります。\n" +"\n" +"
        • 共有: ファイアウォールポリシーをテナント間で共有できます。そのため、承認" +"済みの関連エンティティーによってファイアウォールポリシーを監査することができ" +"る監査ワークフローの一部にすることも可能です。
        • 監査: 監査が True に設" +"定されている場合には、ファイアウォールポリシーが監査されることを意味します。" +"ファイアウォールポリシーまたは関連付けられたファイアウォールルールが変更され" +"るたびに、この属性は False に設定され、更新操作で明示的に True に設定する必要" +"があります。
        • \n" +"名前のフィールドは入力する必要があり、その他のフィールドはすべて省略可能で" +"す。" + +msgid "" +"Create a firewall rule.\n" +"\n" +"A Firewall rule is an association of the following attributes:\n" +"\n" +"
        • IP Addresses: The addresses from/to which the traffic filtration needs " +"to be applied.
        • IP Version: The type of IP packets (IP V4/V6) that " +"needs to be filtered.
        • Protocol: Type of packets (UDP, ICMP, TCP, " +"Any) that needs to be checked.
        • Action: Action is the type of " +"filtration required, it can be Reject/Deny/Allow data packets.
        • \n" +"The protocol and action fields are required, all others are optional." +msgstr "" +"ファイアウォールルールを作成します。\n" +"\n" +"ファイアウォールルールは、以下の属性の組み合わせです。\n" +"\n" +"
        • IP アドレス: トラフィックフィルターを適用する必要があるアドレス (送信元/" +"送信先) です。
        • IP バージョン: フィルタリングする必要がある IP パケッ" +"トの種別 (IP V4/V6) です。
        • プロトコル: チェックされる必要があるパケッ" +"トの種別 (UDP、ICMP、TCP、Any) です。
        • アクション: アクションは必要な" +"フィルターの種別で、データパケットを REJECT/DENY/ALLOW できます。
        • \n" +"プロトコルとアクションは必須項目であり、他のフィールドはすべて省略可能です。" + +msgid "Create a policy with selected rules." +msgstr "ルールを選択してポリシーを作成します。" + +msgctxt "Current status of a Firewall Group" +msgid "Created" +msgstr "作成済み" + +msgid "DENY" +msgstr "DENY" + +msgctxt "Action Name of a Firewall Rule" +msgid "DENY" +msgstr "DENY" + +msgctxt "Admin state of a Firewall Group" +msgid "DOWN" +msgstr "DOWN" + +msgid "Delete Firewall Group" +msgid_plural "Delete Firewall Groups" +msgstr[0] "ファイアウォールグループの削除" + +msgid "Delete Policy" +msgid_plural "Delete Policies" +msgstr[0] "ポリシーの削除" + +msgid "Delete Rule" +msgid_plural "Delete Rules" +msgstr[0] "ルールの削除" + +msgid "Description" +msgstr "説明" + +msgid "Description:" +msgstr "説明:" + +msgid "Destination IP" +msgstr "宛先 IP アドレス" + +msgid "Destination IP Address" +msgstr "宛先 IP アドレス" + +msgid "Destination IP Address/Subnet" +msgstr "宛先 IP アドレス/サブネット" + +msgid "Destination IP address or subnet" +msgstr "宛先 IP アドレスまたはサブネット" + +msgid "Destination Port" +msgstr "宛先ポート番号" + +msgid "Destination Port/Port Range" +msgstr "宛先ポート/ポートの範囲" + +msgid "Destination port (integer in [1, 65535] or range in a:b)" +msgstr "宛先ポート ([1, 65535] の整数か a:b 形式の範囲)" + +msgctxt "Current status of a Firewall Group" +msgid "Down" +msgstr "停止中" + +msgid "Edit Firewall Group" +msgstr "ファイアウォールグループの編集" + +msgid "Edit FirewallGroup {{ name }}" +msgstr "ファイアウォールグループ {{ name }} の編集" + +msgid "Edit Policy" +msgstr "ポリシーの編集" + +msgid "Edit Policy {{ name }}" +msgstr "ポリシー {{ name }} の編集" + +msgid "Edit Rule" +msgstr "ルールの編集" + +msgid "Edit Rule {{ name }}" +msgstr "ルール {{ name }} の編集" + +msgid "Egress Policy" +msgstr "送信ポリシー" + +msgid "Egress Policy ID" +msgstr "送信ポリシー ID" + +msgid "Enabled" +msgstr "有効" + +msgctxt "Current status of a Firewall Group" +msgid "Error" +msgstr "エラー" + +#, python-format +msgid "Failed to add the port(s) to the firewall group %(name)s: %(reason)s" +msgstr "" +"ポートのファイアウォールグループ %(name)s への追加に失敗しました: %(reason)s" + +#, python-format +msgid "Failed to insert rule to policy %(name)s: %(reason)s" +msgstr "ポリシー %(name)s へのルールの挿入に失敗しました: %(reason)s" + +#, python-format +msgid "Failed to remove rule from policy %(name)s: %(reason)s" +msgstr "ポリシー %(name)s からのルールの削除に失敗しました: %(reason)s" + +#, python-format +msgid "" +"Failed to remove the port(s) from the firewall group %(name)s: %(reason)s" +msgstr "" +"ポートのファイアウォールグループ %(name)s からの削除に失敗しました: " +"%(reason)s" + +#, python-format +msgid "Failed to retrieve available rules: %s" +msgstr "利用可能なルールの取得に失敗しました: %s" + +#, python-format +msgid "Failed to retrieve current rules in policy %(name)s: %(reason)s" +msgstr "ポリシー %(name)s の現在のルールの取得に失敗しました: %(reason)s" + +msgid "Failed to retrieve port list of the firewall group." +msgstr "ファイアウォールグループのポート一覧を取得できません。" + +#, python-format +msgid "Failed to update firewall group %(name)s: %(reason)s" +msgstr "ファイアウォールグループ %(name)s の更新に失敗しました: %(reason)s" + +#, python-format +msgid "Failed to update policy %(name)s: %(reason)s" +msgstr "ポリシー %(name)s の更新に失敗しました: %(reason)s" + +#, python-format +msgid "Failed to update rule %(name)s: %(reason)s" +msgstr "ルール %(name)s の更新に失敗しました: %(reason)s" + +msgid "Firewall Groups" +msgstr "ファイアウォールグループ" + +msgid "Firewall Policies" +msgstr "ファイアウォールポリシー" + +msgid "Firewall Rules" +msgstr "ファイアウォールルール" + +#, python-format +msgid "Firewall group %s was successfully updated." +msgstr "ファイアウォールグループ %s が正常に更新されました。" + +msgid "FirewallGroup" +msgstr "ファイアウォールグループ" + +msgid "ICMP" +msgstr "ICMP" + +msgid "ID" +msgstr "ID" + +msgid "IP Version" +msgstr "IP バージョン" + +msgid "IP Version for Firewall Rule" +msgstr "ファイアウォールルールの IP バージョン" + +msgctxt "Current status of a Firewall Group" +msgid "Inactive" +msgstr "非稼働" + +msgid "Ingress Policy" +msgstr "受信ポリシー" + +msgid "Ingress Policy ID" +msgstr "受信ポリシー ID" + +msgid "Insert Rule" +msgstr "ルールの挿入" + +msgid "Insert Rule to Policy" +msgstr "ポリシーへのルールの挿入" + +msgid "Label" +msgstr "ラベル" + +msgid "Name" +msgstr "名前" + +msgid "No options available" +msgstr "利用可能なオプションはありません" + +msgid "None" +msgstr "なし" + +msgctxt "Current status of a Firewall Group" +msgid "Pending Create" +msgstr "作成待ち" + +msgctxt "Current status of a Firewall Group" +msgid "Pending Delete" +msgstr "削除待ち" + +msgctxt "Current status of a Firewall Group" +msgid "Pending Update" +msgstr "更新待ち" + +msgid "Policies" +msgstr "ポリシー" + +msgid "Policy" +msgstr "ポリシー" + +#, python-format +msgid "Policy %s was successfully updated." +msgstr "ポリシー %s が正常に更新されました。" + +msgid "Ports" +msgstr "ポート" + +msgid "Project ID" +msgstr "プロジェクト ID" + +msgid "Protocol" +msgstr "プロトコル" + +msgid "Protocol for the firewall rule" +msgstr "ファイアウォールルールのプロトコル" + +msgid "REJECT" +msgstr "REJECT" + +msgctxt "Action Name of a Firewall Rule" +msgid "REJECT" +msgstr "REJECT" + +msgid "Remove Port" +msgstr "ポートの削除" + +msgid "Remove Port from Firewall Group" +msgstr "ファイアウォールグループからのポートの削除" + +msgid "Remove Rule" +msgstr "ルールの削除" + +msgid "Remove Rule from Policy" +msgstr "ポリシーからのルールの削除" + +msgid "Remove port from FirewallGroup {{ name }}" +msgstr "ファイアウォールグループ {{ name }} からのポートの削除" + +#, python-format +msgid "Removed the port(s) from the firewall group %s successfully." +msgstr "ポートを正常にファイアウォールグループ %s から削除しました。" + +msgid "Rule" +msgstr "ルール" + +#, python-format +msgid "Rule %(rule)s was successfully inserted to policy %(policy)s." +msgstr "ルール %(rule)s のポリシー %(policy)s への挿入に成功しました。" + +#, python-format +msgid "Rule %(rule)s was successfully removed from policy %(policy)s." +msgstr "ポリシー %(policy)s からのルール %(rule)s の削除に成功しました。" + +#, python-format +msgid "Rule %s was successfully updated." +msgstr "ルール %s が正常に更新されました。" + +msgid "Rules" +msgstr "ルール" + +msgid "Save Changes" +msgstr "変更の保存" + +msgid "Scheduled deletion of Firewall Group" +msgid_plural "Scheduled deletion of Firewall Groups" +msgstr[0] "ファイアウォールグループの削除を予約しました" + +msgid "Scheduled deletion of Policy" +msgid_plural "Scheduled deletion of Policies" +msgstr[0] "ポリシーの削除を予約しました" + +msgid "Scheduled deletion of Rule" +msgid_plural "Scheduled deletion of Rules" +msgstr[0] "ルールの削除を予約しました" + +msgid "Select a Policy" +msgstr "ポリシーを選択してください" + +msgid "Select ports for your firewall group." +msgstr "ファイアウォールグループを適用するポートを選択してください。" + +msgid "Select rules for your policy." +msgstr "ポリシーに追加するルールの選択" + +msgid "Selected Ports" +msgstr "選択済みポート" + +msgid "Selected Rules" +msgstr "選択済みのルール" + +msgid "Shared" +msgstr "共有" + +msgid "Source IP" +msgstr "送信元 IP" + +msgid "Source IP Address" +msgstr "送信元 IP アドレス" + +msgid "Source IP Address/Subnet" +msgstr "送信元 IP アドレス/サブネット" + +msgid "Source IP address or subnet" +msgstr "送信元 IP アドレスまたはサブネット" + +msgid "Source Port" +msgstr "送信元ポート番号" + +msgid "Source Port/Port Range" +msgstr "送信元ポート/ポートの範囲" + +msgid "Source port (integer in [1, 65535] or range in a:b)" +msgstr "送信元ポート ([1, 65535] の整数か a:b 形式の範囲)" + +msgid "" +"Source/Destination Network Address and IP version are inconsistent. Please " +"make them consistent." +msgstr "" +"送信元 / 送信先のネットワークアドレスと IP バージョンが一致していません。これ" +"らを一致させてください。" + +msgid "Status" +msgstr "ステータス" + +msgid "TCP" +msgstr "TCP" + +msgid "UDP" +msgstr "UDP" + +msgctxt "Admin state of a Firewall Group" +msgid "UP" +msgstr "UP" + +#, python-format +msgid "Unable to add Firewall Group \"%s\"." +msgstr "ファイアウォールグループ \"%s\" を追加できません。" + +#, python-format +msgid "Unable to add Policy \"%s\"." +msgstr "ポリシー \"%s\" を追加できません。" + +#, python-format +msgid "Unable to add Rule \"%s\"." +msgstr "ルール \"%s\" を追加できません。" + +#, python-format +msgid "Unable to delete firewall group. %s" +msgstr "ファイアウォールグループを削除できません: %s" + +#, python-format +msgid "Unable to delete policy. %s" +msgstr "ポリシーを削除できません: %s" + +#, python-format +msgid "Unable to delete rule. %s" +msgstr "ルールを削除できません: %s" + +msgid "Unable to retrieve firewall group details." +msgstr "ファイアウォールグループの詳細を取得できません。" + +msgid "Unable to retrieve firewall group list." +msgstr "ファイアウォールグループの一覧を取得できません。" + +msgid "Unable to retrieve firewallgroup details." +msgstr "ファイアウォールグループの詳細を取得できません。" + +msgid "Unable to retrieve policies list." +msgstr "ポリシー一覧を取得できません。" + +msgid "Unable to retrieve policy details." +msgstr "ポリシーの詳細を取得できません。" + +#, python-format +msgid "Unable to retrieve policy list (%s)." +msgstr "ポリシー覧を取得できません (%s)" + +msgid "Unable to retrieve policy list." +msgstr "ポリシー一覧を取得できません。" + +#, python-format +msgid "Unable to retrieve ports (%s)." +msgstr "ポートを取得できません (%s)" + +msgid "Unable to retrieve rule details." +msgstr "ルールの詳細を取得できません。" + +#, python-format +msgid "Unable to retrieve rules (%s)." +msgstr "ルール一覧を取得できません (%s)" + +msgid "Unable to retrieve rules list." +msgstr "ルール一覧を取得できません。" + +msgid "Value" +msgstr "値" + +msgid "You may add ports to firewall group here." +msgstr "ファイアウォールグループにポートを追加できます。" + +msgid "You may remove ports from firewall group here." +msgstr "ファイアウォールグループからポートを削除できます。" + +msgid "You may update firewall group details here." +msgstr "ファイアウォールグループの詳細をここから更新できます。" + +msgid "" +"You may update policy details here. Use 'Insert Rule' or 'Remove Rule' links " +"instead to insert or remove a rule." +msgstr "" +"ポリシーの詳細をここから更新できます。ルールの挿入、削除はここではなく「ルー" +"ルの挿入」「ルールの削除」を使ってください。" + +msgid "You may update rule details here." +msgstr "ルールの詳細をここから更新できます。" diff --git a/neutron_fwaas_dashboard/locale/ko_KR/LC_MESSAGES/django.po b/neutron_fwaas_dashboard/locale/ko_KR/LC_MESSAGES/django.po new file mode 100644 index 0000000..e3563a5 --- /dev/null +++ b/neutron_fwaas_dashboard/locale/ko_KR/LC_MESSAGES/django.po @@ -0,0 +1,661 @@ +# Andrea Young Oak Li , 2016. #zanata +# Andreas Jaeger , 2016. #zanata +# Eunseop Shin , 2016. #zanata +# Ian Y. Choi , 2016. #zanata +# Sungjin Kang , 2016. #zanata +# ByungYeol Woo , 2017. #zanata +# Ian Y. Choi , 2017. #zanata +# Sungjin Kang , 2017. #zanata +# minwook-shin , 2017. #zanata +# Ian Y. Choi , 2018. #zanata +# Sungjin Kang , 2018. #zanata +# ByungYeol Woo , 2019. #zanata +msgid "" +msgstr "" +"Project-Id-Version: neutron-fwaas-dashboard VERSION\n" +"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" +"POT-Creation-Date: 2019-02-19 16:16+0000\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"PO-Revision-Date: 2019-03-05 12:40+0000\n" +"Last-Translator: ByungYeol Woo \n" +"Language-Team: Korean (South Korea)\n" +"Language: ko_KR\n" +"X-Generator: Zanata 4.3.3\n" +"Plural-Forms: nplurals=1; plural=0\n" + +msgid "-" +msgstr "-" + +msgid "ALLOW" +msgstr "ALLOW" + +msgctxt "Action Name of a Firewall Rule" +msgid "ALLOW" +msgstr "ALLOW" + +msgid "ANY" +msgstr "ANY" + +msgid "Action" +msgstr "Action" + +msgid "Action for the firewall rule" +msgstr "방화벽 규칙 작업" + +# auto translated by TM merge from project: neutron-lbaas-dashboard, version: stable-pike, DocId: neutron_lbaas_dashboard/locale/djangojs +msgctxt "Current status of a Firewall Group" +msgid "Active" +msgstr "Active" + +# auto translated by TM merge from project: horizon, version: master, DocId: horizon/locale/djangojs +msgid "Add" +msgstr "추가" + +msgid "Add Firewall Group" +msgstr "방화벽 그룹 추가" + +msgid "Add New Firewall Group" +msgstr "방화벽 그룹 추가" + +msgid "Add New Policy" +msgstr "새로운 정책 추가" + +msgid "Add New Rule" +msgstr "새로운 규칙 추가" + +msgid "Add Policy" +msgstr "정책 추가" + +msgid "Add Port" +msgstr "포트 추가" + +msgid "Add Port to Firewall Group" +msgstr "방화벽 그룹에 포트 추가" + +msgid "Add Rule" +msgstr "규칙 추가" + +msgid "Add port to Firewall Group {{ name }}" +msgstr "방화벽 그룹 {{ name }}에 포트 추가" + +#, python-format +msgid "Added Firewall Group\"%s\"." +msgstr "방화벽 그룹 \"%s\"를 추가하였습니다." + +#, python-format +msgid "Added Policy \"%s\"." +msgstr "정책 \"%s\"를 추가하였습니다.:" + +#, python-format +msgid "Added Rule \"%s\"." +msgstr "규칙 \"%s\"를 추가하였습니다." + +#, python-format +msgid "Added the port(s) to the firewall group %s successfully." +msgstr "성공적으로 방화벽 그룹 %s에 포트가 추가되었습니다." + +msgid "Admin State" +msgstr "관리 상태" + +# auto translated by TM merge from project: neutron-fwaas-dashboard, version: stable-queens, DocId: neutron_fwaas_dashboard/locale/django +msgid "Admin State Up" +msgstr "관리자 업 상태" + +msgid "After" +msgstr "이후" + +msgid "Audited" +msgstr "상태 확인" + +# auto translated by TM merge from project: neutron-fwaas-dashboard, version: stable-queens, DocId: neutron_fwaas_dashboard/locale/django +msgid "Available Ports" +msgstr "사용 가능한 포트" + +# auto translated by TM merge from project: neutron-fwaas-dashboard, version: stable-queens, DocId: neutron_fwaas_dashboard/locale/django +msgid "Available Rules" +msgstr "사용 가능한 규칙" + +msgid "Before" +msgstr "이전" + +msgid "Choose port(s) from Available Ports. " +msgstr "사용 가능한 포트에서 포트를 선택합니다." + +# auto translated by TM merge from project: neutron-fwaas-dashboard, version: stable-queens, DocId: neutron_fwaas_dashboard/locale/django +msgid "" +"Choose rule(s) from Available Rules to Selected Rule by push button or drag " +"and drop, you may change their order by drag and drop as well." +msgstr "" +"사용 가능한 규칙에서 선택한 규칙에 버튼을 누르거나 드래그 앤 드롭하여 규칙을 " +"선택합니다. 드래그 앤 드롭으로 규칙 순서를 변경할 수 있습니다." + +# auto translated by TM merge from project: neutron-fwaas-dashboard, version: stable-queens, DocId: neutron_fwaas_dashboard/locale/django +msgid "" +"Choose the rule you want to insert. Specify either the rule you want to " +"insert immediately before, or the rule to insert immediately after. If both " +"are specified, the prior takes precedence." +msgstr "" +"삽입할 규칙을 선택합니다. 바로 앞에 삽입할 규칙인지, 바로 뒤에 삽입할 규칙인" +"지를 정해야합니다. 양 쪽 모두 지정하면, 앞에 있는 것이 우선 순위가 높습니다." + +# auto translated by TM merge from project: neutron-fwaas-dashboard, version: stable-queens, DocId: neutron_fwaas_dashboard/locale/django +msgid "Choose the rule you want to remove." +msgstr "제거할 규칙을 선택하세요." + +msgid "Create Firewall Group" +msgstr "방화벽 그룹 생성" + +msgid "Create a Firewall Group with selected ports." +msgstr "선택한 포트와 함께 방화벽 그룹을 생성합니다." + +msgid "" +"Create a firewall group based on a policy.\n" +"\n" +"A firewall group represents a logical firewall resource that a tenant can " +"instantiate and manage. A firewall group must be associated with one policy, " +"all other fields are optional." +msgstr "" +"정책에 기반하여 방화벽 그룹을 생성합니다.\n" +"\n" +"방화벽 그룹은 tenant가 인스턴스화하고 관리할 수 있는 논리적인 방화벽 자원을 " +"나타냅니다. 방화벽 그룹은 반드시 한 정책과 연결되어야 하며, 그 외 다른 필드들" +"은 선택 사항입니다." + +msgid "" +"Create a firewall policy with an ordered list of firewall rules.\n" +"\n" +"A firewall policy is an ordered collection of firewall rules. So if the " +"traffic matches the first rule, the other rules are not executed. If the " +"traffic does not match the current rule, then the next rule is executed. A " +"firewall policy has the following attributes:\n" +"\n" +"
        • Shared: A firewall policy can be shared across tenants. Thus it can also " +"be made part of an audit workflow wherein the firewall policy can be audited " +"by the relevant entity that is authorized.
        • Audited: When audited is " +"set to True, it indicates that the firewall policy has been audited. Each " +"time the firewall policy or the associated firewall rules are changed, this " +"attribute will be set to False and will have to be explicitly set to True " +"through an update operation.
        • \n" +"The name field is required, all others are optional." +msgstr "" +"방화벽 정책을 방화벽 규칙에 대한 정렬된 목록과 함께 생성합니다.\n" +"\n" +"방화벽 정책은 특정 순서의 방화벽 규칙 모음입니다. 따라서 트래픽이 첫 번째 규" +"칙과 매칭되는 경우, 다른 규칙들은 실행되지 않습니다. 트래픽이 현재 규칙과 매" +"칭이 되지 않는 경우에는, 다음 규칙이 실행됩니다. 방화벽 정책은 다음 속성을 가" +"집니다:\n" +"\n" +"
        • Shared: 방화벽 정책은 tenant에 걸쳐 공유될 수 있습니다. 따라서 이는 방화" +"벽 정책이 인증된 관련 엔티티에 의해 감사가 이루어지는 감사 워크플로우의 일부" +"가 될 수 있습니다.
        • Audited: 해당 속성이 True로 설정되면, 해당 방화벽 " +"정책에 대해 감사가 이루어졌음을 가리킵니다. 방화벽 정책 또는 연관된 방화벽 규" +"칙이 변경될 때마다 해당 속성은 False로 설정된 후 업데이트 작업이 이루어진 다" +"음 명확히 True로 설정될 것입니다.
        • \n" +"name 필드는 필수이며, 그 외는 선택 사항입니다." + +msgid "" +"Create a firewall rule.\n" +"\n" +"A Firewall rule is an association of the following attributes:\n" +"\n" +"
        • IP Addresses: The addresses from/to which the traffic filtration needs " +"to be applied.
        • IP Version: The type of IP packets (IP V4/V6) that " +"needs to be filtered.
        • Protocol: Type of packets (UDP, ICMP, TCP, " +"Any) that needs to be checked.
        • Action: Action is the type of " +"filtration required, it can be Reject/Deny/Allow data packets.
        • \n" +"The protocol and action fields are required, all others are optional." +msgstr "" +"방화벽 규칙을 생성합니다.\n" +"\n" +"방화벽 규칙은 다음과 같은 속성이 있습니다:\n" +"\n" +"
        • IP 주소: 트래픽 필터링이 적용되어야 하는 들어오는/나가는 주소입니다.
        • IP 버전: 필터링을 필요로 하는 IP 패킷에 대한 유형입니다 (IP V4/V6).
        • 프로토콜: 확인될 필요가 있는 패킷 유형입니다 (UDP, ICMP, TCP, Any).
        • 액션: 액션은 필요로 하는 필터링 유형으로, 데이터 패킷에 대해 Reject/" +"Deny/Allow 가 될 수 있습니다.
        • \n" +"프로토콜 및 애견 필드는 필수이며, 그 외는 선택 사항입니다." + +msgid "Create a policy with selected rules." +msgstr "선택한 규칙에 대한 정책를 생성합니다." + +# auto translated by TM merge from project: designate-dashboard, version: master, DocId: designatedashboard/locale/django +msgctxt "Current status of a Firewall Group" +msgid "Created" +msgstr "생성됨" + +msgid "DENY" +msgstr "DENY" + +msgctxt "Action Name of a Firewall Rule" +msgid "DENY" +msgstr "DENY" + +# auto translated by TM merge from project: horizon, version: stable-mitaka, DocId: openstack_dashboard/locale/django +msgctxt "Admin state of a Firewall Group" +msgid "DOWN" +msgstr "DOWN" + +msgid "Delete Firewall Group" +msgid_plural "Delete Firewall Groups" +msgstr[0] "방화벽 그룹 삭제" + +msgid "Delete Policy" +msgid_plural "Delete Policies" +msgstr[0] "정책 삭제" + +msgid "Delete Rule" +msgid_plural "Delete Rules" +msgstr[0] "규칙 삭제" + +msgid "Description" +msgstr "설명" + +# auto translated by TM merge from project: neutron-fwaas-dashboard, version: stable-queens, DocId: neutron_fwaas_dashboard/locale/django +msgid "Description:" +msgstr "설명:" + +msgid "Destination IP" +msgstr "대상 IP" + +# auto translated by TM merge from project: neutron-fwaas-dashboard, version: stable-queens, DocId: neutron_fwaas_dashboard/locale/django +msgid "Destination IP Address" +msgstr "대상 IP 주소" + +msgid "Destination IP Address/Subnet" +msgstr "대상 IP 주소/서브넷" + +msgid "Destination IP address or subnet" +msgstr "대상 IP 주소 또는 서브넷" + +msgid "Destination Port" +msgstr "대상 포트" + +msgid "Destination Port/Port Range" +msgstr "대상 포트/포트 범위" + +msgid "Destination port (integer in [1, 65535] or range in a:b)" +msgstr "대상 포트 ([1, 65535] 중 정수거나 a:b 범위 내)" + +# auto translated by TM merge from project: zun-ui, version: master, DocId: zun_ui/locale/djangojs +msgctxt "Current status of a Firewall Group" +msgid "Down" +msgstr "Down" + +msgid "Edit Firewall Group" +msgstr "방화벽 그룹 편집" + +msgid "Edit FirewallGroup {{ name }}" +msgstr "FirewallGroup {{ name }} 편집" + +msgid "Edit Policy" +msgstr "정책 편집" + +msgid "Edit Policy {{ name }}" +msgstr "정책 {{ name }} 편집" + +msgid "Edit Rule" +msgstr "규칙 편집" + +msgid "Edit Rule {{ name }}" +msgstr "{{ name }} 규칙 편집" + +msgid "Egress Policy" +msgstr "Egress 정책" + +msgid "Egress Policy ID" +msgstr "Egress 정책 ID" + +msgid "Enabled" +msgstr "사용됨" + +# auto translated by TM merge from project: neutron-lbaas-dashboard, version: master, DocId: neutron_lbaas_dashboard/locale/djangojs +msgctxt "Current status of a Firewall Group" +msgid "Error" +msgstr "오류" + +#, python-format +msgid "Failed to add the port(s) to the firewall group %(name)s: %(reason)s" +msgstr "방화벽 그룹 %(name)s 에 포트 추가하기가 실패했습니다: %(reason)s" + +#, python-format +msgid "Failed to insert rule to policy %(name)s: %(reason)s" +msgstr "%(name)s 정책에 규칙을 추가하지 못하였습니다.: %(reason)s" + +#, python-format +msgid "Failed to remove rule from policy %(name)s: %(reason)s" +msgstr "규칙을 %(name)s 정책에서 제거하지 못하였습니다.: %(reason)s" + +#, python-format +msgid "" +"Failed to remove the port(s) from the firewall group %(name)s: %(reason)s" +msgstr "방화벽 그룹 %(name)s 에 포트 제거하기가 실패했습니다: %(reason)s" + +#, python-format +msgid "Failed to retrieve available rules: %s" +msgstr "사용가능한 규칙을 검색하지 못하였습니다.: %s" + +#, python-format +msgid "Failed to retrieve current rules in policy %(name)s: %(reason)s" +msgstr "%(name)s 정책에서 현재 규칙을 찾지 못하였습니다.: %(reason)s" + +msgid "Failed to retrieve port list of the firewall group." +msgstr "방화벽 그룹에 대한 포트 목록을 가져올 수 없습니다." + +#, python-format +msgid "Failed to update firewall group %(name)s: %(reason)s" +msgstr "방화벽 그룹 %(name)s 업데이트에 실패했습니다: %(reason)s" + +#, python-format +msgid "Failed to update policy %(name)s: %(reason)s" +msgstr "정책 %(name)s를 업데이트하지 못하였습니다.: %(reason)s" + +#, python-format +msgid "Failed to update rule %(name)s: %(reason)s" +msgstr "%(name)s 규칙을 업데이트하지 못하였습니다.: %(reason)s" + +msgid "Firewall Groups" +msgstr "방화벽 그룹" + +msgid "Firewall Policies" +msgstr "방화벽 정책" + +msgid "Firewall Rules" +msgstr "방화벽 규칙" + +#, python-format +msgid "Firewall group %s was successfully updated." +msgstr "방화벽 그룹 %s은 성공적으로 업데이트 되었습니다." + +msgid "FirewallGroup" +msgstr "FirewallGroup" + +# auto translated by TM merge from project: openstack-manuals, version: stable-liberty, DocId: doc/common-rst/source/locale/common-rst +msgid "ICMP" +msgstr "ICMP" + +# auto translated by TM merge from project: neutron-fwaas-dashboard, version: stable-queens, DocId: neutron_fwaas_dashboard/locale/django +msgid "ID" +msgstr "ID" + +msgid "IP Version" +msgstr "IP 버전" + +msgid "IP Version for Firewall Rule" +msgstr "방화벽 규칙에 대한 IP 버전" + +# auto translated by TM merge from project: horizon, version: stable-mitaka, DocId: openstack_dashboard/locale/django +msgctxt "Current status of a Firewall Group" +msgid "Inactive" +msgstr "Inactive" + +msgid "Ingress Policy" +msgstr "Ingress 정책" + +msgid "Ingress Policy ID" +msgstr "Ingress 정책 ID" + +msgid "Insert Rule" +msgstr "규칙 추가" + +msgid "Insert Rule to Policy" +msgstr "정책에 규칙을 추가" + +msgid "Label" +msgstr "레이블" + +msgid "Name" +msgstr "이름" + +msgid "No options available" +msgstr "사용 가능한 옵션이 없습니다" + +msgid "None" +msgstr "없음" + +# auto translated by TM merge from project: horizon, version: stable-mitaka, DocId: openstack_dashboard/locale/django +msgctxt "Current status of a Firewall Group" +msgid "Pending Create" +msgstr "대기중인 생성" + +# auto translated by TM merge from project: horizon, version: stable-mitaka, DocId: openstack_dashboard/locale/django +msgctxt "Current status of a Firewall Group" +msgid "Pending Delete" +msgstr "대기중인 삭제" + +# auto translated by TM merge from project: horizon, version: stable-mitaka, DocId: openstack_dashboard/locale/django +msgctxt "Current status of a Firewall Group" +msgid "Pending Update" +msgstr "대기중인 업데이트" + +msgid "Policies" +msgstr "정책" + +msgid "Policy" +msgstr "정책" + +#, python-format +msgid "Policy %s was successfully updated." +msgstr "정책 %s를 성공적으로 업데이트 하였습니다." + +msgid "Ports" +msgstr "포트" + +# auto translated by TM merge from project: neutron-fwaas-dashboard, version: stable-queens, DocId: neutron_fwaas_dashboard/locale/django +msgid "Project ID" +msgstr "프로젝트 ID" + +# auto translated by TM merge from project: zun-ui, version: master, DocId: zun_ui/locale/djangojs +msgid "Protocol" +msgstr "프로토콜" + +msgid "Protocol for the firewall rule" +msgstr "방화벽 규칙 프로토콜" + +msgid "REJECT" +msgstr "REJECT" + +msgctxt "Action Name of a Firewall Rule" +msgid "REJECT" +msgstr "REJECT" + +msgid "Remove Port" +msgstr "포트 제거" + +msgid "Remove Port from Firewall Group" +msgstr "방화벽 그룹에 포트 제거" + +msgid "Remove Rule" +msgstr "규칙 삭제" + +msgid "Remove Rule from Policy" +msgstr "정책에서 규칙 제거" + +msgid "Remove port from FirewallGroup {{ name }}" +msgstr "FirewallGroup {{ name }} 에서 포트 제거" + +#, python-format +msgid "Removed the port(s) from the firewall group %s successfully." +msgstr "성공적으로 방화벽 그룹 %s에서 포트를 제거하였습니다." + +msgid "Rule" +msgstr "규칙" + +#, python-format +msgid "Rule %(rule)s was successfully inserted to policy %(policy)s." +msgstr "%(rule)s 규칙을 %(policy)s 정책에 성공적으로 추가하였습니다." + +#, python-format +msgid "Rule %(rule)s was successfully removed from policy %(policy)s." +msgstr "%(rule)s 규칙을 %(policy)s 정책에서 성공적으로 제거하였습니다." + +#, python-format +msgid "Rule %s was successfully updated." +msgstr "%s 규칙을 성공적으로 업데이트 하였습니다." + +msgid "Rules" +msgstr "규칙" + +msgid "Save Changes" +msgstr "변경사항 저장" + +msgid "Scheduled deletion of Firewall Group" +msgid_plural "Scheduled deletion of Firewall Groups" +msgstr[0] "방화벽 그룹에 대한 예약된 삭제" + +msgid "Scheduled deletion of Policy" +msgid_plural "Scheduled deletion of Policies" +msgstr[0] "정책 예약 삭제" + +msgid "Scheduled deletion of Rule" +msgid_plural "Scheduled deletion of Rules" +msgstr[0] "규칙 예약 삭제" + +msgid "Select a Policy" +msgstr "정책 선택합니다." + +msgid "Select ports for your firewall group." +msgstr "방화벽 그룹에 대한 포트를 선택합니다." + +msgid "Select rules for your policy." +msgstr "정책을 위해 규칙을 선택합니다." + +msgid "Selected Ports" +msgstr "선택한 포트" + +# auto translated by TM merge from project: neutron-fwaas-dashboard, version: stable-queens, DocId: neutron_fwaas_dashboard/locale/django +msgid "Selected Rules" +msgstr "규칙을 선택합니다." + +msgid "Shared" +msgstr "공유" + +# auto translated by TM merge from project: neutron-lbaas-dashboard, version: master, DocId: neutron_lbaas_dashboard/locale/djangojs +msgid "Source IP" +msgstr "소스 IP" + +# auto translated by TM merge from project: neutron-fwaas-dashboard, version: stable-queens, DocId: neutron_fwaas_dashboard/locale/django +msgid "Source IP Address" +msgstr "소스 IP 주소" + +msgid "Source IP Address/Subnet" +msgstr "소스 IP 주소/서브넷" + +msgid "Source IP address or subnet" +msgstr "소스 IP 주소 또는 서브넷" + +msgid "Source Port" +msgstr "소스 포트" + +msgid "Source Port/Port Range" +msgstr "소스 포트/포트 범위" + +msgid "Source port (integer in [1, 65535] or range in a:b)" +msgstr "소스 포트 (정수 [1, 65535] 사이 또는 a:b 범위)" + +msgid "" +"Source/Destination Network Address and IP version are inconsistent. Please " +"make them consistent." +msgstr "" +"원본/대상 네트워크 주소와 IP 버전이 일치하지 않습니다. 일치 시켜주세요." + +msgid "Status" +msgstr "상태" + +msgid "TCP" +msgstr "TCP" + +msgid "UDP" +msgstr "UDP" + +# auto translated by TM merge from project: horizon, version: stable-mitaka, DocId: openstack_dashboard/locale/django +msgctxt "Admin state of a Firewall Group" +msgid "UP" +msgstr "UP" + +#, python-format +msgid "Unable to add Firewall Group \"%s\"." +msgstr "방화벽 그룹 \"%s\"를 추가할 수 없습니다." + +#, python-format +msgid "Unable to add Policy \"%s\"." +msgstr "정책 \"%s\"를 추가할 수 없습니다." + +#, python-format +msgid "Unable to add Rule \"%s\"." +msgstr "규칙 \"%s\"를 추가할 수 없습니다." + +#, python-format +msgid "Unable to delete firewall group. %s" +msgstr "방화벽 그룹 %s를 삭제할 수 없습니다." + +#, python-format +msgid "Unable to delete policy. %s" +msgstr "정책를 삭제할 수 없습니다. %s" + +#, python-format +msgid "Unable to delete rule. %s" +msgstr "규칙을 삭제할 수 없습니다. %s" + +msgid "Unable to retrieve firewall group details." +msgstr "방화벽 그룹 상세 정볼르 가져올 수 없습니다." + +msgid "Unable to retrieve firewall group list." +msgstr "방화벽 그룹 목록을 가져올 수 없습니다." + +msgid "Unable to retrieve firewallgroup details." +msgstr "방화벽 그룹 세부 사항을 가져올 수 없습니다." + +msgid "Unable to retrieve policies list." +msgstr "정책 목록을 찾지 못 했습니다." + +msgid "Unable to retrieve policy details." +msgstr "정책 세부 정보를 찾지 못 했습니다." + +#, python-format +msgid "Unable to retrieve policy list (%s)." +msgstr "정책 목록 (%s) 을(를) 가져올 수 없습니다." + +msgid "Unable to retrieve policy list." +msgstr "정책 목록을 찾지 못 했습니다." + +#, python-format +msgid "Unable to retrieve ports (%s)." +msgstr "포트 (%s) 을(를) 가져올 수 없습니다." + +msgid "Unable to retrieve rule details." +msgstr "규칙 세부 정보를 찾지 못 했습니다." + +#, python-format +msgid "Unable to retrieve rules (%s)." +msgstr "규칙 (%s) 을(를) 가져올 수 없습니다." + +msgid "Unable to retrieve rules list." +msgstr "규칙 목록을 찾지 못 했습니다." + +msgid "Value" +msgstr "값" + +msgid "You may add ports to firewall group here." +msgstr "이 방화벽 그룹에 포트를 추가할 수 있습니다." + +msgid "You may remove ports from firewall group here." +msgstr "이 방화벽 그룹에서 포트를 제거할 수 있습니다." + +# auto translated by TM merge from project: neutron-fwaas-dashboard, version: stable-queens, DocId: neutron_fwaas_dashboard/locale/django +msgid "You may update firewall group details here." +msgstr "방화벽 그룹 세부 사항을 여기서 업데이트 할 수 있습니다." + +# auto translated by TM merge from project: neutron-fwaas-dashboard, version: stable-queens, DocId: neutron_fwaas_dashboard/locale/django +msgid "" +"You may update policy details here. Use 'Insert Rule' or 'Remove Rule' links " +"instead to insert or remove a rule." +msgstr "" +"여기서 정책의 세부 사항을 업데이트 할 수 있습니다. 규칙을 삽입하거나 삭제하" +"는 대신에 '규칙 삽입'나 '규칙 삭제' 링크를 사용하십시오." + +# auto translated by TM merge from project: neutron-fwaas-dashboard, version: stable-queens, DocId: neutron_fwaas_dashboard/locale/django +msgid "You may update rule details here." +msgstr "여기서 규칙 세부 사항을 업데이트 할 수 있습니다." diff --git a/neutron_fwaas_dashboard/locale/pt_BR/LC_MESSAGES/django.po b/neutron_fwaas_dashboard/locale/pt_BR/LC_MESSAGES/django.po new file mode 100644 index 0000000..e3a76a8 --- /dev/null +++ b/neutron_fwaas_dashboard/locale/pt_BR/LC_MESSAGES/django.po @@ -0,0 +1,425 @@ +# Carlos Marques , 2016. #zanata +# José Mello , 2016. #zanata +# Fernando Pimenta , 2017. #zanata +# José Mello , 2017. #zanata +# Rodrigo Loures , 2018. #zanata +msgid "" +msgstr "" +"Project-Id-Version: neutron-fwaas-dashboard VERSION\n" +"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" +"POT-Creation-Date: 2019-02-19 16:16+0000\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"PO-Revision-Date: 2018-05-02 12:55+0000\n" +"Last-Translator: Rodrigo Loures \n" +"Language-Team: Portuguese (Brazil)\n" +"Language: pt_BR\n" +"X-Generator: Zanata 4.3.3\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" + +msgid "-" +msgstr "-" + +msgid "ALLOW" +msgstr "ALLOW" + +msgctxt "Action Name of a Firewall Rule" +msgid "ALLOW" +msgstr "ALLOW" + +msgid "ANY" +msgstr "ANY" + +msgid "Action" +msgstr "Ação" + +msgid "Action for the firewall rule" +msgstr "Ação para a regra de firewall" + +msgid "Add" +msgstr "Adicionar" + +msgid "Add New Policy" +msgstr "Adicionar Nova Política" + +msgid "Add New Rule" +msgstr "Adicionar Nova Regra" + +msgid "Add Policy" +msgstr "Adicionar Política" + +msgid "Add Rule" +msgstr "Adicionar Regra" + +#, python-format +msgid "Added Policy \"%s\"." +msgstr "Política Adicionada \"%s\"." + +#, python-format +msgid "Added Rule \"%s\"." +msgstr "Adicionada Regra \"%s\"." + +msgid "Admin State" +msgstr "Estado de Administração" + +msgid "Admin State Up" +msgstr "Estado Administrativo Ligado" + +msgid "After" +msgstr "Após" + +msgid "Audited" +msgstr "Auditado" + +msgid "Available Rules" +msgstr "Regras Disponíveis" + +msgid "Before" +msgstr "Antes" + +msgid "" +"Choose rule(s) from Available Rules to Selected Rule by push button or drag " +"and drop, you may change their order by drag and drop as well." +msgstr "" +"Escolha regra(s) a partir de Regras Disponíveis para Regras Selecionadas " +"clicando no botão ou arrastando e soltando, você pode alterar sua ordem " +"arrastando e soltando também." + +msgid "" +"Choose the rule you want to insert. Specify either the rule you want to " +"insert immediately before, or the rule to insert immediately after. If both " +"are specified, the prior takes precedence." +msgstr "" +"Escolha a regra que você deseja inserir. Especifique a regra que você deseja " +"inserir imediatamente antes, ou a regra para inserir imediatamente após. Se " +"ambos forem especificados, o anterior tem precedência." + +msgid "Choose the rule you want to remove." +msgstr "Escolha a regra que você quer remover." + +msgid "" +"Create a firewall policy with an ordered list of firewall rules.\n" +"\n" +"A firewall policy is an ordered collection of firewall rules. So if the " +"traffic matches the first rule, the other rules are not executed. If the " +"traffic does not match the current rule, then the next rule is executed. A " +"firewall policy has the following attributes:\n" +"\n" +"
        • Shared: A firewall policy can be shared across tenants. Thus it can also " +"be made part of an audit workflow wherein the firewall policy can be audited " +"by the relevant entity that is authorized.
        • Audited: When audited is " +"set to True, it indicates that the firewall policy has been audited. Each " +"time the firewall policy or the associated firewall rules are changed, this " +"attribute will be set to False and will have to be explicitly set to True " +"through an update operation.
        • \n" +"The name field is required, all others are optional." +msgstr "" +"Crie uma política de firewall com uma lista ordenada de regras de firewall.\n" +"\n" +"Uma política de firewall é uma coleta ordenada de regras de firewall. " +"Portanto, se o tráfego corresponder à primeira regra, as outras regras não " +"serão executadas. Se o tráfego não corresponder à regra atual, então a " +"próxima regra será executada. Uma política de firewall possui os seguintes " +"atributos:\n" +"\n" +"
        • Compartilhado: Uma política de firewall pode ser compartilhada entre " +"locatários. Assim, ela também pode fazer parte de um fluxo de trabalho de " +"auditoria no qual a política de firewall pode ser auditada pela entidade " +"relevante que estiver autorizada.
        • Auditada: Quando auditada estiver " +"configurada para True, indica que a política de firewall foi auditada. " +"Sempre que a política de firewall ou as regras de firewall associadas forem " +"alteradas, esse atributo será configurado para False e precisará ser " +"configurado explicitamente para True por meio de uma operação de atualização." +"
        • \n" +"O campo de nome é obrigatório, e todos os outros são opcionais." + +msgid "" +"Create a firewall rule.\n" +"\n" +"A Firewall rule is an association of the following attributes:\n" +"\n" +"
        • IP Addresses: The addresses from/to which the traffic filtration needs " +"to be applied.
        • IP Version: The type of IP packets (IP V4/V6) that " +"needs to be filtered.
        • Protocol: Type of packets (UDP, ICMP, TCP, " +"Any) that needs to be checked.
        • Action: Action is the type of " +"filtration required, it can be Reject/Deny/Allow data packets.
        • \n" +"The protocol and action fields are required, all others are optional." +msgstr "" +"Crie uma regra de firewall.\n" +"\n" +"Uma regra de Firewall é uma associação dos seguintes atributos:\n" +"\n" +"
        • Endereços IP: Os endereços a partir dos quais a filtragem de tráfego " +"precisa ser aplicada
        • Versão do IP: O tipo de pacotes de IP (IP V4/" +"V6) que precisa ser filtrado.
        • Protocolo: O tipo de pacotes (UDP, " +"ICMP, TCP, Any) que precisa ser verificado.
        • Ação: Ação é o tipo de " +"filtragem necessário, que pode ser Rejeitar/Negar/Permitir pacotes de dados." +"
        • \n" +"Os campos de protocolo e de ação são obrigatórios, e todos os outros são " +"opcionais." + +msgid "Create a policy with selected rules." +msgstr "Criar uma política com as regras selecionadas." + +msgid "DENY" +msgstr "DENY" + +msgctxt "Action Name of a Firewall Rule" +msgid "DENY" +msgstr "DENY" + +msgid "Delete Policy" +msgid_plural "Delete Policies" +msgstr[0] "Excluir Política" +msgstr[1] "Excluir Políticas" + +msgid "Delete Rule" +msgid_plural "Delete Rules" +msgstr[0] "Excluir Regra" +msgstr[1] "Excluir Regras" + +# #-#-#-#-# app_reserved_uids.pot (Installation Guide 0.1) #-#-#-#-# +# #-#-#-#-# basic_environment.pot (Installation Guide 0.1) #-#-#-#-# +# #-#-#-#-# overview.pot (Installation Guide 0.1) #-#-#-#-# +msgid "Description" +msgstr "Descrição" + +msgid "Description:" +msgstr "Descrição:" + +msgid "Destination IP" +msgstr "IP de Destino" + +msgid "Destination IP Address" +msgstr "Endereço IP de Destino" + +msgid "Destination IP Address/Subnet" +msgstr "Endereço IP/Sub-rede de Destino" + +msgid "Destination IP address or subnet" +msgstr "Endereço IP ou sub-rede de destino" + +msgid "Destination Port" +msgstr "Porta de Destino" + +msgid "Destination Port/Port Range" +msgstr "Porta destino/Faixa de Porta" + +msgid "Destination port (integer in [1, 65535] or range in a:b)" +msgstr "Porta de destino (número inteiro em [1, 65535] ou faixa em a:b)" + +msgid "Edit Policy" +msgstr "Editar Política" + +msgid "Edit Policy {{ name }}" +msgstr "Editar política {{ name }}" + +msgid "Edit Rule" +msgstr "Editar Regra" + +msgid "Edit Rule {{ name }}" +msgstr "Editar Regra {{ name }}" + +msgid "Enabled" +msgstr "Habilitado" + +#, python-format +msgid "Failed to insert rule to policy %(name)s: %(reason)s" +msgstr "Falha ao inserir regra na política %(name)s: %(reason)s" + +#, python-format +msgid "Failed to remove rule from policy %(name)s: %(reason)s" +msgstr "Falha ao remover regra da política %(name)s: %(reason)s" + +#, python-format +msgid "Failed to retrieve available rules: %s" +msgstr "Falha ao recuperar as regras disponíveis: %s" + +#, python-format +msgid "Failed to retrieve current rules in policy %(name)s: %(reason)s" +msgstr "Falha ao recuperar as regras atuais na política %(name)s: %(reason)s" + +#, python-format +msgid "Failed to update policy %(name)s: %(reason)s" +msgstr "Falha para atualizar a política %(name)s: %(reason)s" + +#, python-format +msgid "Failed to update rule %(name)s: %(reason)s" +msgstr "Falha ao atualizar a regra %(name)s: %(reason)s" + +msgid "Firewall Policies" +msgstr "Políticas de Firewall" + +msgid "Firewall Rules" +msgstr "Regras de Firewall" + +msgid "ICMP" +msgstr "ICMP" + +msgid "ID" +msgstr "ID" + +msgid "IP Version" +msgstr "Versão do IP" + +msgid "IP Version for Firewall Rule" +msgstr "Versão do IP para Regra de Firewall" + +msgid "Insert Rule" +msgstr "Inserir Regra" + +msgid "Insert Rule to Policy" +msgstr "Inserir regra na Política" + +msgid "Name" +msgstr "Nome" + +msgid "None" +msgstr "Nenhum" + +msgid "Policies" +msgstr "Políticas" + +msgid "Policy" +msgstr "Política" + +#, python-format +msgid "Policy %s was successfully updated." +msgstr "A política %s foi atualizada com sucesso." + +msgid "Ports" +msgstr "Portas" + +msgid "Project ID" +msgstr "ID do projeto" + +msgid "Protocol" +msgstr "Protocolo" + +msgid "Protocol for the firewall rule" +msgstr "Protocolo para a regra de firewall" + +msgid "REJECT" +msgstr "REJECT" + +msgctxt "Action Name of a Firewall Rule" +msgid "REJECT" +msgstr "REJECT" + +msgid "Remove Rule" +msgstr "Remover Regra" + +msgid "Remove Rule from Policy" +msgstr "Remover Regra da Política" + +msgid "Rule" +msgstr "Regra" + +#, python-format +msgid "Rule %(rule)s was successfully inserted to policy %(policy)s." +msgstr "A regra %(rule)s foi adicionada com sucesso na política %(policy)s." + +#, python-format +msgid "Rule %(rule)s was successfully removed from policy %(policy)s." +msgstr "A regra %(rule)s foi removida com sucesso da política %(policy)s." + +#, python-format +msgid "Rule %s was successfully updated." +msgstr "A regra %s foi atualizada com sucesso." + +msgid "Rules" +msgstr "Regras" + +msgid "Save Changes" +msgstr "Salvar Alterações" + +msgid "Scheduled deletion of Policy" +msgid_plural "Scheduled deletion of Policies" +msgstr[0] "Exclusão agendada de Política" +msgstr[1] "Exclusão agendada de Políticas" + +msgid "Scheduled deletion of Rule" +msgid_plural "Scheduled deletion of Rules" +msgstr[0] "Exclusão agendada de Regra" +msgstr[1] "Exclusão agendada de Regras" + +msgid "Select a Policy" +msgstr "Selecione uma Política" + +msgid "Select rules for your policy." +msgstr "Selecionar regras para a sua política." + +msgid "Selected Rules" +msgstr "Regras Selecionadas" + +msgid "Shared" +msgstr "Compartilhado" + +msgid "Source IP" +msgstr "IP de origem" + +msgid "Source IP Address" +msgstr "Endereço IP de Origem" + +msgid "Source IP Address/Subnet" +msgstr "Endereço IP/Sub-rede de Origem" + +msgid "Source IP address or subnet" +msgstr "Endereço IP ou sub-rede de origem" + +msgid "Source Port" +msgstr "Porta de Origem" + +msgid "Source Port/Port Range" +msgstr "Porta de Origem/Faixa de Porta" + +msgid "Source port (integer in [1, 65535] or range in a:b)" +msgstr "Porta de origem (número inteiro em [1, 65535] ou faixa em a:b)" + +msgid "" +"Source/Destination Network Address and IP version are inconsistent. Please " +"make them consistent." +msgstr "" +"O Endereço de Rede de Origem/Destino e a Versão do IP estão inconsistentes. " +"Torne-os consistentes" + +msgid "Status" +msgstr "Status" + +msgid "TCP" +msgstr "TCP" + +msgid "UDP" +msgstr "UDP" + +#, python-format +msgid "Unable to add Policy \"%s\"." +msgstr "Não é possível adicionar a Política \"%s\"." + +#, python-format +msgid "Unable to add Rule \"%s\"." +msgstr "Não é possível adicionar a Regra \"%s\"." + +#, python-format +msgid "Unable to delete policy. %s" +msgstr "Não é possível excluir a política. %s" + +#, python-format +msgid "Unable to delete rule. %s" +msgstr "Não é possível excluir regra. %s" + +msgid "Unable to retrieve policies list." +msgstr "Não é possível recuperar a lista de políticas." + +msgid "Unable to retrieve policy details." +msgstr "Não é possível recuperar detalhes da política." + +msgid "Unable to retrieve policy list." +msgstr "Não é possível recuperar a lista de políticas." + +msgid "Unable to retrieve rule details." +msgstr "Não é possível recuperar detalhes da regra." + +msgid "Unable to retrieve rules list." +msgstr "Não é possível recuperar lista de regras." diff --git a/neutron_fwaas_dashboard/locale/ru/LC_MESSAGES/django.po b/neutron_fwaas_dashboard/locale/ru/LC_MESSAGES/django.po new file mode 100644 index 0000000..bc846e1 --- /dev/null +++ b/neutron_fwaas_dashboard/locale/ru/LC_MESSAGES/django.po @@ -0,0 +1,655 @@ +# Antonio Kless , 2015. #zanata +# OpenStack Infra , 2015. #zanata +# Aleksey Alekseenko <9118250541@mail.ru>, 2016. #zanata +# Alexander , 2016. #zanata +# Andreas Jaeger , 2016. #zanata +# Artem , 2016. #zanata +# Daisy , 2016. #zanata +# Fedor Tarasenko , 2016. #zanata +# Irina Kochetova , 2016. #zanata +# Maxim Bozhenko , 2016. #zanata +# Valentin Chikunov , 2016. #zanata +# Artem , 2017. #zanata +# Ilya Alekseyev , 2017. #zanata +# Roman Gorshunov , 2019. #zanata +msgid "" +msgstr "" +"Project-Id-Version: neutron-fwaas-dashboard VERSION\n" +"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" +"POT-Creation-Date: 2019-08-10 13:30+0000\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"PO-Revision-Date: 2019-08-13 06:01+0000\n" +"Last-Translator: Roman Gorshunov \n" +"Language-Team: Russian\n" +"Language: ru\n" +"X-Generator: Zanata 4.3.3\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" + +msgid "-" +msgstr "-" + +msgid "ALLOW" +msgstr "ALLOW" + +msgctxt "Action Name of a Firewall Rule" +msgid "ALLOW" +msgstr "ALLOW" + +msgid "ANY" +msgstr "Любой" + +msgid "Action" +msgstr "Действие" + +msgid "Action for the firewall rule" +msgstr "Действие для правила межсетевого экрана." + +msgctxt "Current status of a Firewall Group" +msgid "Active" +msgstr "Активен" + +msgid "Add" +msgstr "Добавить" + +msgid "Add Firewall Group" +msgstr "Добавить новую группу межсетевого экрана" + +msgid "Add New Firewall Group" +msgstr "Добавить новую группу межсетевого экрана" + +msgid "Add New Policy" +msgstr "Добавить новую политику" + +msgid "Add New Rule" +msgstr "Добавить новое правило" + +msgid "Add Policy" +msgstr "Добавить политику" + +msgid "Add Port" +msgstr "Добавить порт" + +msgid "Add Port to Firewall Group" +msgstr "Добавить порт в группу межсетевого экрана" + +msgid "Add Rule" +msgstr "Добавить правило" + +msgid "Add port to Firewall Group {{ name }}" +msgstr "Добавить порт в группу межсетевого экрана {{ name }}" + +#, python-format +msgid "Added Firewall Group\"%s\"." +msgstr "Добавлена группа межсетевого экрана \"%s\"." + +#, python-format +msgid "Added Policy \"%s\"." +msgstr "Добалена политика \"%s\"." + +#, python-format +msgid "Added Rule \"%s\"." +msgstr "Добавлено правило \"%s\"." + +#, python-format +msgid "Added the port(s) to the firewall group %s successfully." +msgstr "Успешно добавлены порты в группу %s межсетевого экрана." + +msgid "Admin State" +msgstr "Административное состояние" + +msgid "Admin State Up" +msgstr "Административное состояние UP" + +msgid "After" +msgstr "После" + +msgid "Audited" +msgstr "Проверено" + +msgid "Available Ports" +msgstr "Доступные порты" + +msgid "Available Rules" +msgstr "Доступные правила" + +msgid "Before" +msgstr "Перед" + +msgid "Choose port(s) from Available Ports. " +msgstr "Выберите порт(ы) из доступных портов" + +msgid "" +"Choose rule(s) from Available Rules to Selected Rule by push button or drag " +"and drop, you may change their order by drag and drop as well." +msgstr "" +"Переместите правила из Доступных в Выбранные, нажав на кнопку или перетащив " +"мышью, вы также можете изменить порядок с помощью мыши. " + +msgid "" +"Choose the rule you want to insert. Specify either the rule you want to " +"insert immediately before, or the rule to insert immediately after. If both " +"are specified, the prior takes precedence." +msgstr "" +"Выберите правило, которое вы хотите вставить. Укажите правило до или после " +"которого вы хотите вставить новое правило. Если указаны оба, используется " +"первое." + +msgid "Choose the rule you want to remove." +msgstr "Выберите правило, которое вы хотите удалить." + +msgid "Create Firewall Group" +msgstr "Создать группу межсетевого экрана" + +msgid "Create a Firewall Group with selected ports." +msgstr "Создать группу межсетевого экрана с выбранными портами." + +msgid "" +"Create a firewall group based on a policy.\n" +"\n" +"A firewall group represents a logical firewall resource that a tenant can " +"instantiate and manage. A firewall group must be associated with one policy, " +"all other fields are optional." +msgstr "" +"Создание группы межсетевого экрана, основывающейся на политике.\n" +"\n" +"Группа межсетевого экрана представляет из себя ресурс логического " +"межсетевого экрана, который может быть создан и настроен тенантом. Групп " +"межсетевого экрана должна основываться на (одной) политике, все остальные " +"поля - опциональны." + +msgid "" +"Create a firewall policy with an ordered list of firewall rules.\n" +"\n" +"A firewall policy is an ordered collection of firewall rules. So if the " +"traffic matches the first rule, the other rules are not executed. If the " +"traffic does not match the current rule, then the next rule is executed. A " +"firewall policy has the following attributes:\n" +"\n" +"
        • Shared: A firewall policy can be shared across tenants. Thus it can also " +"be made part of an audit workflow wherein the firewall policy can be audited " +"by the relevant entity that is authorized.
        • Audited: When audited is " +"set to True, it indicates that the firewall policy has been audited. Each " +"time the firewall policy or the associated firewall rules are changed, this " +"attribute will be set to False and will have to be explicitly set to True " +"through an update operation.
        • \n" +"The name field is required, all others are optional." +msgstr "" +"Создайте политику межсетевого экрана с упорядоченной цепочкой правил.\n" +"\n" +"Политика межсетевого экрана - упорядоченная цепочка правил. Если трафик " +"соответствует критериям первого правила, к нему не применяются остальные. " +"Если трафик не соотвтествует критериям текущего правила, идёт проверка на " +"соответствие критериям следующего правила до конца цепочки. Политика " +"межсетевого экрана имеет следующие атрибуты:\n" +"\n" +"
        • Shared: Политика может быть общей между проектами. Предоставление общего " +"доступа может частью процесса аудита, когда к политикам предоставляется " +"авторизованный общий доступ.
        • Audited: При включении данной опции, " +"при каждом изменении политики/правил атрибут Audited сбрасывается в " +"состояние False и должен быть явно возвращён в состоянии True обновлением " +"политики.
        • \n" +"Имя является обязательным, остальные поля опциональны." + +msgid "" +"Create a firewall rule.\n" +"\n" +"A Firewall rule is an association of the following attributes:\n" +"\n" +"
        • IP Addresses: The addresses from/to which the traffic filtration needs " +"to be applied.
        • IP Version: The type of IP packets (IP V4/V6) that " +"needs to be filtered.
        • Protocol: Type of packets (UDP, ICMP, TCP, " +"Any) that needs to be checked.
        • Action: Action is the type of " +"filtration required, it can be Reject/Deny/Allow data packets.
        • \n" +"The protocol and action fields are required, all others are optional." +msgstr "" +"Создайте правило брандмауэра.\n" +"\n" +"Правило брандмауэра содержит следующие атрибуты:\n" +"\n" +"
        • IP-адреса: исходные или целевые адреса, для которых действует правило.
        • Версия IP: тип пакетов IP (IP V4/V6), для которых требуется " +"фильтрация.
        • Протокол: тип пакетов (UDP, ICMP, TCP, Any), для которых " +"действует правило.
        • Действие: действие правила с пакетом (отклонить, " +"сбросить, разрешить).
        • \n" +"Поля протокола и действия являются обязательными, все прочие поля - " +"необязательные." + +msgid "Create a policy with selected rules." +msgstr "Создать политику с выбранными правилами." + +msgctxt "Current status of a Firewall Group" +msgid "Created" +msgstr "Создано" + +msgid "DENY" +msgstr "DENY" + +msgctxt "Action Name of a Firewall Rule" +msgid "DENY" +msgstr "DENY" + +msgctxt "Admin state of a Firewall Group" +msgid "DOWN" +msgstr "Выключен" + +msgid "Delete Firewall Group" +msgid_plural "Delete Firewall Groups" +msgstr[0] "Удалить группу межсетевого экрана" +msgstr[1] "Удалить группу межсетевого экрана" +msgstr[2] "Удалить группы межсетевого экрана" + +msgid "Delete Policy" +msgid_plural "Delete Policies" +msgstr[0] "Удалить политику" +msgstr[1] "Удалить политики" +msgstr[2] "Удалить политики" + +msgid "Delete Rule" +msgid_plural "Delete Rules" +msgstr[0] "Удалить правило" +msgstr[1] "Удалить правила" +msgstr[2] "Удалить правила" + +msgid "Description" +msgstr "Описание" + +msgid "Description:" +msgstr "Описание:" + +msgid "Destination IP" +msgstr "IP адрес назначения" + +msgid "Destination IP Address" +msgstr "IP адрес назначения" + +msgid "Destination IP Address/Subnet" +msgstr "IP адрес/подсеть назначения" + +msgid "Destination IP address or subnet" +msgstr "IP адрес/подсеть назначения" + +msgid "Destination Port" +msgstr "Порт назначения" + +msgid "Destination Port/Port Range" +msgstr "Порт/диапазон портов назначения" + +msgid "Destination port (integer in [1, 65535] or range in a:b)" +msgstr "Порт назначения (целое число из диапазона [1-65535] или диапазон a:b) " + +msgctxt "Current status of a Firewall Group" +msgid "Down" +msgstr "Выключен" + +msgid "Edit Firewall Group" +msgstr "Редактировать группу межсетевого экрана" + +msgid "Edit FirewallGroup {{ name }}" +msgstr "Редактирование группы межсетевого экрана {{ name }}" + +msgid "Edit Policy" +msgstr "Редактировать политику" + +msgid "Edit Policy {{ name }}" +msgstr "Редактирование политики {{ name }}" + +msgid "Edit Rule" +msgstr "Редактировать правило" + +msgid "Edit Rule {{ name }}" +msgstr "Редактирование правила {{ name }}" + +msgid "Egress Policy" +msgstr "Политика исходящего трафика" + +msgid "Egress Policy ID" +msgstr "ID политики исходящего трафика" + +msgid "Enabled" +msgstr "Включен" + +msgctxt "Current status of a Firewall Group" +msgid "Error" +msgstr "Ошибка" + +#, python-format +msgid "Failed to add the port(s) to the firewall group %(name)s: %(reason)s" +msgstr "" +"Не удалось добавить порты в группу межсетевого экрана %(name)s: %(reason)s" + +#, python-format +msgid "Failed to insert rule to policy %(name)s: %(reason)s" +msgstr "Не удалось добавить правило в политику %(name)s: %(reason)s" + +#, python-format +msgid "Failed to remove rule from policy %(name)s: %(reason)s" +msgstr "Не удалось удалить правило из политики %(name)s: %(reason)s" + +#, python-format +msgid "" +"Failed to remove the port(s) from the firewall group %(name)s: %(reason)s" +msgstr "" +"Не удалось удалить порты из группы межсетевого экрана %(name)s: %(reason)s" + +#, python-format +msgid "Failed to retrieve available rules: %s" +msgstr "Не удалось получить доступные правила: %s" + +#, python-format +msgid "Failed to retrieve current rules in policy %(name)s: %(reason)s" +msgstr "Не удалось получить текущие правила политики %(name)s: %(reason)s" + +msgid "Failed to retrieve port list of the firewall group." +msgstr "Не удалось получить список портов группы межсетевого экрана." + +#, python-format +msgid "Failed to update firewall group %(name)s: %(reason)s" +msgstr "Не удалось обновить группу межсетевого экрана %(name)s: %(reason)s" + +#, python-format +msgid "Failed to update policy %(name)s: %(reason)s" +msgstr "Не удалось обновить политику %(name)s: %(reason)s" + +#, python-format +msgid "Failed to update rule %(name)s: %(reason)s" +msgstr "Не удалось обновить правило %(name)s: %(reason)s" + +msgid "Firewall Groups" +msgstr "Группы межсетевого экрана" + +msgid "Firewall Policies" +msgstr "Политики межсетевого экрана" + +msgid "Firewall Rules" +msgstr "Правила межсетевого экрана" + +#, python-format +msgid "Firewall group %s was successfully updated." +msgstr "Группа %s межсетевого экрана была успешно обновлена." + +msgid "FirewallGroup" +msgstr "Группа межсетевого экрана" + +msgid "ICMP" +msgstr "ICMP" + +msgid "ID" +msgstr "ID" + +msgid "IP Version" +msgstr "Версия IP" + +msgid "IP Version for Firewall Rule" +msgstr "Версия протокола IP для правила межсетевого экрана" + +msgctxt "Current status of a Firewall Group" +msgid "Inactive" +msgstr "Неактивно" + +msgid "Ingress Policy" +msgstr "Политика входящего трафика" + +msgid "Ingress Policy ID" +msgstr "ID политики входящего трафика" + +msgid "Insert Rule" +msgstr "Вставить правило" + +msgid "Insert Rule to Policy" +msgstr "Вставить правило в политику" + +msgid "Label" +msgstr "Метка" + +msgid "Name" +msgstr "Имя" + +msgid "No options available" +msgstr "Нет доступных опций" + +msgid "None" +msgstr "Нет" + +msgctxt "Current status of a Firewall Group" +msgid "Pending Create" +msgstr "Ожидает создания" + +msgctxt "Current status of a Firewall Group" +msgid "Pending Delete" +msgstr "Ожидает удаления" + +msgctxt "Current status of a Firewall Group" +msgid "Pending Update" +msgstr "Ожидает обновления" + +msgid "Policies" +msgstr "Политики" + +msgid "Policy" +msgstr "Политика" + +#, python-format +msgid "Policy %s was successfully updated." +msgstr "Политика %s была успешно обновлена." + +msgid "Ports" +msgstr "Порты" + +msgid "Project ID" +msgstr " ID проекта" + +msgid "Protocol" +msgstr "Протокол" + +msgid "Protocol for the firewall rule" +msgstr "Протокол для правила межсетевого экрана." + +msgid "REJECT" +msgstr "REJECT" + +msgctxt "Action Name of a Firewall Rule" +msgid "REJECT" +msgstr "REJECT" + +msgid "Remove Port" +msgstr "Удалить порт" + +msgid "Remove Port from Firewall Group" +msgstr "Удалить порт из группы межсетевого экрана" + +msgid "Remove Rule" +msgstr "Удалить правило" + +msgid "Remove Rule from Policy" +msgstr "Удалить правило из политики" + +msgid "Remove port from FirewallGroup {{ name }}" +msgstr "Удалить порт из группы межсетевого экрана {{ name }}" + +#, python-format +msgid "Removed the port(s) from the firewall group %s successfully." +msgstr "Порты успешно удалены из группы %s межсетевого экрана." + +msgid "Rule" +msgstr "Правило" + +#, python-format +msgid "Rule %(rule)s was successfully inserted to policy %(policy)s." +msgstr "Правило %(rule)s было успешно добавлено в политику %(policy)s." + +#, python-format +msgid "Rule %(rule)s was successfully removed from policy %(policy)s." +msgstr "Правило %(rule)s было успешно удалено из политики %(policy)s." + +#, python-format +msgid "Rule %s was successfully updated." +msgstr "Правило %s было успешно обновлено." + +msgid "Rules" +msgstr "Правила" + +msgid "Save Changes" +msgstr "Сохранить изменения" + +msgid "Scheduled deletion of Firewall Group" +msgid_plural "Scheduled deletion of Firewall Groups" +msgstr[0] "Запланировано удаление группы межсетевого экрана" +msgstr[1] "Запланировано удаление группы межсетевого экрана" +msgstr[2] "Запланировано удаление групп межсетевого экрана" + +msgid "Scheduled deletion of Policy" +msgid_plural "Scheduled deletion of Policies" +msgstr[0] "Запланированное удаление политики" +msgstr[1] "Запланированное удаление политик" +msgstr[2] "Запланировано удаление политик" + +msgid "Scheduled deletion of Rule" +msgid_plural "Scheduled deletion of Rules" +msgstr[0] "Запланированное удаление правила" +msgstr[1] "Запланированное удаление правил" +msgstr[2] "Запланировано удаление правил" + +msgid "Select a Policy" +msgstr "Выберите политику" + +msgid "Select ports for your firewall group." +msgstr "Выберите порты для вашей группы сетевого экрана." + +msgid "Select rules for your policy." +msgstr "Выбрать правила для вашей политики." + +msgid "Selected Ports" +msgstr "Выбранные порты" + +msgid "Selected Rules" +msgstr "Выбранные правила" + +msgid "Shared" +msgstr "Общая" + +msgid "Source IP" +msgstr "IP-адрес источника" + +msgid "Source IP Address" +msgstr "IP адрес источника" + +msgid "Source IP Address/Subnet" +msgstr "IP адрес/подсеть источника" + +msgid "Source IP address or subnet" +msgstr "IP адрес/подсеть источника" + +msgid "Source Port" +msgstr "Порт отправления" + +msgid "Source Port/Port Range" +msgstr "Порт/диапазон портов отправления" + +msgid "Source port (integer in [1, 65535] or range in a:b)" +msgstr "" +"Порт отправления (целое число из диапазона [1-65535] или диапазон a:b) " + +msgid "" +"Source/Destination Network Address and IP version are inconsistent. Please " +"make them consistent." +msgstr "" +"Адрес источника/получателя и версия протокола несовместимы. Пожалуйста " +"исправьте." + +msgid "Status" +msgstr "Статус" + +msgid "TCP" +msgstr "TCP" + +msgid "UDP" +msgstr "UDP" + +msgctxt "Admin state of a Firewall Group" +msgid "UP" +msgstr "Включен" + +#, python-format +msgid "Unable to add Firewall Group \"%s\"." +msgstr "Не удалось добавить группу межсетевого экрана \"%s\"." + +#, python-format +msgid "Unable to add Policy \"%s\"." +msgstr "Не удалось добавить политику \"%s\"." + +#, python-format +msgid "Unable to add Rule \"%s\"." +msgstr "Не удалось добавить правило \"%s\"." + +#, python-format +msgid "Unable to delete firewall group. %s" +msgstr "Не удалось удалить группу межсетевого экрана. %s" + +#, python-format +msgid "Unable to delete policy. %s" +msgstr "Не удалось удалить политику. %s" + +#, python-format +msgid "Unable to delete rule. %s" +msgstr "Не удалось удалить правило. %s" + +msgid "Unable to retrieve firewall group details." +msgstr "Не удалось получить детали группы межсетевого экрана." + +msgid "Unable to retrieve firewall group list." +msgstr "Не удалось получить список групп межсетевого экрана." + +msgid "Unable to retrieve firewallgroup details." +msgstr "Не удалось получить информацию о группе межсетевого экрана." + +msgid "Unable to retrieve policies list." +msgstr "Не удалось получить список политик." + +msgid "Unable to retrieve policy details." +msgstr "Не удалось получить информацию о политике." + +#, python-format +msgid "Unable to retrieve policy list (%s)." +msgstr "Не удалось получить список политик (%s)." + +msgid "Unable to retrieve policy list." +msgstr "Не удалось получить список политик." + +#, python-format +msgid "Unable to retrieve ports (%s)." +msgstr "Не удалось получить порты (%s)." + +msgid "Unable to retrieve rule details." +msgstr "Не удалось получить информацию о правиле." + +#, python-format +msgid "Unable to retrieve rules (%s)." +msgstr "Не удалось получить правила (%s).." + +msgid "Unable to retrieve rules list." +msgstr "Не удалось получить список правил." + +msgid "Value" +msgstr "Значение" + +msgid "You may add ports to firewall group here." +msgstr " Здесь вы можете добавить порты к группе межсетевого экрана." + +msgid "You may remove ports from firewall group here." +msgstr " Здесь вы можете удалить порты из группы межсетевого экрана." + +msgid "You may update firewall group details here." +msgstr " Здесь вы можете изменить конфигурацию группы межсетевого экрана." + +msgid "" +"You may update policy details here. Use 'Insert Rule' or 'Remove Rule' links " +"instead to insert or remove a rule." +msgstr "" +"Здесь вы можете изменить конфигурацию политики. Для добавления или удаления " +"правил используйте соответствующие ссылки." + +msgid "You may update rule details here." +msgstr " Здесь вы можете изменить конфигурацию правила." diff --git a/neutron_fwaas_dashboard/locale/zh_CN/LC_MESSAGES/django.po b/neutron_fwaas_dashboard/locale/zh_CN/LC_MESSAGES/django.po new file mode 100644 index 0000000..b9fe4d0 --- /dev/null +++ b/neutron_fwaas_dashboard/locale/zh_CN/LC_MESSAGES/django.po @@ -0,0 +1,624 @@ +# BillXiang , 2016. #zanata +# Linda , 2016. #zanata +# Tony , 2016. #zanata +# liuyanfu , 2016. #zanata +# sunanchen , 2016. #zanata +# TigerFang , 2017. #zanata +# Tony , 2017. #zanata +# blkart , 2017. #zanata +# vuuv , 2017. #zanata +# zhonghuali , 2017. #zanata +# Tony , 2018. #zanata +# Tony , 2019. #zanata +msgid "" +msgstr "" +"Project-Id-Version: neutron-fwaas-dashboard VERSION\n" +"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" +"POT-Creation-Date: 2019-03-19 10:41+0000\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"PO-Revision-Date: 2019-03-21 12:54+0000\n" +"Last-Translator: Tony \n" +"Language-Team: Chinese (China)\n" +"Language: zh_CN\n" +"X-Generator: Zanata 4.3.3\n" +"Plural-Forms: nplurals=1; plural=0\n" + +msgid "-" +msgstr "_" + +msgid "ALLOW" +msgstr "允许" + +msgctxt "Action Name of a Firewall Rule" +msgid "ALLOW" +msgstr "允许" + +msgid "ANY" +msgstr "任意选项" + +msgid "Action" +msgstr "操作" + +msgid "Action for the firewall rule" +msgstr "针对防火墙规则的操作" + +msgctxt "Current status of a Firewall Group" +msgid "Active" +msgstr "运行中" + +msgid "Add" +msgstr "添加" + +msgid "Add Firewall Group" +msgstr "添加防火墙组" + +msgid "Add New Firewall Group" +msgstr "添加新的防火墙组" + +msgid "Add New Policy" +msgstr "添加新的策略" + +msgid "Add New Rule" +msgstr "添加新的规则" + +msgid "Add Policy" +msgstr "添加策略" + +msgid "Add Port" +msgstr "添加端口" + +msgid "Add Port to Firewall Group" +msgstr "为防火墙组添加端口" + +msgid "Add Rule" +msgstr "增加规则" + +msgid "Add port to Firewall Group {{ name }}" +msgstr "添加端口到防火墙组 {{ name }}" + +#, python-format +msgid "Added Firewall Group\"%s\"." +msgstr "添加防火墙组 \"%s\"。" + +#, python-format +msgid "Added Policy \"%s\"." +msgstr "添加策略 “%s”。" + +#, python-format +msgid "Added Rule \"%s\"." +msgstr "已添加规则 “%s”。" + +#, python-format +msgid "Added the port(s) to the firewall group %s successfully." +msgstr "为防火墙组 %s 添加端口成功。" + +msgid "Admin State" +msgstr "管理员状态" + +msgid "Admin State Up" +msgstr "管理状态为启动" + +msgid "After" +msgstr "晚于" + +msgid "Audited" +msgstr "已审计" + +msgid "Available Ports" +msgstr "可用的端口" + +msgid "Available Rules" +msgstr "可用的规则" + +msgid "Before" +msgstr "早于" + +msgid "Choose port(s) from Available Ports. " +msgstr "从可用端口列表中选择。" + +msgid "" +"Choose rule(s) from Available Rules to Selected Rule by push button or drag " +"and drop, you may change their order by drag and drop as well." +msgstr "" +"通过按下按钮或者拖放从可用策略中选择一个(或多个)添加至选定策略,您也可以利" +"用拖放改变它们的顺序。" + +msgid "" +"Choose the rule you want to insert. Specify either the rule you want to " +"insert immediately before, or the rule to insert immediately after. If both " +"are specified, the prior takes precedence." +msgstr "" +"选择希望插入的规则。 指定在某条规则之前插入,或在某条规则之后插入。如果二者均" +"被指定,前面指定的优先。" + +msgid "Choose the rule you want to remove." +msgstr "选择希望移除的规则。" + +msgid "Create Firewall Group" +msgstr "创建防火墙组" + +msgid "Create a Firewall Group with selected ports." +msgstr "创建带有所选端口的路由器组。" + +msgid "" +"Create a firewall group based on a policy.\n" +"\n" +"A firewall group represents a logical firewall resource that a tenant can " +"instantiate and manage. A firewall group must be associated with one policy, " +"all other fields are optional." +msgstr "" +"根据策略创建防火墙组。\n" +"\n" +"防火墙组代表租户可以实例化和管理的逻辑防火墙资源。一个防火墙组必须与一个策略" +"相关联,所有其它字段是可选字段。" + +msgid "" +"Create a firewall policy with an ordered list of firewall rules.\n" +"\n" +"A firewall policy is an ordered collection of firewall rules. So if the " +"traffic matches the first rule, the other rules are not executed. If the " +"traffic does not match the current rule, then the next rule is executed. A " +"firewall policy has the following attributes:\n" +"\n" +"
        • Shared: A firewall policy can be shared across tenants. Thus it can also " +"be made part of an audit workflow wherein the firewall policy can be audited " +"by the relevant entity that is authorized.
        • Audited: When audited is " +"set to True, it indicates that the firewall policy has been audited. Each " +"time the firewall policy or the associated firewall rules are changed, this " +"attribute will be set to False and will have to be explicitly set to True " +"through an update operation.
        • \n" +"The name field is required, all others are optional." +msgstr "" +"使用有序防火墙规则列表创建防火墙策略。\n" +"\n" +"防火墙策略是防火墙规则的有序集合。所以,如果流量满足第一个规则,那么其它规则" +"将不会执行。如果流量与当前规则不匹配,那么将执行下一个规则。防火墙策略具有以" +"下属性:\n" +"\n" +"
        • Shared:防火墙规则可以在租户间共享。因此,它可以作为审计工作流的一部分," +"已被授权的相关实体可对该防火墙策略进行审计。
        • Audited:当 Audited 设置" +"为 True 时,表示已审计该防火墙策略。每次防火墙策略或关联防火墙规则发生变化" +"时,此属性将会设置为 False,并且必须通过更新操作显式设置为 True。
        • \n" +"名称字段是必填字段,所有其他字段是可选字段。" + +msgid "" +"Create a firewall rule.\n" +"\n" +"A Firewall rule is an association of the following attributes:\n" +"\n" +"
        • IP Addresses: The addresses from/to which the traffic filtration needs " +"to be applied.
        • IP Version: The type of IP packets (IP V4/V6) that " +"needs to be filtered.
        • Protocol: Type of packets (UDP, ICMP, TCP, " +"Any) that needs to be checked.
        • Action: Action is the type of " +"filtration required, it can be Reject/Deny/Allow data packets.
        • \n" +"The protocol and action fields are required, all others are optional." +msgstr "" +"创建防火墙规则。\n" +"\n" +"防火墙规则由以下属性关联形成:\n" +"\n" +"
        • IP 地址:需要应用流量过滤的来源/目标地址。
        • IP版本:需要过滤的 IP " +"包类型 (IP V4/V6)。
        • 协议:需要检查的数据包类型(UDP、ICMP、TCP 和 " +"Any)。
        • 操作:操作是所需过滤类型,它可为拒绝/放弃/允许数据包。
        • \n" +"协议字段和操作字段是必填字段,所有其他字段为可选字段。" + +msgid "Create a policy with selected rules." +msgstr "使用所选规则创建策略。" + +msgctxt "Current status of a Firewall Group" +msgid "Created" +msgstr "已创建" + +msgid "DENY" +msgstr "拒绝" + +msgctxt "Action Name of a Firewall Rule" +msgid "DENY" +msgstr "丢弃" + +msgctxt "Admin state of a Firewall Group" +msgid "DOWN" +msgstr "关闭" + +msgid "Delete Firewall Group" +msgid_plural "Delete Firewall Groups" +msgstr[0] "删除防火墙组" + +msgid "Delete Policy" +msgid_plural "Delete Policies" +msgstr[0] "删除策略" + +msgid "Delete Rule" +msgid_plural "Delete Rules" +msgstr[0] "删除规则" + +msgid "Description" +msgstr "描述" + +msgid "Description:" +msgstr "描述:" + +msgid "Destination IP" +msgstr "目标 IP" + +msgid "Destination IP Address" +msgstr "目的 IP 地址" + +msgid "Destination IP Address/Subnet" +msgstr "目标 IP 地址/子网" + +msgid "Destination IP address or subnet" +msgstr "目标 IP 地址或子网" + +msgid "Destination Port" +msgstr "目标端口" + +msgid "Destination Port/Port Range" +msgstr "目标端口/端口范围" + +msgid "Destination port (integer in [1, 65535] or range in a:b)" +msgstr "目标端口([1, 65535] 区间内的整数或者以 a:b 形式表示的范围)" + +msgctxt "Current status of a Firewall Group" +msgid "Down" +msgstr "关闭" + +msgid "Edit Firewall Group" +msgstr "编辑防火墙组" + +msgid "Edit FirewallGroup {{ name }}" +msgstr "编辑防火墙组 {{ name }}" + +msgid "Edit Policy" +msgstr "编辑策略" + +msgid "Edit Policy {{ name }}" +msgstr "编辑策略 {{ name }}" + +msgid "Edit Rule" +msgstr "编辑规则" + +msgid "Edit Rule {{ name }}" +msgstr "编辑规则 {{ name }}" + +msgid "Egress Policy" +msgstr "出口策略" + +msgid "Egress Policy ID" +msgstr "出口策略 ID" + +msgid "Enabled" +msgstr "已启用" + +msgctxt "Current status of a Firewall Group" +msgid "Error" +msgstr "错误" + +#, python-format +msgid "Failed to add the port(s) to the firewall group %(name)s: %(reason)s" +msgstr "为防火墙组 %(name)s 添加端口失败 : %(reason)s" + +#, python-format +msgid "Failed to insert rule to policy %(name)s: %(reason)s" +msgstr "无法将规则插入至策略 %(name)s:%(reason)s" + +#, python-format +msgid "Failed to remove rule from policy %(name)s: %(reason)s" +msgstr "无法从策略 %(name)s 中移除规则:%(reason)s" + +#, python-format +msgid "" +"Failed to remove the port(s) from the firewall group %(name)s: %(reason)s" +msgstr "从防火墙组 %(name)s 中删除端口失败 : %(reason)s" + +#, python-format +msgid "Failed to retrieve available rules: %s" +msgstr "无法检索可用的规则:%s" + +#, python-format +msgid "Failed to retrieve current rules in policy %(name)s: %(reason)s" +msgstr "无法检索策略 %(name)s 中的当前规则:%(reason)s" + +msgid "Failed to retrieve port list of the firewall group." +msgstr "无法获取防火墙组的端口列表。" + +#, python-format +msgid "Failed to update firewall group %(name)s: %(reason)s" +msgstr "更新防火墙组 %(name)s 失败 : %(reason)s" + +#, python-format +msgid "Failed to update policy %(name)s: %(reason)s" +msgstr "无法更新策略 %(name)s:%(reason)s" + +#, python-format +msgid "Failed to update rule %(name)s: %(reason)s" +msgstr "无法更新规则 %(name)s:%(reason)s" + +msgid "Firewall Groups" +msgstr "防火墙组" + +msgid "Firewall Policies" +msgstr "防火墙策略" + +msgid "Firewall Rules" +msgstr "防火墙规则" + +#, python-format +msgid "Firewall group %s was successfully updated." +msgstr "防火墙组 %s 被成功更新。" + +msgid "FirewallGroup" +msgstr "防火墙组" + +msgid "ICMP" +msgstr "ICMP" + +msgid "ID" +msgstr "ID" + +msgid "IP Version" +msgstr "IP版本" + +msgid "IP Version for Firewall Rule" +msgstr "防火墙规则的 IP 版本" + +msgctxt "Current status of a Firewall Group" +msgid "Inactive" +msgstr "不活跃" + +msgid "Ingress Policy" +msgstr "入口策略" + +msgid "Ingress Policy ID" +msgstr "入口策略 ID" + +msgid "Insert Rule" +msgstr "插入规则" + +msgid "Insert Rule to Policy" +msgstr "向策略中添加规则" + +msgid "Label" +msgstr "标签" + +msgid "Name" +msgstr "名称" + +msgid "No options available" +msgstr "无可用选项" + +msgid "None" +msgstr "无" + +msgctxt "Current status of a Firewall Group" +msgid "Pending Create" +msgstr "等待创建" + +msgctxt "Current status of a Firewall Group" +msgid "Pending Delete" +msgstr "等待删除" + +msgctxt "Current status of a Firewall Group" +msgid "Pending Update" +msgstr "等待更新" + +msgid "Policies" +msgstr "策略" + +msgid "Policy" +msgstr "策略" + +#, python-format +msgid "Policy %s was successfully updated." +msgstr "已成功更新策略 %s。" + +msgid "Ports" +msgstr "端口" + +msgid "Project ID" +msgstr "项目 ID" + +msgid "Protocol" +msgstr "协议" + +msgid "Protocol for the firewall rule" +msgstr "防火墙规则的协议" + +msgid "REJECT" +msgstr "拒绝" + +msgctxt "Action Name of a Firewall Rule" +msgid "REJECT" +msgstr "拒绝" + +msgid "Remove Port" +msgstr "删除端口" + +msgid "Remove Port from Firewall Group" +msgstr "为防火墙组删除端口" + +msgid "Remove Rule" +msgstr "移除规则" + +msgid "Remove Rule from Policy" +msgstr "从策略中移除规则" + +msgid "Remove port from FirewallGroup {{ name }}" +msgstr "从防火墙组中删除端口 {{ name }}" + +#, python-format +msgid "Removed the port(s) from the firewall group %s successfully." +msgstr "从防火墙组 %s 中删除端口成功。" + +msgid "Rule" +msgstr "规则" + +#, python-format +msgid "Rule %(rule)s was successfully inserted to policy %(policy)s." +msgstr "规则 %(rule)s 被成功插入至策略 %(policy)s。" + +#, python-format +msgid "Rule %(rule)s was successfully removed from policy %(policy)s." +msgstr "规则 %(rule)s 已从策略 %(policy)s 中成功移除。" + +#, python-format +msgid "Rule %s was successfully updated." +msgstr "已成功更新规则 %s。" + +msgid "Rules" +msgstr "规则" + +msgid "Save Changes" +msgstr "保存变更" + +msgid "Scheduled deletion of Firewall Group" +msgid_plural "Scheduled deletion of Firewall Groups" +msgstr[0] "已调度的删除防火期组" + +msgid "Scheduled deletion of Policy" +msgid_plural "Scheduled deletion of Policies" +msgstr[0] "已安排删除策略" + +msgid "Scheduled deletion of Rule" +msgid_plural "Scheduled deletion of Rules" +msgstr[0] "已安排删除规则" + +msgid "Select a Policy" +msgstr "选择策略" + +msgid "Select ports for your firewall group." +msgstr "为防火墙组选择端口。" + +msgid "Select rules for your policy." +msgstr "为您的策略选择规则。" + +msgid "Selected Ports" +msgstr "选择的端口" + +msgid "Selected Rules" +msgstr "选定的规则" + +msgid "Shared" +msgstr "共享" + +msgid "Source IP" +msgstr "源 IP" + +msgid "Source IP Address" +msgstr "源 IP 地址" + +msgid "Source IP Address/Subnet" +msgstr "源 IP 地址/子网" + +msgid "Source IP address or subnet" +msgstr "源 IP 地址或子网" + +msgid "Source Port" +msgstr "源端口" + +msgid "Source Port/Port Range" +msgstr "源端口/端口范围" + +msgid "Source port (integer in [1, 65535] or range in a:b)" +msgstr "源端口([1, 65535] 区间内的整数或者以 a:b 形式表示的范围)" + +msgid "" +"Source/Destination Network Address and IP version are inconsistent. Please " +"make them consistent." +msgstr "源/目标网络地址和 IP 版本不一致。请确保它们一致。" + +msgid "Status" +msgstr "状态" + +msgid "TCP" +msgstr "TCP" + +msgid "UDP" +msgstr "UDP" + +msgctxt "Admin state of a Firewall Group" +msgid "UP" +msgstr "启动" + +#, python-format +msgid "Unable to add Firewall Group \"%s\"." +msgstr "无法添加防火墙组 \"%s\"。" + +#, python-format +msgid "Unable to add Policy \"%s\"." +msgstr "无法添加策略 “%s”。" + +#, python-format +msgid "Unable to add Rule \"%s\"." +msgstr "无法添加规则 “%s”。" + +#, python-format +msgid "Unable to delete firewall group. %s" +msgstr "无法删除防火墙组 %s" + +#, python-format +msgid "Unable to delete policy. %s" +msgstr "无法删除策略 %s" + +#, python-format +msgid "Unable to delete rule. %s" +msgstr "无法删除规则 %s" + +msgid "Unable to retrieve firewall group details." +msgstr "无法获取防火墙组详情。" + +msgid "Unable to retrieve firewall group list." +msgstr "无法获取防火墙组列表。" + +msgid "Unable to retrieve firewallgroup details." +msgstr "无法获取防火墙组详情。" + +msgid "Unable to retrieve policies list." +msgstr "无法检索策略列表。" + +msgid "Unable to retrieve policy details." +msgstr "无法检索策略详情。" + +#, python-format +msgid "Unable to retrieve policy list (%s)." +msgstr "无法检索策略列表 (%s)。" + +msgid "Unable to retrieve policy list." +msgstr "无法检索策略列表。" + +#, python-format +msgid "Unable to retrieve ports (%s)." +msgstr "无法获取端口 (%s)。" + +msgid "Unable to retrieve rule details." +msgstr "无法检索规则详情。" + +#, python-format +msgid "Unable to retrieve rules (%s)." +msgstr "无法获取规则 (%s)。" + +msgid "Unable to retrieve rules list." +msgstr "无法检索规则列表。" + +msgid "Value" +msgstr "值" + +msgid "You may add ports to firewall group here." +msgstr "您可以在这里为防火墙组添加端口。" + +msgid "You may remove ports from firewall group here." +msgstr "您可以在这里为防火墙组删除端口。" + +msgid "You may update firewall group details here." +msgstr "您可以在此处更新防火墙组的详情。" + +msgid "" +"You may update policy details here. Use 'Insert Rule' or 'Remove Rule' links " +"instead to insert or remove a rule." +msgstr "" +"您可以在此处更新策略详情。使用'插入规则'或'移除规则'链接来插入或删除规则。" + +msgid "You may update rule details here." +msgstr "您可以在此处更新规则详情。" diff --git a/neutron_fwaas_dashboard/static/neutron_fwaas_dashboard/js/horizon.firewalls_v2.js b/neutron_fwaas_dashboard/static/neutron_fwaas_dashboard/js/horizon.firewalls_v2.js new file mode 100644 index 0000000..999d219 --- /dev/null +++ b/neutron_fwaas_dashboard/static/neutron_fwaas_dashboard/js/horizon.firewalls_v2.js @@ -0,0 +1,21 @@ +/** + * 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. + */ + +horizon.firewalls_v2 = { + workflow_init: function() { + // Initialise the drag and drop rule list + horizon.lists.generate_html("port"); + horizon.lists.generate_html("rule"); + } +}; diff --git a/neutron_fwaas_dashboard/static/neutron_fwaas_dashboard/scss/firewalls.scss b/neutron_fwaas_dashboard/static/neutron_fwaas_dashboard/scss/firewalls.scss new file mode 100644 index 0000000..0eb6cbf --- /dev/null +++ b/neutron_fwaas_dashboard/static/neutron_fwaas_dashboard/scss/firewalls.scss @@ -0,0 +1,75 @@ +@import "/dashboard/scss/components/resource_topology"; + +.sort-container { + #selected_port { + @include common_box_list_selected("rule"); + } + /* FWaaS v2 */ + #selected_port { + @include common_box_list_selected("port"); + } +} + +// Table like styling of boostrap grid options in a bootstrap-dropdown +// compatible with the horizon dropdown javascript +.dropdown-table { + @media only screen and (min-width: 768px) { + min-width: $modal-md - $grid-gutter-width; + } + + margin-bottom: $line-height-computed; + background-color: $body-bg; + padding: 10px; + + .dropdown-thead { + margin: 0px; + + .col-xs-12 { + padding: 0px; + } + + .dropdown-tr { + .dropdown-th { + vertical-align: bottom; + border-bottom: 1px solid $table-border-color; + border-top: 0; + + display: table-cell; + vertical-align: inherit; + font-weight: bold; + text-align: -internal-center; + + color: $dropdown-header-color; + } + } + } + + .dropdown-tr { + margin: 0px; + padding: 0px; + + .col-xs-12 { + padding: 0px; + margin: 0px; + } + + .row { + margin: 0px; + padding: 0px; + } + + .dropdown-th, .dropdown-td { + padding: $table-cell-padding; + line-height: $line-height-base; + vertical-align: top; + border-top: 1px solid $table-border-color; + color: $dropdown-link-color; + } + } + + .empty-options { + text-align: center; + vertical-align: center; + font-style: italic; + } +} \ No newline at end of file diff --git a/neutron_fwaas_dashboard/test/__init__.py b/neutron_fwaas_dashboard/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/neutron_fwaas_dashboard/test/api_tests/__init__.py b/neutron_fwaas_dashboard/test/api_tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/neutron_fwaas_dashboard/test/api_tests/test_fwaas_v2.py b/neutron_fwaas_dashboard/test/api_tests/test_fwaas_v2.py new file mode 100644 index 0000000..d857102 --- /dev/null +++ b/neutron_fwaas_dashboard/test/api_tests/test_fwaas_v2.py @@ -0,0 +1,769 @@ +# Copyright 2017, Juniper Networks. +# +# 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 unittest import mock + +from neutronclient.v2_0.client import Client as neutronclient +import openstack_dashboard.api.nova as nova + +from openstack_dashboard.test import helpers + +from neutron_fwaas_dashboard.api import fwaas_v2 as api_fwaas_v2 +from neutron_fwaas_dashboard.test import helpers as test + + +class FwaasV2ApiTests(test.APITestCase): + + @helpers.create_mocks({nova: ('server_list',)}) + def test_get_servers(self): + fields = ['id', 'name'] + + mock_servers = { + '916562da-fa95-4ae1-8bea-0b45f2f8297a': self._mock_server( + id='916562da-fa95-4ae1-8bea-0b45f2f8297a', + name='mock-server-1' + ), + '7038e456-3067-493a-8f2b-69bc26acbccf': self._mock_server( + id='7038e456-3067-493a-8f2b-69bc26acbccf', + name='mock-server-2' + ), + '23f683e5-8536-4e5a-806b-0382b02743dc': self._mock_server( + id='23f683e5-8536-4e5a-806b-0382b02743dc', + name='mock-server-3' + ) + } + + mock_server_ids = sorted(mock_servers.keys()) + + self.mock_server_list.return_value = [list(mock_servers.values())] + + servers = api_fwaas_v2.get_servers(self.request) + + server_ids = sorted(servers.keys()) + + self.assertEqual(server_ids, mock_server_ids) + for key in mock_server_ids: + expected_server = mock_servers[key] + server = servers[key] + self._assert_subobject(expected_server, server, fields) + + def _assert_subobject(self, child, parent, fields): + for field in fields: + self.assertEqual( + getattr(child, field), + getattr(parent, field) + ) + + def _mock_server(self, **kwargs): + server = nova.Server({}, self.request) + for key, val in kwargs.items(): + setattr(server, key, val) + return server + + @helpers.create_mocks({neutronclient: ('list_networks',)}) + def test_get_networks(self): + fields = ['name', 'id'] + + mock_networks = { + '64e8c993-1c99-40fb-a8bc-42d3fd487a97': { + 'name': 'mock-network-1', + 'id': '64e8c993-1c99-40fb-a8bc-42d3fd487a97' + }, + 'f1bd4bb5-2bf3-4e0e-9c8d-9a1a500eaece': { + 'name': 'mock-network-2', + 'id': 'f1bd4bb5-2bf3-4e0e-9c8d-9a1a500eaece' + }, + '74173cf1-461e-4fd0-881e-2a0cc4a94e14': { + 'name': 'mock-network-3', + 'id': '74173cf1-461e-4fd0-881e-2a0cc4a94e14' + } + } + mock_network_ids = sorted(mock_networks.keys()) + + self.mock_list_networks.return_value = { + 'networks': list(mock_networks.values()) + } + + network_names = api_fwaas_v2.get_network_names(self.request) + + self.mock_list_networks.assert_called_once_with(fields=fields) + + network_ids = sorted(network_names.keys()) + + self.assertEqual(network_ids, mock_network_ids) + + for key in mock_network_ids: + self._assert_api_dict( + network_names[key]._apidict, + mock_networks[key], + fields + ) + + @helpers.create_mocks({neutronclient: ('list_routers',)}) + def test_get_router_names(self): + fields = ['name', 'id'] + + mock_routers = { + '9d143b82-bd74-4ccf-81ba-9b7e02f3f7b2': { + 'name': 'mock-router-1', + 'id': '9d143b82-bd74-4ccf-81ba-9b7e02f3f7b2' + }, + '84d72522-1c26-4d28-83ed-b8653ac5d38c': { + 'name': 'mock-router-2', + 'id': '84d72522-1c26-4d28-83ed-b8653ac5d38c' + }, + '2149de19-840a-4b41-8a44-4755ce8a881b': { + 'name': 'mock-router-3', + 'id': '2149de19-840a-4b41-8a44-4755ce8a881b' + } + } + mock_router_ids = sorted(mock_routers.keys()) + + # Mock API call + self.mock_list_routers.return_value = { + 'routers': list(mock_routers.values()) + } + # call results + router_names = api_fwaas_v2.get_router_names(self.request) + + # Check that the correct filters were applied for the API call + self.mock_list_routers.assert_called_once_with(fields=fields) + # Ensure that exactly the expected mock data ids have been retrieved + router_ids = sorted(router_names.keys()) + self.assertEqual(router_ids, mock_router_ids) + + # Check that the returned values correspond to the (mocked) API data + for key in mock_router_ids: + # Note that _apidict is being checked + self._assert_api_dict( + router_names[key]._apidict, + mock_routers[key], + fields + ) + + def _assert_api_dict(self, actual, expected, fields): + # Ensure exactly the required fields have been retrieved + actual_fields = sorted(actual.keys()) + self.assertEqual(actual_fields, sorted(fields)) + + # Ensure expected datum was returned in each field + for field in fields: + self.assertEqual(actual[field], expected[field]) + + @helpers.create_mocks({neutronclient: ('create_fwaas_firewall_rule',)}) + def test_rule_create(self): + rule1 = self.fw_rules_v2.first() + rule1_dict = self.api_fw_rules_v2.first() + form_data = {'name': rule1.name, + 'description': rule1.description, + 'protocol': rule1.protocol, + 'action': rule1.action, + 'source_ip_address': rule1.source_ip_address, + 'source_port': rule1.source_port, + 'destination_ip_address': rule1.destination_ip_address, + 'destination_port': rule1.destination_port, + 'shared': rule1.shared, + 'enabled': rule1.enabled + } + form_dict = {'firewall_rule': form_data} + ret_dict = {'firewall_rule': rule1_dict} + self.mock_create_fwaas_firewall_rule.return_value = ret_dict + + ret_val = api_fwaas_v2.rule_create(self.request, **form_data) + self._assert_rule_return_value(ret_val, rule1) + + self.mock_create_fwaas_firewall_rule.assert_called_once_with(form_dict) + + def _assert_rule_return_value(self, ret_val, exp_rule): + self.assertIsInstance(ret_val, api_fwaas_v2.Rule) + self.assertEqual(exp_rule.name, ret_val.name) + self.assertTrue(ret_val.id) + + @helpers.create_mocks({neutronclient: ('list_fwaas_firewall_rules',)}) + def test_rule_list(self): + exp_rules = self.fw_rules_v2.list() + api_rules = {'firewall_rules': self.api_fw_rules_v2.list()} + + self.mock_list_fwaas_firewall_rules.return_value = api_rules + + ret_val = api_fwaas_v2.rule_list(self.request) + for (v, d) in zip(ret_val, exp_rules): + self._assert_rule_return_value(v, d) + self.mock_list_fwaas_firewall_rules.assert_called_once_with() + + @helpers.create_mocks({neutronclient: ('list_fwaas_firewall_rules',)}) + def test_rule_list_for_tenant(self): + tenant_id = self.request.user.project_id + exp_rules = self.fw_rules_v2.list() + api_rules = {'firewall_rules': self.api_fw_rules_v2.list()} + + self.mock_list_fwaas_firewall_rules.side_effect = [ + {'firewall_rules': []}, + api_rules, + ] + + ret_val = api_fwaas_v2.rule_list_for_tenant(self.request, tenant_id) + for (v, d) in zip(ret_val, exp_rules): + self._assert_rule_return_value(v, d) + + self.assertEqual(2, self.mock_list_fwaas_firewall_rules.call_count) + self.mock_list_fwaas_firewall_rules.assert_has_calls([ + mock.call(tenant_id=tenant_id, shared=False), + mock.call(shared=True), + ]) + + @helpers.create_mocks({neutronclient: ('show_fwaas_firewall_rule',)}) + def test_rule_get(self): + exp_rule = self.fw_rules_v2.first() + ret_dict = {'firewall_rule': self.api_fw_rules_v2.first()} + + self.mock_show_fwaas_firewall_rule.return_value = ret_dict + + ret_val = api_fwaas_v2.rule_get(self.request, exp_rule.id) + self._assert_rule_return_value(ret_val, exp_rule) + + self.mock_show_fwaas_firewall_rule.assert_called_once_with(exp_rule.id) + + @helpers.create_mocks({neutronclient: ('update_fwaas_firewall_rule',)}) + def test_rule_update(self): + rule = self.fw_rules_v2.first() + rule_dict = self.api_fw_rules_v2.first() + + rule.name = 'new name' + rule.description = 'new desc' + rule.protocol = 'icmp' + rule.action = 'deny' + rule.shared = True + rule.enabled = False + + rule_dict['name'] = 'new name' + rule_dict['description'] = 'new desc' + rule_dict['protocol'] = 'icmp' + rule_dict['action'] = 'deny' + rule_dict['shared'] = True + rule_dict['enabled'] = False + + form_data = {'name': rule.name, + 'description': rule.description, + 'protocol': rule.protocol, + 'action': rule.action, + 'shared': rule.shared, + 'enabled': rule.enabled + } + form_dict = {'firewall_rule': form_data} + ret_dict = {'firewall_rule': rule_dict} + + self.mock_update_fwaas_firewall_rule.return_value = ret_dict + + ret_val = api_fwaas_v2.rule_update(self.request, + rule.id, **form_data) + self._assert_rule_return_value(ret_val, rule) + + self.mock_update_fwaas_firewall_rule.assert_called_once_with( + rule.id, form_dict) + + @helpers.create_mocks({neutronclient: ('create_fwaas_firewall_policy', )}) + def test_policy_create(self): + policy1 = self.fw_policies_v2.first() + policy1_dict = self.api_fw_policies_v2.first() + + form_data = {'name': policy1.name, + 'description': policy1.description, + 'firewall_rules': policy1.firewall_rules, + 'shared': policy1.shared, + 'audited': policy1.audited + } + form_dict = {'firewall_policy': form_data} + ret_dict = {'firewall_policy': policy1_dict} + + self.mock_create_fwaas_firewall_policy.return_value = ret_dict + + ret_val = api_fwaas_v2.policy_create(self.request, **form_data) + self.assertIsInstance(ret_val, api_fwaas_v2.Policy) + self.assertEqual(policy1.name, ret_val.name) + self.assertTrue(ret_val.id) + + self.mock_create_fwaas_firewall_policy.assert_called_once_with( + form_dict) + + def _assert_policy_return_value(self, ret_val, exp_policy): + self.assertIsInstance(ret_val, api_fwaas_v2.Policy) + self.assertEqual(exp_policy.name, ret_val.name) + self.assertTrue(ret_val.id) + self.assertEqual(len(exp_policy.firewall_rules), len(ret_val.rules)) + self.assertEqual(len(exp_policy.firewall_rules), + len(ret_val.firewall_rules)) + for (r, exp_r) in zip(ret_val.rules, exp_policy.rules): + self.assertEqual(exp_r.id, r.id) + + @helpers.create_mocks({neutronclient: ('list_fwaas_firewall_policies', + 'list_fwaas_firewall_rules')}) + def test_policy_list(self): + exp_policies = self.fw_policies_v2.list() + policies_dict = {'firewall_policies': self.api_fw_policies_v2.list()} + rules_dict = {'firewall_rules': self.api_fw_rules_v2.list()} + + self.mock_list_fwaas_firewall_policies.return_value = policies_dict + self.mock_list_fwaas_firewall_rules.return_value = rules_dict + + ret_val = api_fwaas_v2.policy_list(self.request) + for (v, d) in zip(ret_val, exp_policies): + self._assert_policy_return_value(v, d) + + self.mock_list_fwaas_firewall_policies.assert_called_once_with() + self.mock_list_fwaas_firewall_rules.assert_called_once_with() + + @helpers.create_mocks({neutronclient: ('list_fwaas_firewall_policies', + 'list_fwaas_firewall_rules')}) + def test_policy_list_for_tenant(self): + tenant_id = self.request.user.project_id + exp_policies = self.fw_policies_v2.list() + policies_dict = {'firewall_policies': self.api_fw_policies_v2.list()} + rules_dict = {'firewall_rules': self.api_fw_rules_v2.list()} + + self.mock_list_fwaas_firewall_policies.side_effect = [ + {'firewall_policies': []}, + policies_dict, + ] + self.mock_list_fwaas_firewall_rules.return_value = rules_dict + + ret_val = api_fwaas_v2.policy_list_for_tenant(self.request, tenant_id) + for (v, d) in zip(ret_val, exp_policies): + self._assert_policy_return_value(v, d) + + self.assertEqual(2, self.mock_list_fwaas_firewall_policies.call_count) + self.mock_list_fwaas_firewall_policies.assert_has_calls([ + mock.call(tenant_id=tenant_id, shared=False), + mock.call(shared=True), + ]) + self.mock_list_fwaas_firewall_rules.assert_called_once_with() + + @helpers.create_mocks({neutronclient: ('show_fwaas_firewall_policy', + 'list_fwaas_firewall_rules')}) + def test_policy_get(self): + exp_policy = self.fw_policies_v2.first() + policy_dict = self.api_fw_policies_v2.first() + # The first two rules are associated with the first policy. + api_rules = self.api_fw_rules_v2.list()[:2] + + ret_dict = {'firewall_policy': policy_dict} + self.mock_show_fwaas_firewall_policy.return_value = ret_dict + filters = {'firewall_policy_id': exp_policy.id} + ret_dict = {'firewall_rules': api_rules} + + self.mock_list_fwaas_firewall_rules.return_value = ret_dict + + ret_val = api_fwaas_v2.policy_get(self.request, exp_policy.id) + self._assert_policy_return_value(ret_val, exp_policy) + + self.mock_show_fwaas_firewall_policy.assert_called_once_with( + exp_policy.id) + self.mock_list_fwaas_firewall_rules.assert_called_once_with(**filters) + + @helpers.create_mocks({neutronclient: ('show_fwaas_firewall_policy',)}) + def test_policy_get_no_rule(self): + # 2nd policy is not associated with any rules. + exp_policy = self.fw_policies_v2.list()[1] + policy_dict = self.api_fw_policies_v2.list()[1] + + ret_dict = {'firewall_policy': policy_dict} + self.mock_show_fwaas_firewall_policy.return_value = ret_dict + + ret_val = api_fwaas_v2.policy_get(self.request, exp_policy.id) + self.assertIsInstance(ret_val, api_fwaas_v2.Policy) + self.assertEqual(exp_policy.name, ret_val.name) + self.assertTrue(ret_val.id) + self.assertFalse(len(ret_val.rules)) + + self.mock_show_fwaas_firewall_policy.assert_called_once_with( + exp_policy.id) + + @helpers.create_mocks({neutronclient: ('update_fwaas_firewall_policy',)}) + def test_policy_update(self): + policy = self.fw_policies_v2.first() + policy_dict = self.api_fw_policies_v2.first() + + policy.name = 'new name' + policy.description = 'new desc' + policy.shared = True + policy.audited = False + + policy_dict['name'] = 'new name' + policy_dict['description'] = 'new desc' + policy_dict['shared'] = True + policy_dict['audited'] = False + + form_data = {'name': policy.name, + 'description': policy.description, + 'shared': policy.shared, + 'audited': policy.audited + } + + form_dict = {'firewall_policy': form_data} + ret_dict = {'firewall_policy': policy_dict} + + self.mock_update_fwaas_firewall_policy.return_value = ret_dict + + ret_val = api_fwaas_v2.policy_update(self.request, + policy.id, **form_data) + self.assertIsInstance(ret_val, api_fwaas_v2.Policy) + self.assertEqual(policy.name, ret_val.name) + self.assertTrue(ret_val.id) + + self.mock_update_fwaas_firewall_policy.assert_called_once_with( + policy.id, form_dict) + + @helpers.create_mocks( + {neutronclient: ('insert_rule_fwaas_firewall_policy',)}) + def test_policy_insert_rule(self): + policy = self.fw_policies_v2.first() + policy_dict = self.api_fw_policies_v2.first() + + new_rule_id = 'h0881d38-c3eb-4fee-9763-12de3338041d' + policy.firewall_rules.append(new_rule_id) + policy_dict['firewall_rules'].append(new_rule_id) + + body = {'firewall_rule_id': new_rule_id, + 'insert_before': policy.firewall_rules[1], + 'insert_after': policy.firewall_rules[0]} + + self.mock_insert_rule_fwaas_firewall_policy.return_value = policy_dict + + ret_val = api_fwaas_v2.policy_insert_rule(self.request, + policy.id, **body) + self.assertIn(new_rule_id, ret_val.firewall_rules) + self.mock_insert_rule_fwaas_firewall_policy.assert_called_once_with( + policy.id, body) + + @helpers.create_mocks( + {neutronclient: ('remove_rule_fwaas_firewall_policy',)}) + def test_policy_remove_rule(self): + policy = self.fw_policies_v2.first() + policy_dict = self.api_fw_policies_v2.first() + + remove_rule_id = policy.firewall_rules[0] + policy_dict['firewall_rules'].remove(remove_rule_id) + + body = {'firewall_rule_id': remove_rule_id} + + self.mock_remove_rule_fwaas_firewall_policy.return_value = policy_dict + + ret_val = api_fwaas_v2.policy_remove_rule(self.request, + policy.id, **body) + self.assertNotIn(remove_rule_id, ret_val.firewall_rules) + self.mock_remove_rule_fwaas_firewall_policy.assert_called_once_with( + policy.id, body) + + @helpers.create_mocks({neutronclient: ('create_fwaas_firewall_group', )}) + def test_firewall_group_create(self): + firewall_group = self.firewall_groups_v2.first() + firewall_group_dict = self.api_firewall_groups_v2.first() + + form_data = { + '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 + } + + form_dict = {'firewall_group': form_data} + ret_dict = {'firewall_group': firewall_group_dict} + self.mock_create_fwaas_firewall_group.return_value = ret_dict + + ret_val = api_fwaas_v2.firewall_group_create(self.request, **form_data) + self.assertIsInstance(ret_val, api_fwaas_v2.FirewallGroup) + self.assertEqual(firewall_group.name, ret_val.name) + self.assertEqual(firewall_group.id, ret_val.id) + + self.mock_create_fwaas_firewall_group.assert_called_once_with( + form_dict) + + def _assert_firewall_return_value(self, ret_val, exp_firewall, + expand_policy=True): + self.assertIsInstance(ret_val, api_fwaas_v2.FirewallGroup) + self.assertEqual(exp_firewall.name, ret_val.name) + self.assertTrue(ret_val.id) + self.assertEqual(exp_firewall.ingress_firewall_policy_id, + ret_val.ingress_firewall_policy_id) + if expand_policy: + if exp_firewall.ingress_firewall_policy_id: + self.assertEqual(exp_firewall.ingress_firewall_policy_id, + ret_val.ingress_policy.id) + self.assertEqual(exp_firewall.ingress_policy.name, + ret_val.ingress_policy.name) + else: + self.assertIsNone(ret_val.ingress_policy) + if exp_firewall.egress_firewall_policy_id: + self.assertEqual(exp_firewall.egress_firewall_policy_id, + ret_val.egress_policy.id) + self.assertEqual(exp_firewall.egress_policy.name, + ret_val.egress_policy.name) + else: + self.assertIsNone(ret_val.egress_policy) + + # TODO(Sarath Mekala) : Add API tests for firewall_group_create with ports, + # add port to firewall and remove port from fw. + + @helpers.create_mocks({neutronclient: ('list_fwaas_firewall_groups', + 'list_fwaas_firewall_policies')}) + def test_firewall_group_list(self): + exp_firewalls = self.firewall_groups_v2.list() + firewalls_dict = { + 'firewall_groups': self.api_firewall_groups_v2.list()} + + self.mock_list_fwaas_firewall_groups.return_value = firewalls_dict + + ret_val = api_fwaas_v2.firewall_group_list(self.request) + for (v, d) in zip(ret_val, exp_firewalls): + self._assert_firewall_return_value(v, d, expand_policy=False) + self.mock_list_fwaas_firewall_groups.assert_called_once_with() + + @helpers.create_mocks({neutronclient: ('list_fwaas_firewall_groups', + 'list_fwaas_firewall_policies')}) + def test_firewall_group_list_for_tenant(self): + tenant_id = self.request.user.project_id + exp_firewalls = self.firewall_groups_v2.list() + firewalls_dict = { + 'firewall_groups': self.api_firewall_groups_v2.list()} + + self.mock_list_fwaas_firewall_groups.side_effect = [ + firewalls_dict, + firewalls_dict, + ] + + ret_val = api_fwaas_v2.firewall_group_list_for_tenant( + self.request, tenant_id) + for (v, d) in zip(ret_val, exp_firewalls): + self._assert_firewall_return_value(v, d, expand_policy=False) + + self.assertEqual(2, self.mock_list_fwaas_firewall_groups.call_count) + self.mock_list_fwaas_firewall_groups.assert_has_calls([ + mock.call(shared=False, tenant_id=tenant_id), + mock.call(shared=True), + ]) + + @helpers.create_mocks({neutronclient: ('list_fwaas_firewall_groups', )}) + def test_fwg_port_list(self): + mock_port_id_1 = '62b974c5-48fb-4fd1-946f-5ace1d970dd4' + mock_port_id_2 = 'da012bb6-c350-4a72-b6c9-69c4f2008aa4' + mock_port_id_3 = 'c2a2ce11-71dd-49a5-84ec-2407ecb42106' + + mock_groups = [ + {'ports': [mock_port_id_1, mock_port_id_2]}, + {'ports': []}, + {'ports': [mock_port_id_3]} + ] + self.mock_list_fwaas_firewall_groups.return_value = { + 'firewall_groups': mock_groups + } + + expected_set = {mock_port_id_1, mock_port_id_2, mock_port_id_3} + retrieved_set = api_fwaas_v2.fwg_port_list(self.request) + + self.assertEqual(expected_set, retrieved_set) + + @helpers.create_mocks({neutronclient: ('list_ports', + 'list_fwaas_firewall_groups')}) + def test_fwg_port_list_for_tenant(self): + tenant_id = self.request.user.project_id + router_port = { + 'id': 'id-1', + 'name': 'port-1', + 'device_owner': 'network:router_interface' + } + vm_port1 = { + 'id': 'id-vm_port-1', + 'name': 'port-2', + 'device_owner': 'compute:nova' + } + vm_port2 = { + 'id': 'id-vm_port-2', + 'name': 'port-2', + 'device_owner': 'compute:nova' + } + gateway_port = { + 'id': 'id-3', + 'name': 'port-3', + 'device_owner': 'network:router_gateway' + } + dhcp_port = { + 'id': 'id-4', + 'name': 'port-4', + 'device_owner': 'network:dhcp' + } + dummy_ports = {'ports': [ + router_port, + vm_port1, + vm_port2, + gateway_port, + dhcp_port, + ]} + + self.mock_list_ports.return_value = dummy_ports + self.mock_list_fwaas_firewall_groups.return_value = \ + {'firewall_groups': []} + + ports = api_fwaas_v2.fwg_port_list_for_tenant(self.request, tenant_id) + self.assertEqual(router_port['id'], ports[0]['id']) + self.assertEqual(vm_port1['id'], ports[1]['id']) + self.assertEqual(vm_port2['id'], ports[2]['id']) + + self.mock_list_ports.assert_called_once_with(tenant_id=tenant_id) + self.mock_list_fwaas_firewall_groups.assert_called_once_with( + tenant_id=tenant_id) + + @helpers.create_mocks({neutronclient: ('list_ports', + 'list_fwaas_firewall_groups')}) + def test_fwg_port_list_for_tenant_with_used_port(self): + tenant_id = self.request.user.project_id + router_port = { + 'id': 'id-1', + 'name': 'port-1', + 'device_owner': 'network:router_interface' + } + vm_port1 = { + 'id': 'id-vm_port-1', + 'name': 'port-2', + 'device_owner': 'compute:nova' + } + gateway_port = { + 'id': 'id-3', + 'name': 'port-3', + 'device_owner': 'network:router_gateway' + } + dhcp_port = { + 'id': 'id-4', + 'name': 'port-4', + 'device_owner': 'network:dhcp' + } + dummy_ports = {'ports': [ + router_port, + vm_port1, + gateway_port, + dhcp_port, + ]} + + used_ports = {'firewall_groups': [{'ports': [router_port['id']]}]} + + self.mock_list_ports.return_value = dummy_ports + self.mock_list_fwaas_firewall_groups.return_value = used_ports + + ports = api_fwaas_v2.fwg_port_list_for_tenant(self.request, tenant_id) + self.assertEqual(vm_port1['id'], ports[0]['id']) + + self.mock_list_ports.assert_called_once_with(tenant_id=tenant_id) + self.mock_list_fwaas_firewall_groups.assert_called_once_with( + tenant_id=tenant_id) + + @helpers.create_mocks({neutronclient: ('list_ports', + 'list_fwaas_firewall_groups')}) + def test_fwg_port_list_for_tenant_no_match(self): + tenant_id = self.request.user.project_id + dummy_ports = {'ports': [ + {'name': 'port-3', 'device_owner': 'network:router_gateway'}, + {'name': 'port-4', 'device_owner': 'network:dhcp'}, + ]} + + self.mock_list_ports.return_value = dummy_ports + self.mock_list_fwaas_firewall_groups.return_value = \ + {'firewall_groups': []} + + ports = api_fwaas_v2.fwg_port_list_for_tenant(self.request, tenant_id) + self.assertEqual([], ports) + + self.mock_list_ports.assert_called_once_with(tenant_id=tenant_id) + self.mock_list_fwaas_firewall_groups.assert_called_once_with( + tenant_id=tenant_id) + + @helpers.create_mocks({neutronclient: ('list_ports', + 'list_fwaas_firewall_groups')}) + def test_fwg_port_list_for_tenant_no_ports(self): + tenant_id = self.request.user.project_id + self.mock_list_ports.return_value = {'ports': []} + self.mock_list_fwaas_firewall_groups.return_value = \ + {'firewall_groups': []} + + ports = api_fwaas_v2.fwg_port_list_for_tenant(self.request, tenant_id) + self.assertEqual([], ports) + + self.mock_list_ports.assert_called_once_with(tenant_id=tenant_id) + self.mock_list_fwaas_firewall_groups.assert_called_once_with( + tenant_id=tenant_id) + + @helpers.create_mocks({neutronclient: ('show_fwaas_firewall_group', + 'show_fwaas_firewall_policy')}) + def test_firewall_group_get(self): + exp_firewall = self.firewall_groups_v2.first() + ret_dict = {'firewall_group': self.api_firewall_groups_v2.first()} + + ingress_policy_id = exp_firewall.ingress_firewall_policy_id + ingress_policy = [p for p in self.api_fw_policies_v2.list() + if p['id'] == ingress_policy_id][0] + + egress_policy_id = exp_firewall.egress_firewall_policy_id + egress_policy = [p for p in self.api_fw_policies_v2.list() + if p['id'] == egress_policy_id][0] + + self.mock_show_fwaas_firewall_group.return_value = ret_dict + self.mock_show_fwaas_firewall_policy.side_effect = [ + {'firewall_policy': ingress_policy}, + {'firewall_policy': egress_policy} + ] + + ret_val = api_fwaas_v2.firewall_group_get(self.request, + exp_firewall.id) + self._assert_firewall_return_value(ret_val, exp_firewall) + + self.mock_show_fwaas_firewall_group.assert_called_once_with( + exp_firewall.id) + self.assertEqual(2, self.mock_show_fwaas_firewall_policy.call_count) + self.mock_show_fwaas_firewall_policy.assert_has_calls([ + mock.call(ingress_policy_id), + mock.call(egress_policy_id), + ]) + + @helpers.create_mocks({neutronclient: ('update_fwaas_firewall_group',)}) + def test_firewall_group_update(self): + firewall = self.firewall_groups_v2.first() + firewall_dict = self.api_firewall_groups_v2.first() + + firewall.name = 'new name' + firewall.description = 'new desc' + firewall.admin_state_up = False + + firewall_dict['name'] = 'new name' + firewall_dict['description'] = 'new desc' + firewall_dict['admin_state_up'] = False + + form_data = {'name': firewall.name, + 'description': firewall.description, + 'admin_state_up': firewall.admin_state_up + } + + form_dict = {'firewall_group': form_data} + ret_dict = {'firewall_group': firewall_dict} + + self.mock_update_fwaas_firewall_group.return_value = ret_dict + + ret_val = api_fwaas_v2.firewall_group_update(self.request, + firewall.id, **form_data) + self.assertIsInstance(ret_val, api_fwaas_v2.FirewallGroup) + self.assertEqual(firewall.name, ret_val.name) + self.assertTrue(ret_val.id) + + self.mock_update_fwaas_firewall_group.assert_called_once_with( + firewall.id, form_dict) diff --git a/neutron_fwaas_dashboard/test/helpers.py b/neutron_fwaas_dashboard/test/helpers.py new file mode 100644 index 0000000..b191c0e --- /dev/null +++ b/neutron_fwaas_dashboard/test/helpers.py @@ -0,0 +1,33 @@ +# 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 openstack_dashboard.test import helpers + +from neutron_fwaas_dashboard.test.test_data import utils + + +class TestDataLoaderMixin(object): + def _setup_test_data(self): + super(TestDataLoaderMixin, self)._setup_test_data() + utils.load_data(self) + + +class TestCase(TestDataLoaderMixin, helpers.TestCase): + pass + + +class BaseAdminViewTests(TestDataLoaderMixin, helpers.TestCase): + pass + + +class APITestCase(TestDataLoaderMixin, helpers.APITestCase): + pass diff --git a/neutron_fwaas_dashboard/test/integration/__init__.py b/neutron_fwaas_dashboard/test/integration/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/neutron_fwaas_dashboard/test/integration/horizon.conf b/neutron_fwaas_dashboard/test/integration/horizon.conf new file mode 100644 index 0000000..9b512a0 --- /dev/null +++ b/neutron_fwaas_dashboard/test/integration/horizon.conf @@ -0,0 +1,4 @@ +[plugin] +is_plugin=True +plugin_page_path=neutron_fwaas_dashboard.test.integration.pages +plugin_page_structure='{"Project": {"Network": {"_": ["Firewall Groups"]}}}' diff --git a/neutron_fwaas_dashboard/test/integration/pages/__init__.py b/neutron_fwaas_dashboard/test/integration/pages/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/neutron_fwaas_dashboard/test/integration/pages/project/__init__.py b/neutron_fwaas_dashboard/test/integration/pages/project/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/neutron_fwaas_dashboard/test/integration/pages/project/network/__init__.py b/neutron_fwaas_dashboard/test/integration/pages/project/network/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/neutron_fwaas_dashboard/test/integration/pages/project/network/firewallgroupspage.py b/neutron_fwaas_dashboard/test/integration/pages/project/network/firewallgroupspage.py new file mode 100644 index 0000000..71b2e32 --- /dev/null +++ b/neutron_fwaas_dashboard/test/integration/pages/project/network/firewallgroupspage.py @@ -0,0 +1,21 @@ +# +# 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 openstack_dashboard.test.integration_tests.pages import basepage + + +class FirewallgroupsPage(basepage.BaseNavigationPage): + def __init__(self, driver, conf): + super(FirewallgroupsPage, self).__init__(driver, conf) + self._page_title = "Firewall Groups" diff --git a/neutron_fwaas_dashboard/test/integration/test_basic.py b/neutron_fwaas_dashboard/test/integration/test_basic.py new file mode 100644 index 0000000..678cf4f --- /dev/null +++ b/neutron_fwaas_dashboard/test/integration/test_basic.py @@ -0,0 +1,24 @@ +# +# 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 openstack_dashboard.test.integration_tests import helpers + + +class TestNeutronFWaaSDashboardInstalled(helpers.TestCase): + + def test_alarms_page_opened(self): + firewall_groups_page = \ + self.home_pg.go_to_project_network_firewallgroupspage() + self.assertEqual(firewall_groups_page.page_title, + 'Firewall Groups - OpenStack Dashboard') diff --git a/neutron_fwaas_dashboard/test/settings.py b/neutron_fwaas_dashboard/test/settings.py new file mode 100644 index 0000000..de07606 --- /dev/null +++ b/neutron_fwaas_dashboard/test/settings.py @@ -0,0 +1,51 @@ +# 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. + +# Default to Horizons test settings to avoid any missing keys +from horizon.test.settings import * # noqa +from openstack_dashboard.test.settings import * # noqa + +import openstack_dashboard.enabled +from openstack_dashboard.utils import settings + +import neutron_fwaas_dashboard.enabled + +# pop these keys to avoid log warnings about deprecation +# update_dashboards will populate them anyway +HORIZON_CONFIG.pop('dashboards', None) +HORIZON_CONFIG.pop('default_dashboard', None) + +# Update the dashboards with neutron_fwaas_dashboard +settings.update_dashboards( + [ + openstack_dashboard.enabled, + neutron_fwaas_dashboard.enabled, + ], + HORIZON_CONFIG, + INSTALLED_APPS +) + +# Ensure any duplicate apps are removed after the update_dashboards call +INSTALLED_APPS = list(set(INSTALLED_APPS)) + +# -------------------- +# Test-only settings +# -------------------- +# TEST_GLOBAL_MOCKS_ON_PANELS: defines what and how methods should be +# mocked globally for unit tests and Selenium tests. +# 'method' is required. 'return_value' and 'side_effect' +# are optional and passed to mock.patch(). +TEST_GLOBAL_MOCKS_ON_PANELS['firewalls_v2'] = { + 'method': ('neutron_fwaas_dashboard.dashboards.project.firewalls_v2.panel.' + 'Firewall_V2.can_access'), + 'return_value': True, +} diff --git a/neutron_fwaas_dashboard/test/test_data/__init__.py b/neutron_fwaas_dashboard/test/test_data/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/neutron_fwaas_dashboard/test/test_data/fwaas_v2_data.py b/neutron_fwaas_dashboard/test/test_data/fwaas_v2_data.py new file mode 100644 index 0000000..3c55561 --- /dev/null +++ b/neutron_fwaas_dashboard/test/test_data/fwaas_v2_data.py @@ -0,0 +1,167 @@ +# Copyright 2017 Juniper Networks +# +# 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 + +from openstack_dashboard.test.test_data import utils + +from neutron_fwaas_dashboard.api import fwaas_v2 as fwaas + + +def data(TEST): + # Data returned by openstack_dashboard.api.neutron wrapper. + TEST.firewall_groups_v2 = utils.TestDataContainer() + TEST.fw_policies_v2 = utils.TestDataContainer() + TEST.fw_rules_v2 = utils.TestDataContainer() + + # Data return by neutronclient. + TEST.api_firewall_groups_v2 = utils.TestDataContainer() + TEST.api_fw_policies_v2 = utils.TestDataContainer() + TEST.api_fw_rules_v2 = utils.TestDataContainer() + + # 1st rule (used by 1st policy) + rule1_dict = { + 'action': 'allow', + 'description': 'rule1 description', + 'destination_ip_address': '4.5.6.7/32', + 'destination_port': '1:65535', + 'enabled': True, + 'id': 'f0881d38-c3eb-4fee-9763-12de3338041d', + 'ip_version': '4', + 'name': 'rule1', + 'protocol': 'tcp', + 'shared': True, + 'source_ip_address': '1.2.3.0/24', + 'source_port': '80', + 'tenant_id': '1', + } + TEST.api_fw_rules_v2.add(rule1_dict) + + rule1 = fwaas.Rule(copy.deepcopy(rule1_dict)) + TEST.fw_rules_v2.add(rule1) + + # 2nd rule (used by 2nd policy; no name) + rule2_dict = { + 'action': 'deny', + 'description': '', + 'destination_ip_address': None, + 'destination_port': '1:65535', + 'enabled': True, + 'id': 'c6298a93-850f-4f64-b78a-959fd4f1e5df', + 'ip_version': '6', + 'name': '', + 'protocol': 'udp', + 'shared': False, + 'source_ip_address': '2001:db8::/32', + 'source_port': '80', + 'tenant_id': '1', + } + TEST.api_fw_rules_v2.add(rule2_dict) + + rule2 = fwaas.Rule(copy.deepcopy(rule2_dict)) + TEST.fw_rules_v2.add(rule2) + + # 3rd rule (not used by any policy) + rule3_dict = { + 'action': 'allow', + 'description': 'rule3 description', + 'destination_ip_address': '4.5.6.7/32', + 'destination_port': '1:65535', + 'enabled': True, + 'id': 'h0881d38-c3eb-4fee-9763-12de3338041d', + 'ip_version': '4', + 'name': 'rule3', + 'protocol': None, + 'shared': True, + 'source_ip_address': '1.2.3.0/24', + 'source_port': '80', + 'tenant_id': '1', + } + TEST.api_fw_rules_v2.add(rule3_dict) + + rule3 = fwaas.Rule(copy.deepcopy(rule3_dict)) + TEST.fw_rules_v2.add(rule3) + + # 1st policy (associated with 2 rules) + policy1_dict = { + 'audited': True, + 'description': 'policy with two rules', + 'firewall_rules': [rule1_dict['id'], rule2_dict['id']], + 'id': 'abcdef-c3eb-4fee-9763-12de3338041e', + 'name': 'policy1', + 'shared': True, + 'tenant_id': '1', + } + TEST.api_fw_policies_v2.add(policy1_dict) + + policy1 = fwaas.Policy(copy.deepcopy(policy1_dict)) + policy1._apidict['rules'] = [rule1, rule2] + TEST.fw_policies_v2.add(policy1) + + # 2nd policy (associated with no rules; no name) + policy2_dict = { + 'audited': False, + 'description': '', + 'firewall_rules': [], + 'id': 'cf50b331-787a-4623-825e-da794c918d6a', + 'name': '', + 'shared': False, + 'tenant_id': '1', + } + TEST.api_fw_policies_v2.add(policy2_dict) + + policy2 = fwaas.Policy(copy.deepcopy(policy2_dict)) + policy2._apidict['rules'] = [] + TEST.fw_policies_v2.add(policy2) + + # 1st firewall group + fwg1_dict = { + 'admin_state_up': True, + 'description': 'firewall description', + 'egress_firewall_policy_id': 'cf50b331-787a-4623-825e-da794c918d6a', + 'id': '8913dde8-4915-4b90-8d3e-b95eeedb0d49', + 'ingress_firewall_policy_id': 'abcdef-c3eb-4fee-9763-12de3338041e', + 'name': 'firewallgroup1', + 'ports': [], + 'shared': False, + 'status': 'PENDING_CREATE', + 'tenant_id': '1', + } + TEST.api_firewall_groups_v2.add(fwg1_dict) + + fwg1 = fwaas.FirewallGroup(copy.deepcopy(fwg1_dict)) + fwg1._apidict['ingress_policy'] = policy1 + fwg1._apidict['egress_policy'] = policy2 + fwg1._apidict['port_ids'] = [] + TEST.firewall_groups_v2.add(fwg1) + + # 2nd firewall group (no name) + fwg2_dict = { + 'admin_state_up': True, + 'description': '', + 'egress_firewall_policy_id': None, + 'id': '1aa75150-415f-458e-bae5-5a362a4fb1f7', + 'ingress_firewall_policy_id': None, + 'name': '', + 'ports': [], + 'shared': False, + 'status': 'INACTIVE', + 'tenant_id': '1', + } + TEST.api_firewall_groups_v2.add(fwg2_dict) + + fwg2 = fwaas.FirewallGroup(copy.deepcopy(fwg2_dict)) + fwg2._apidict['ingress_policy'] = None + fwg2._apidict['egress_policy'] = None + TEST.firewall_groups_v2.add(fwg2) diff --git a/neutron_fwaas_dashboard/test/test_data/utils.py b/neutron_fwaas_dashboard/test/test_data/utils.py new file mode 100644 index 0000000..7894479 --- /dev/null +++ b/neutron_fwaas_dashboard/test/test_data/utils.py @@ -0,0 +1,28 @@ +# 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 openstack_dashboard.test.test_data import utils + + +def load_data(load_onto=None): + from neutron_fwaas_dashboard.test.test_data import fwaas_v2_data + + # The order of these loaders matters, some depend on others. + loaders = ( + fwaas_v2_data.data, + ) + if load_onto: + for data_func in loaders: + data_func(load_onto) + return load_onto + else: + return utils.TestData(*loaders) diff --git a/neutron_fwaas_dashboard/version.py b/neutron_fwaas_dashboard/version.py new file mode 100644 index 0000000..4b1eff1 --- /dev/null +++ b/neutron_fwaas_dashboard/version.py @@ -0,0 +1,15 @@ +# 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_dashboard') diff --git a/releasenotes/notes/.placeholder b/releasenotes/notes/.placeholder new file mode 100644 index 0000000..e69de29 diff --git a/releasenotes/notes/add-remove-router-policy-a3145bce0ce3cd92.yaml b/releasenotes/notes/add-remove-router-policy-a3145bce0ce3cd92.yaml new file mode 100644 index 0000000..648a5c5 --- /dev/null +++ b/releasenotes/notes/add-remove-router-policy-a3145bce0ce3cd92.yaml @@ -0,0 +1,10 @@ +--- +fixes: + - | + "Add Router" and "Remove Router" operations are now controlled by + ``update_firewall`` rule in the policy.json file. + Previously they were controlled by ``get_firewall`` rule and non-existing + rules like ``add_router`` and ``remove_router``. + If operators define their custom policy rules for ``add_router`` and + ``remove_router``, such policy rules need to updated accordingly. + [`bug 1703952 `__] diff --git a/releasenotes/notes/deprecate-neutron-fwaas-as-stadium-project-af51b5bcab1cb25f.yaml b/releasenotes/notes/deprecate-neutron-fwaas-as-stadium-project-af51b5bcab1cb25f.yaml new file mode 100644 index 0000000..37c27d3 --- /dev/null +++ b/releasenotes/notes/deprecate-neutron-fwaas-as-stadium-project-af51b5bcab1cb25f.yaml @@ -0,0 +1,14 @@ +--- +prelude: > + Neutron-fwaas-dashboard project is now deprecated in the Neutron stadium. +deprecations: + - | + Due to lack of maintainers neutron-fwaas-dashboard project is now + deprecated in the Neutron stadium. There is no planned releases of this + project in the ``Victoria`` cycle. + In ``W`` cycle project will be moved out from the stadium to the unofficial + OpenStack projects. + If You want to step in and be maintainer of this project to keep it in the + Neutron stadium, please contact the ``neutron team`` via + openstack-discuss@lists.openstack.org or IRC channel #openstack-neutron + @freenode. diff --git a/releasenotes/notes/drop-fwaas-v1-d10b108e8cf970fe.yaml b/releasenotes/notes/drop-fwaas-v1-d10b108e8cf970fe.yaml new file mode 100644 index 0000000..c367bb3 --- /dev/null +++ b/releasenotes/notes/drop-fwaas-v1-d10b108e8cf970fe.yaml @@ -0,0 +1,7 @@ +--- +upgrade: + - | + FWaaS v1 support was dropped. + FWaaS v1 has been deprecated in neutron-fwaas and was dropped in Stein + release. Along with neutron-fwaas, neutron-fwaas-dashboard dropped + its support. diff --git a/releasenotes/notes/drop-py-2-7-aeac41c824c80d4a.yaml b/releasenotes/notes/drop-py-2-7-aeac41c824c80d4a.yaml new file mode 100644 index 0000000..8cdc657 --- /dev/null +++ b/releasenotes/notes/drop-py-2-7-aeac41c824c80d4a.yaml @@ -0,0 +1,6 @@ +--- +upgrade: + - | + Python 2.7 support has been dropped. Last release of neutron-fwaas + dashboard to support py2.7 is OpenStack Train. The minimum version + of Python now supported by neutron-fwaas-dashboard is Python 3.6. diff --git a/releasenotes/notes/fwaas-v2-dashboard-cd148bd824c5827f.yaml b/releasenotes/notes/fwaas-v2-dashboard-cd148bd824c5827f.yaml new file mode 100644 index 0000000..a53b5ba --- /dev/null +++ b/releasenotes/notes/fwaas-v2-dashboard-cd148bd824c5827f.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + A panel for FWaaS v2 is newly added. There is no specific installation + process. After installing the new release of neutron-fwaas-dashboard, + if FWaaS v2 API is available on your neutron deployment, the panel for + FWaaS v2 will be displayed. diff --git a/releasenotes/notes/shared-policy-fix-db8ca3cd327540d0.yaml b/releasenotes/notes/shared-policy-fix-db8ca3cd327540d0.yaml new file mode 100644 index 0000000..8934c74 --- /dev/null +++ b/releasenotes/notes/shared-policy-fix-db8ca3cd327540d0.yaml @@ -0,0 +1,9 @@ +--- +fixes: + - | + Handling a shared rule or policy is now controlled by the policy + mechanism. Only admin users (by default) can set ``shared`` attribute + when creating or updating a firewall rule or policy. + The checkbox on ``shared`` attribute is now disabled if a user + has no sufficient permission. + [`bug 1699717 `__] diff --git a/releasenotes/notes/split-out-from-horizon-4807f953d5dc0799.yaml b/releasenotes/notes/split-out-from-horizon-4807f953d5dc0799.yaml new file mode 100644 index 0000000..982472b --- /dev/null +++ b/releasenotes/notes/split-out-from-horizon-4807f953d5dc0799.yaml @@ -0,0 +1,12 @@ +--- +prelude: > + Neutron FWaaS support in the OpenStack Dashboard is now split out into + a separate python package. +features: + - | + Neutron FWaaS support in the OpenStack Dashboard is now split out into a + separate package ``neutron-fwaas-dashboard``. You need to install + ``neutron-fwaas-dashboard`` after upgrading the OpenStack Dashboard + to Pike release and add ``enabled`` file for Neutron FWaaS dashboard. + For detail information, see + https://docs.openstack.org/neutron-fwaas-dashboard/latest/. diff --git a/releasenotes/source/conf.py b/releasenotes/source/conf.py new file mode 100644 index 0000000..09c1371 --- /dev/null +++ b/releasenotes/source/conf.py @@ -0,0 +1,197 @@ +# -*- 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. + +# Glance 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 = [ + 'reno.sphinxext', + 'openstackdocstheme', +] + +# openstackdocstheme options +openstackdocs_repo_name = 'openstack/neutron-fwaas-dashboard' +openstackdocs_auto_name = False +openstackdocs_bug_project = 'neutron-fwaas-dashboard' +openstackdocs_bug_tag = 'doc' + +# 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 Dashboard Release Notes' +copyright = u'2017, OpenStack Foundation' + +# 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 = 'native' + +# 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 = [] + +# 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' + +# 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 = 'neutronfwaasdashboardReleaseNotesdoc' + +# -- Options for Internationalization output ------------------------------ +locale_dirs = ['locale/'] diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst new file mode 100644 index 0000000..21987f2 --- /dev/null +++ b/releasenotes/source/index.rst @@ -0,0 +1,14 @@ +===================================== +Neutron FWaaS Dashboard Release Notes +===================================== + +.. toctree:: + :maxdepth: 1 + + unreleased + ussuri + train + stein + rocky + queens + pike diff --git a/releasenotes/source/locale/cs/LC_MESSAGES/releasenotes.po b/releasenotes/source/locale/cs/LC_MESSAGES/releasenotes.po new file mode 100644 index 0000000..57370ac --- /dev/null +++ b/releasenotes/source/locale/cs/LC_MESSAGES/releasenotes.po @@ -0,0 +1,25 @@ +# Lenka Husáková , 2016. #zanata +# Zbyněk Schwarz , 2017. #zanata +msgid "" +msgstr "" +"Project-Id-Version: neutron-fwaas-dashboard\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-04-22 07:50+0000\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"PO-Revision-Date: 2017-11-17 09:06+0000\n" +"Last-Translator: Zbyněk Schwarz \n" +"Language-Team: Czech\n" +"Language: cs\n" +"X-Generator: Zanata 4.3.3\n" +"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2\n" + +msgid "Bug Fixes" +msgstr "Opravy chyb" + +msgid "Current Series Release Notes" +msgstr "Poznámky k vydání současné verze" + +msgid "Pike Series Release Notes" +msgstr "Poznámky k vydání verze Pike" diff --git a/releasenotes/source/locale/de/LC_MESSAGES/releasenotes.po b/releasenotes/source/locale/de/LC_MESSAGES/releasenotes.po new file mode 100644 index 0000000..7bc8251 --- /dev/null +++ b/releasenotes/source/locale/de/LC_MESSAGES/releasenotes.po @@ -0,0 +1,150 @@ +# Frank Kloeker , 2017. #zanata +# Robert Simai , 2017. #zanata +# Robert Simai , 2018. #zanata +# Robert Simai , 2019. #zanata +# Andreas Jaeger , 2020. #zanata +msgid "" +msgstr "" +"Project-Id-Version: neutron-fwaas-dashboard\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-04-24 05:38+0000\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"PO-Revision-Date: 2020-04-25 09:26+0000\n" +"Last-Translator: Andreas Jaeger \n" +"Language-Team: German\n" +"Language: de\n" +"X-Generator: Zanata 4.3.3\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" + +msgid "" +"\"Add Router\" and \"Remove Router\" operations are now controlled by " +"``update_firewall`` rule in the policy.json file. Previously they were " +"controlled by ``get_firewall`` rule and non-existing rules like " +"``add_router`` and ``remove_router``. If operators define their custom " +"policy rules for ``add_router`` and ``remove_router``, such policy rules " +"need to updated accordingly. [`bug 1703952 `__]" +msgstr "" +"Die \"Router hinzufügen\" und \"Router Entfernen\" Operationen werden jetzt " +"durch die ``update_firewall`` Regel in der policy.json Datei gesteuert. " +"Zuvor wurden sie durch die ``get_firewall`` Regel gesteuert, sowie durch " +"nicht vorhandene Regeln wie ``add_router`` und ``remove_router``. Wenn " +"Betreiber ihre eigenen Richtlinien für ``add_router`` und ``remove_router`` " +"anlegen, müssen solche Richtlinienregeln entsprechend aktualisiert werden. " +"[`bug 1703952 `__]" + +msgid "0.1.0" +msgstr "0.1.0" + +msgid "1.1.0" +msgstr "1.1.0" + +msgid "2.0.0" +msgstr "2.0.0" + +msgid "" +"A panel for FWaaS v2 is newly added. There is no specific installation " +"process. After installing the new release of neutron-fwaas-dashboard, if " +"FWaaS v2 API is available on your neutron deployment, the panel for FWaaS v2 " +"will be displayed." +msgstr "" +"Ein neues Paneel für FWaaS v2 wurde hinzugefügt. Es gibt keinen spezifischen " +"Installationsprozess. Nach Installation des neuen Release von neutron-fwaas-" +"dashboard und wenn das FWaaS v2 API in Ihrer Neutron-Umgebung zur Verfügung " +"steht, wird das Paneel für FWaaS v2 angezeigt." + +msgid "Bug Fixes" +msgstr "Fehlerkorrekturen" + +msgid "Current Series Release Notes" +msgstr "Aktuelle Serie Releasenotes" + +msgid "Deprecation Notes" +msgstr "Ablaufwarnungen" + +msgid "" +"FWaaS v1 support was dropped. FWaaS v1 has been deprecated in neutron-fwaas " +"and was dropped in Stein release. Along with neutron-fwaas, neutron-fwaas-" +"dashboard dropped its support." +msgstr "" +"FWaaS v1 Unterstützung beendet. FWaaS v1 wurde in neutron-fwaas obsolet und " +"im Stein Release fallen gelassen. Zusammen mit neutron-fwaas hat auch " +"neutron-fwaas-dashboard die Unterstützung aufgegeben." + +msgid "" +"Handling a shared rule or policy is now controlled by the policy mechanism. " +"Only admin users (by default) can set ``shared`` attribute when creating or " +"updating a firewall rule or policy. The checkbox on ``shared`` attribute is " +"now disabled if a user has no sufficient permission. [`bug 1699717 `__]" +msgstr "" +"Eine geteilte Regel oder Richtlinie wird jetzt über den " +"Richtlinienmechanismus gesteuert. Nur Administratoren (Standard) können ein " +"``shared`` Attribute setzen, wenn sie eine Firewall Regel oder Richtlinie " +"erstellen oder aktualisieren. Die Checkbox für das ``shared`` Attribut ist " +"inaktiv, wenn dem Benutzer entsprechende Rechte fehlen. [`bug 1699717 " +"`__]" + +msgid "Neutron FWaaS Dashboard Release Notes" +msgstr "Neutron FWaaS Dashboard Releasenotes" + +msgid "" +"Neutron FWaaS support in the OpenStack Dashboard is now split out into a " +"separate package ``neutron-fwaas-dashboard``. You need to install ``neutron-" +"fwaas-dashboard`` after upgrading the OpenStack Dashboard to Pike release " +"and add ``enabled`` file for Neutron FWaaS dashboard. For detail " +"information, see https://docs.openstack.org/neutron-fwaas-dashboard/latest/." +msgstr "" +"Neutron FWaaS Unterstützung im OpenStack Dashboard ist jetzt in ein " +"separates Paket ``neutron-fwaas-dashboard`` ausgelagert. Sie müssen " +"``neutron-fwaas-dashboard`` nach dem Upgrade des Dashboards zu Pike, und " +"eine ``enabled`` Datei für das Neutron FWaaS Dashboard anlegen. Weitere " +"Informationen finden Sie unter https://docs.openstack.org/neutron-fwaas-" +"dashboard/latest/." + +msgid "" +"Neutron FWaaS support in the OpenStack Dashboard is now split out into a " +"separate python package." +msgstr "" +"Neutron VPNaaS Unterstützung im OpenStack Dashboard wurde in ein separates " +"Python-Paket ausgelagert." + +msgid "New Features" +msgstr "Neue Funktionen" + +msgid "Pike Series Release Notes" +msgstr "Pike Serie Release Notes" + +msgid "Prelude" +msgstr "Einleitung" + +msgid "" +"Python 2.7 support has been dropped. Last release of neutron-fwaas dashboard " +"to support py2.7 is OpenStack Train. The minimum version of Python now " +"supported by neutron-fwaas-dashboard is Python 3.6." +msgstr "" +"Python 2.7 Unterstützung wurde beendet. Der letzte Release von neutron-fwaas-" +"dashboard welcher Python 2.7 unterstützt ist OpenStack Train. Die minimal " +"Python Version welche von neutron-fwaas-dashboard unterstützt wird, ist " +"Python 3.6." + +msgid "Queens Series Release Notes" +msgstr "Queens Serie Releasenotes" + +msgid "Rocky Series Release Notes" +msgstr "Rocky Serie Releasenotes" + +msgid "Stein Series Release Notes" +msgstr "Stein Serie Releasenotes" + +msgid "Train Series Release Notes" +msgstr "Train Serie Releasenotes" + +msgid "Upgrade Notes" +msgstr "Aktualisierungsnotizen" + +msgid "Ussuri Series Release Notes" +msgstr "Ussuri Serie Releasenotes" diff --git a/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po b/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po new file mode 100644 index 0000000..215023e --- /dev/null +++ b/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po @@ -0,0 +1,170 @@ +# Andi Chandler , 2017. #zanata +# Andi Chandler , 2018. #zanata +# Andi Chandler , 2019. #zanata +# Andi Chandler , 2020. #zanata +msgid "" +msgstr "" +"Project-Id-Version: neutron-fwaas-dashboard\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-05-02 03:38+0000\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"PO-Revision-Date: 2020-04-27 08:03+0000\n" +"Last-Translator: Andi Chandler \n" +"Language-Team: English (United Kingdom)\n" +"Language: en_GB\n" +"X-Generator: Zanata 4.3.3\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" + +msgid "" +"\"Add Router\" and \"Remove Router\" operations are now controlled by " +"``update_firewall`` rule in the policy.json file. Previously they were " +"controlled by ``get_firewall`` rule and non-existing rules like " +"``add_router`` and ``remove_router``. If operators define their custom " +"policy rules for ``add_router`` and ``remove_router``, such policy rules " +"need to updated accordingly. [`bug 1703952 `__]" +msgstr "" +"\"Add Router\" and \"Remove Router\" operations are now controlled by " +"``update_firewall`` rule in the policy.json file. Previously they were " +"controlled by ``get_firewall`` rule and non-existing rules like " +"``add_router`` and ``remove_router``. If operators define their custom " +"policy rules for ``add_router`` and ``remove_router``, such policy rules " +"need to updated accordingly. [`bug 1703952 `__]" + +msgid "0.1.0" +msgstr "0.1.0" + +msgid "1.1.0" +msgstr "1.1.0" + +msgid "2.0.0" +msgstr "2.0.0" + +msgid "3.0.0.0rc1" +msgstr "3.0.0.0rc1" + +msgid "" +"A panel for FWaaS v2 is newly added. There is no specific installation " +"process. After installing the new release of neutron-fwaas-dashboard, if " +"FWaaS v2 API is available on your neutron deployment, the panel for FWaaS v2 " +"will be displayed." +msgstr "" +"A panel for FWaaS v2 is newly added. There is no specific installation " +"process. After installing the new release of neutron-fwaas-dashboard, if " +"FWaaS v2 API is available on your Neutron deployment, the panel for FWaaS v2 " +"will be displayed." + +msgid "Bug Fixes" +msgstr "Bug Fixes" + +msgid "Current Series Release Notes" +msgstr "Current Series Release Notes" + +msgid "Deprecation Notes" +msgstr "Deprecation Notes" + +msgid "" +"Due to lack of maintainers neutron-fwaas-dashboard project is now deprecated " +"in the Neutron stadium. There is no planned releases of this project in the " +"``Victoria`` cycle. In ``W`` cycle project will be moved out from the " +"stadium to the unofficial OpenStack projects. If You want to step in and be " +"maintainer of this project to keep it in the Neutron stadium, please contact " +"the ``neutron team`` via openstack-discuss@lists.openstack.org or IRC " +"channel #openstack-neutron @freenode." +msgstr "" +"Due to lack of maintainers neutron-fwaas-dashboard project is now deprecated " +"in the Neutron stadium. There is no planned releases of this project in the " +"``Victoria`` cycle. In ``W`` cycle project will be moved out from the " +"stadium to the unofficial OpenStack projects. If You want to step in and be " +"maintainer of this project to keep it in the Neutron stadium, please contact " +"the ``neutron team`` via openstack-discuss@lists.openstack.org or IRC " +"channel #openstack-neutron @freenode." + +msgid "" +"FWaaS v1 support was dropped. FWaaS v1 has been deprecated in neutron-fwaas " +"and was dropped in Stein release. Along with neutron-fwaas, neutron-fwaas-" +"dashboard dropped its support." +msgstr "" +"FWaaS v1 support was dropped. FWaaS v1 has been deprecated in neutron-fwaas " +"and was dropped in Stein release. Along with neutron-fwaas, neutron-fwaas-" +"dashboard dropped its support." + +msgid "" +"Handling a shared rule or policy is now controlled by the policy mechanism. " +"Only admin users (by default) can set ``shared`` attribute when creating or " +"updating a firewall rule or policy. The checkbox on ``shared`` attribute is " +"now disabled if a user has no sufficient permission. [`bug 1699717 `__]" +msgstr "" +"Handling a shared rule or policy is now controlled by the policy mechanism. " +"Only admin users (by default) can set ``shared`` attribute when creating or " +"updating a firewall rule or policy. The checkbox on ``shared`` attribute is " +"now disabled if a user has no sufficient permission. [`bug 1699717 `__]" + +msgid "Neutron FWaaS Dashboard Release Notes" +msgstr "Neutron FWaaS Dashboard Release Notes" + +msgid "" +"Neutron FWaaS support in the OpenStack Dashboard is now split out into a " +"separate package ``neutron-fwaas-dashboard``. You need to install ``neutron-" +"fwaas-dashboard`` after upgrading the OpenStack Dashboard to Pike release " +"and add ``enabled`` file for Neutron FWaaS dashboard. For detail " +"information, see https://docs.openstack.org/neutron-fwaas-dashboard/latest/." +msgstr "" +"Neutron FWaaS support in the OpenStack Dashboard is now split out into a " +"separate package ``neutron-fwaas-dashboard``. You need to install ``neutron-" +"fwaas-dashboard`` after upgrading the OpenStack Dashboard to Pike release " +"and add ``enabled`` file for Neutron FWaaS dashboard. For detail " +"information, see https://docs.openstack.org/neutron-fwaas-dashboard/latest/." + +msgid "" +"Neutron FWaaS support in the OpenStack Dashboard is now split out into a " +"separate python package." +msgstr "" +"Neutron FWaaS support in the OpenStack Dashboard is now split out into a " +"separate python package." + +msgid "" +"Neutron-fwaas-dashboard project is now deprecated in the Neutron stadium." +msgstr "" +"Neutron-fwaas-dashboard project is now deprecated in the Neutron stadium." + +msgid "New Features" +msgstr "New Features" + +msgid "Pike Series Release Notes" +msgstr "Pike Series Release Notes" + +msgid "Prelude" +msgstr "Prelude" + +msgid "" +"Python 2.7 support has been dropped. Last release of neutron-fwaas dashboard " +"to support py2.7 is OpenStack Train. The minimum version of Python now " +"supported by neutron-fwaas-dashboard is Python 3.6." +msgstr "" +"Python 2.7 support has been dropped. Last release of Neutron-fwaas dashboard " +"to support Python 2.7 is OpenStack Train. The minimum version of Python now " +"supported by Neutron-fwaas-dashboard is Python 3.6." + +msgid "Queens Series Release Notes" +msgstr "Queens Series Release Notes" + +msgid "Rocky Series Release Notes" +msgstr "Rocky Series Release Notes" + +msgid "Stein Series Release Notes" +msgstr "Stein Series Release Notes" + +msgid "Train Series Release Notes" +msgstr "Train Series Release Notes" + +msgid "Upgrade Notes" +msgstr "Upgrade Notes" + +msgid "Ussuri Series Release Notes" +msgstr "Ussuri Series Release Notes" diff --git a/releasenotes/source/locale/es/LC_MESSAGES/releasenotes.po b/releasenotes/source/locale/es/LC_MESSAGES/releasenotes.po new file mode 100644 index 0000000..9c99e95 --- /dev/null +++ b/releasenotes/source/locale/es/LC_MESSAGES/releasenotes.po @@ -0,0 +1,25 @@ +# Jose Porrua , 2016. #zanata +# Eduardo Gonzalez Gutierrez , 2017. #zanata +msgid "" +msgstr "" +"Project-Id-Version: neutron-fwaas-dashboard\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-04-22 07:50+0000\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"PO-Revision-Date: 2017-09-27 03:49+0000\n" +"Last-Translator: Eduardo Gonzalez Gutierrez \n" +"Language-Team: Spanish\n" +"Language: es\n" +"X-Generator: Zanata 4.3.3\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" + +msgid "Bug Fixes" +msgstr "Corrección de errores" + +msgid "Current Series Release Notes" +msgstr "Notas de la versión actual" + +msgid "New Features" +msgstr "Nuevas Funcionalidades" diff --git a/releasenotes/source/locale/fr/LC_MESSAGES/releasenotes.po b/releasenotes/source/locale/fr/LC_MESSAGES/releasenotes.po new file mode 100644 index 0000000..cb4ef53 --- /dev/null +++ b/releasenotes/source/locale/fr/LC_MESSAGES/releasenotes.po @@ -0,0 +1,32 @@ +# Gérald LONLAS , 2016. #zanata +# Gaelle , 2017. #zanata +# Loic Nicolle , 2017. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Neutron FWaaS Dashboard Release Notes\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-01-28 19:29+0000\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"PO-Revision-Date: 2017-11-14 07:50+0000\n" +"Last-Translator: Gaelle \n" +"Language-Team: French\n" +"Language: fr\n" +"X-Generator: Zanata 3.9.6\n" +"Plural-Forms: nplurals=2; plural=(n > 1)\n" + +msgid "0.1.0" +msgstr "0.1.0" + +msgid "Bug Fixes" +msgstr "Corrections de bugs" + +msgid "Current Series Release Notes" +msgstr "Notes sur la Release Actuelle" + +msgid "New Features" +msgstr "Nouvelles fonctionnalités" + +msgid "Pike Series Release Notes" +msgstr "Note de release pour Pike" diff --git a/releasenotes/source/locale/id/LC_MESSAGES/releasenotes.po b/releasenotes/source/locale/id/LC_MESSAGES/releasenotes.po new file mode 100644 index 0000000..85a7c5e --- /dev/null +++ b/releasenotes/source/locale/id/LC_MESSAGES/releasenotes.po @@ -0,0 +1,127 @@ +# suhartono , 2017. #zanata +# suhartono , 2018. #zanata +# suhartono , 2019. #zanata +msgid "" +msgstr "" +"Project-Id-Version: neutron-fwaas-dashboard\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-04-09 08:56+0000\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"PO-Revision-Date: 2019-04-08 05:30+0000\n" +"Last-Translator: suhartono \n" +"Language-Team: Indonesian\n" +"Language: id\n" +"X-Generator: Zanata 4.3.3\n" +"Plural-Forms: nplurals=1; plural=0\n" + +msgid "" +"\"Add Router\" and \"Remove Router\" operations are now controlled by " +"``update_firewall`` rule in the policy.json file. Previously they were " +"controlled by ``get_firewall`` rule and non-existing rules like " +"``add_router`` and ``remove_router``. If operators define their custom " +"policy rules for ``add_router`` and ``remove_router``, such policy rules " +"need to updated accordingly. [`bug 1703952 `__]" +msgstr "" +"Operasi \"Add Router\" dan \"Remove Router\" sekarang dikendalikan oleh " +"aturan ``update_firewall`` dalam file policy.json. Sebelumnya mereka " +"dikontrol oleh aturan ``get_firewall`` dan aturan non-existing seperti " +"``add_router`` dan ``remove_router``. Jika operator menentukan aturan " +"kebijakan kustom mereka untuk ``add_router`` dan ``remove_router``, aturan " +"kebijakan tersebut perlu diperbarui sesuai. [`bug 1703952 ` __]" + +msgid "0.1.0" +msgstr "0.1.0" + +msgid "1.1.0" +msgstr "1.1.0" + +msgid "2.0.0" +msgstr "2.0.0" + +msgid "" +"A panel for FWaaS v2 is newly added. There is no specific installation " +"process. After installing the new release of neutron-fwaas-dashboard, if " +"FWaaS v2 API is available on your neutron deployment, the panel for FWaaS v2 " +"will be displayed." +msgstr "" +"Panel untuk FWaaS v2 baru ditambahkan. Tidak ada proses instalasi khusus. " +"Setelah menginstal rilis baru neutron-fwaas-dashboard, dan jika FWaaS v2 API " +"tersedia pada penyebaran neutron Anda, maka panel untuk FWaaS v2 akan " +"ditampilkan." + +msgid "Bug Fixes" +msgstr "Perbaikan bug" + +msgid "Current Series Release Notes" +msgstr "Catatan rilis seri saat ini" + +msgid "" +"FWaaS v1 support was dropped. FWaaS v1 has been deprecated in neutron-fwaas " +"and was dropped in Stein release. Along with neutron-fwaas, neutron-fwaas-" +"dashboard dropped its support." +msgstr "" +"Dukungan FWaaS v1 dijatuhkan (dropped). FWaaS v1 telah ditinggalkan dalam " +"neutron-fwaas dan dijatuhkan dalam rilis Stein. Seiring dengan neutron-" +"fwaas, neutron-fwaas-dashboard menjatuhkan dukungannya." + +msgid "" +"Handling a shared rule or policy is now controlled by the policy mechanism. " +"Only admin users (by default) can set ``shared`` attribute when creating or " +"updating a firewall rule or policy. The checkbox on ``shared`` attribute is " +"now disabled if a user has no sufficient permission. [`bug 1699717 `__]" +msgstr "" +"Menangani aturan atau kebijakan bersama sekarang dikendalikan oleh mekanisme " +"kebijakan. Hanya pengguna admin (secara default) yang dapat mengatur atribut " +"``shared`` saat membuat atau memperbarui aturan atau kebijakan firewall. " +"Kotak centang pada atribut ``shared`` sekarang dinonaktifkan jika pengguna " +"tidak memiliki izin yang memadai. [`bug 1699717 ` __]" + +msgid "Neutron FWaaS Dashboard Release Notes" +msgstr "Catatan Rilis Dashboard FWaaS Neutron" + +msgid "" +"Neutron FWaaS support in the OpenStack Dashboard is now split out into a " +"separate package ``neutron-fwaas-dashboard``. You need to install ``neutron-" +"fwaas-dashboard`` after upgrading the OpenStack Dashboard to Pike release " +"and add ``enabled`` file for Neutron FWaaS dashboard. For detail " +"information, see https://docs.openstack.org/neutron-fwaas-dashboard/latest/." +msgstr "" +"Dukungan Neutron FWaaS di Dasbor OpenStack kini dipisah menjadi paket " +"terpisah ``neutron-fwaas-dashboard``. Anda perlu menginstal ``neutron-fwaas-" +"dashboard`` setelah memutakhirkan OpenStack Dashboard ke rilis Pike dan " +"menambahkan ``enabled`` file untuk dasbor Neutron FWaaS. Untuk informasi " +"detail, lihat https://docs.openstack.org/neutron-fwaas-dashboard/latest/." + +msgid "" +"Neutron FWaaS support in the OpenStack Dashboard is now split out into a " +"separate python package." +msgstr "" +"Dukungan Neutron FWaaS di Dasbor OpenStack kini dipisah menjadi paket python " +"terpisah." + +msgid "New Features" +msgstr "Fitur baru" + +msgid "Pike Series Release Notes" +msgstr "Catatan Rilis Seri Pike" + +msgid "Prelude" +msgstr "Prelude" + +msgid "Queens Series Release Notes" +msgstr "Catatan Rilis Seri Queens" + +msgid "Rocky Series Release Notes" +msgstr "Catatan Rilis Seri Rocky" + +msgid "Stein Series Release Notes" +msgstr "Catatan Rilis Seri Stein" + +msgid "Upgrade Notes" +msgstr "Catatan Upgrade" diff --git a/releasenotes/source/locale/ja/LC_MESSAGES/releasenotes.po b/releasenotes/source/locale/ja/LC_MESSAGES/releasenotes.po new file mode 100644 index 0000000..9259b15 --- /dev/null +++ b/releasenotes/source/locale/ja/LC_MESSAGES/releasenotes.po @@ -0,0 +1,76 @@ +# Akihiro Motoki , 2017. #zanata +# Hidekazu Nakamura , 2017. #zanata +# Yuko Fukuda , 2017. #zanata +# Akihiro Motoki , 2018. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Neutron FWaaS Dashboard Release Notes\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-02-07 04:59+0000\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"PO-Revision-Date: 2018-02-06 09:35+0000\n" +"Last-Translator: Akihiro Motoki \n" +"Language-Team: Japanese\n" +"Language: ja\n" +"X-Generator: Zanata 3.9.6\n" +"Plural-Forms: nplurals=1; plural=0\n" + +msgid "0.1.0" +msgstr "0.1.0" + +msgid "1.1.0" +msgstr "1.1.0" + +msgid "" +"A panel for FWaaS v2 is newly added. There is no specific installation " +"process. After installing the new release of neutron-fwaas-dashboard, if " +"FWaaS v2 API is available on your neutron deployment, the panel for FWaaS v2 " +"will be displayed." +msgstr "" +"FWaaS v2 用のパネルが新たに追加されました。 neutron-fwaas-dashboard の新リ" +"リースをインストールすると、お使いの環境の neutron で FWaaS v2 API が利用可能" +"であれば、 FWaaS v2 用のパネルが表示されます、" + +msgid "Bug Fixes" +msgstr "バグ修正" + +msgid "Current Series Release Notes" +msgstr "開発中バージョンのリリースノート" + +msgid "Neutron FWaaS Dashboard Release Notes" +msgstr "Neutron FWaaS Dashboard リリースノート" + +msgid "" +"Neutron FWaaS support in the OpenStack Dashboard is now split out into a " +"separate package ``neutron-fwaas-dashboard``. You need to install ``neutron-" +"fwaas-dashboard`` after upgrading the OpenStack Dashboard to Pike release " +"and add ``enabled`` file for Neutron FWaaS dashboard. For detail " +"information, see https://docs.openstack.org/neutron-fwaas-dashboard/latest/." +msgstr "" +"OpenStack Dashboard の Neutron FWaaS 対応は、別のパッケージ ``neutron-fwaas-" +"dashboard`` として分離されました。 OpenStack Dashboard を Pike リリースにアッ" +"プグレードした後で、 ``neutron-fwaas-dashboard`` をインストールし、 Neutron " +"FWaaS ダッシュボード用の ``enabled`` ファイルを追加する必要があります。詳しく" +"は https://docs.openstack.org/neutron-fwaas-dashboard/latest/ を参照してくだ" +"さい。" + +msgid "" +"Neutron FWaaS support in the OpenStack Dashboard is now split out into a " +"separate python package." +msgstr "" +"OpenStack Dashboard の Neutron FWaaS 対応は、別の Python パッケージに分離され" +"ました。" + +msgid "New Features" +msgstr "新機能" + +msgid "Pike Series Release Notes" +msgstr "Pike バージョンのリリースノート" + +msgid "Prelude" +msgstr "前置" + +msgid "Queens Series Release Notes" +msgstr "Queens バージョンのリリースノート" diff --git a/releasenotes/source/locale/ko_KR/LC_MESSAGES/releasenotes.po b/releasenotes/source/locale/ko_KR/LC_MESSAGES/releasenotes.po new file mode 100644 index 0000000..1185625 --- /dev/null +++ b/releasenotes/source/locale/ko_KR/LC_MESSAGES/releasenotes.po @@ -0,0 +1,39 @@ +# ByungYeol Woo , 2017. #zanata +# SEOKJAE BARK , 2017. #zanata +# Sungjin Kang , 2017. #zanata +# minwook-shin , 2017. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Neutron FWaaS Dashboard Release Notes\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-02-22 06:54+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-27 02:31+0000\n" +"Last-Translator: ByungYeol Woo \n" +"Language-Team: Korean (South Korea)\n" +"Language: ko_KR\n" +"X-Generator: Zanata 4.3.3\n" +"Plural-Forms: nplurals=1; plural=0\n" + +msgid "0.1.0" +msgstr "0.1.0" + +msgid "1.1.0" +msgstr "1.1.0" + +msgid "Bug Fixes" +msgstr "버그 수정" + +msgid "Current Series Release Notes" +msgstr "최신 시리즈에 대한 릴리즈 노트" + +msgid "New Features" +msgstr "새로운 기능" + +msgid "Pike Series Release Notes" +msgstr "Pike 시리즈 릴리즈 노트" + +msgid "Prelude" +msgstr "서문" diff --git a/releasenotes/source/locale/pt_BR/LC_MESSAGES/releasenotes.po b/releasenotes/source/locale/pt_BR/LC_MESSAGES/releasenotes.po new file mode 100644 index 0000000..fa2b99b --- /dev/null +++ b/releasenotes/source/locale/pt_BR/LC_MESSAGES/releasenotes.po @@ -0,0 +1,30 @@ +# Rodrigo Loures , 2018. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Neutron FWaaS Dashboard Release Notes\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-02-22 06:54+0000\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"PO-Revision-Date: 2018-01-25 10:14+0000\n" +"Last-Translator: Rodrigo Loures \n" +"Language-Team: Portuguese (Brazil)\n" +"Language: pt_BR\n" +"X-Generator: Zanata 4.3.3\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" + +msgid "Bug Fixes" +msgstr "Correção de erros" + +msgid "Current Series Release Notes" +msgstr "Atual - Séries de Notas de Versão" + +msgid "New Features" +msgstr "Novos recursos" + +msgid "Pike Series Release Notes" +msgstr "Pike - Série de Notas de Versão" + +msgid "Prelude" +msgstr "Prelúdio" diff --git a/releasenotes/source/locale/ru/LC_MESSAGES/releasenotes.po b/releasenotes/source/locale/ru/LC_MESSAGES/releasenotes.po new file mode 100644 index 0000000..7a35f96 --- /dev/null +++ b/releasenotes/source/locale/ru/LC_MESSAGES/releasenotes.po @@ -0,0 +1,170 @@ +# Alexander , 2016. #zanata +# Artem , 2017. #zanata +# Roman Gorshunov , 2019. #zanata +# Roman Gorshunov , 2020. #zanata +msgid "" +msgstr "" +"Project-Id-Version: neutron-fwaas-dashboard\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-04-08 17:28+0000\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"PO-Revision-Date: 2020-04-03 11:59+0000\n" +"Last-Translator: Roman Gorshunov \n" +"Language-Team: Russian\n" +"Language: ru\n" +"X-Generator: Zanata 4.3.3\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" + +msgid "" +"\"Add Router\" and \"Remove Router\" operations are now controlled by " +"``update_firewall`` rule in the policy.json file. Previously they were " +"controlled by ``get_firewall`` rule and non-existing rules like " +"``add_router`` and ``remove_router``. If operators define their custom " +"policy rules for ``add_router`` and ``remove_router``, such policy rules " +"need to updated accordingly. [`bug 1703952 `__]" +msgstr "" +"Операции \"Добавить роутер\" и \"Удалить роутер\" (\"Add Router\" и \"Remove " +"Router\") теперь управляются правилом ``update_firewall`` в файле policy." +"json. Ранее они управлялись правилом ``get_firewall`` и не существующими " +"ныне правилами, такими как ``add_router`` и ``remove_router``. Если " +"операторы определили свои собственные правила ``add_router`` и " +"``remove_router``, то правила этих политик должны быть обновлены " +"соответствующим образом. [`bug 1703952 `__]" + +msgid "0.1.0" +msgstr "0.1.0" + +msgid "1.1.0" +msgstr "1.1.0" + +msgid "2.0.0" +msgstr "2.0.0" + +msgid "" +"A panel for FWaaS v2 is newly added. There is no specific installation " +"process. After installing the new release of neutron-fwaas-dashboard, if " +"FWaaS v2 API is available on your neutron deployment, the panel for FWaaS v2 " +"will be displayed." +msgstr "" +"Была добавлена новая панель для FWaaS v2. Какого то специального процесса её " +"установки нет. В случае, если FWaaS v2 API доступен в вашей инсталляции " +"Neutron, панель управления FWaaS v2 появится после установки нового релиза " +"панели neutron-fwaas-dashboard." + +msgid "Bug Fixes" +msgstr "Исправленные ошибки" + +msgid "Current Series Release Notes" +msgstr "Примечания к текущему релизу" + +msgid "Deprecation Notes" +msgstr "Уведомление о прекращении поддержки" + +msgid "" +"Due to lack of maintainers neutron-fwaas-dashboard project is now deprecated " +"in the Neutron stadium. There is no planned releases of this project in the " +"``Victoria`` cycle. In ``W`` cycle project will be moved out from the " +"stadium to the unofficial OpenStack projects. If You want to step in and be " +"maintainer of this project to keep it in the Neutron stadium, please contact " +"the ``neutron team`` via openstack-discuss@lists.openstack.org or IRC " +"channel #openstack-neutron @freenode." +msgstr "" +"Ввиду отсутствия команды поддержки, проект neutron-fwaas-dashboard был " +"исключён из списка активно поддерживаемых проектов Neutron (Neutron " +"Stadium). Не планируется выпускать релиз этого проекта в цикле ``Victoria``. " +"В цикле ``W`` проект будет удалён из общего списка проектов и перемещён в " +"список неофициальных проектов OpenStack. Если вы хотите включиться в работу " +"и стать членом команды поддержки этого проекта, чтобы он остался в списке " +"активно поддерживаемых проектов Neutron (Neutron Stadium), то свяжитесь с " +"``neutron team`` посредством openstack-discuss@lists.openstack.org или на " +"IRC канале #openstack-neutron @freenode." + +msgid "" +"FWaaS v1 support was dropped. FWaaS v1 has been deprecated in neutron-fwaas " +"and was dropped in Stein release. Along with neutron-fwaas, neutron-fwaas-" +"dashboard dropped its support." +msgstr "" +"Поддержка FWaaS v1 быда прекращена. FWaaS v1 был помечен устаревшим в " +"neutron-fwaas и удалён в релизе Stein. Панель управления neutron-fwaas-" +"dashboard так же удалила его поддержку, вместе с neutron-fwaas." + +msgid "" +"Handling a shared rule or policy is now controlled by the policy mechanism. " +"Only admin users (by default) can set ``shared`` attribute when creating or " +"updating a firewall rule or policy. The checkbox on ``shared`` attribute is " +"now disabled if a user has no sufficient permission. [`bug 1699717 `__]" +msgstr "" +"Управление общими правилами или политиками теперь контроллируется механизмом " +"политик. Только администраторы (по умолчанию) могут устанавливать аттрибут " +"``общая`` (``shared``) при создании или обновлении правила межсетевого " +"экрана или политики. Флажок аттрибута ``общий`` (``shared``) теперь " +"отключен, если у пользователя нет достаточных привилегий. [`bug 1699717 " +"`__]" + +msgid "Neutron FWaaS Dashboard Release Notes" +msgstr "Примечания о панели Neutron FWaaS" + +msgid "" +"Neutron FWaaS support in the OpenStack Dashboard is now split out into a " +"separate package ``neutron-fwaas-dashboard``. You need to install ``neutron-" +"fwaas-dashboard`` after upgrading the OpenStack Dashboard to Pike release " +"and add ``enabled`` file for Neutron FWaaS dashboard. For detail " +"information, see https://docs.openstack.org/neutron-fwaas-dashboard/latest/." +msgstr "" +"Поддержка Neutron FWaaS в панели управления OpenStack теперь выделена в " +"отдельный Python модуль ``neutron-fwaas-dashboard``. При обновлении панели " +"управления OpenStack до релиза Pike, вам потребуется установить модуль " +"``neutron-fwaas-dashboard`` и добавить файл ``enabled`` для панели " +"управления Neutron FWaaS. Дополнительную информацию можно получить на " +"странице документации тут: https://docs.openstack.org/neutron-fwaas-" +"dashboard/latest/." + +msgid "" +"Neutron FWaaS support in the OpenStack Dashboard is now split out into a " +"separate python package." +msgstr "" +"Поддержка Neutron FWaaS в панели управления OpenStack теперь выделена в " +"отдельный Python модуль." + +msgid "" +"Neutron-fwaas-dashboard project is now deprecated in the Neutron stadium." +msgstr "Проект neutron-fwaas-dashboard был исключён из Neutron Stadium." + +msgid "New Features" +msgstr "Новые особенности" + +msgid "Pike Series Release Notes" +msgstr "Примечания к релизу Pike" + +msgid "Prelude" +msgstr "Введение" + +msgid "" +"Python 2.7 support has been dropped. Last release of neutron-fwaas dashboard " +"to support py2.7 is OpenStack Train. The minimum version of Python now " +"supported by neutron-fwaas-dashboard is Python 3.6." +msgstr "" +"Поддержка Python 2.7 более не производится. Последним релизом neutron-fwaas " +"dashboard, поддерживавшим py2.7 был OpenStack Train. Минимальная " +"поддерживаемая neutron-fwaas-dashboard версия – Python 3.6." + +msgid "Queens Series Release Notes" +msgstr "Примечания к релизу Queens" + +msgid "Rocky Series Release Notes" +msgstr "Примечания к релизу Rocky" + +msgid "Stein Series Release Notes" +msgstr "Примечания к релизу Stein" + +msgid "Train Series Release Notes" +msgstr "Примечания к релизу Train" + +msgid "Upgrade Notes" +msgstr "Информация об обновлении" diff --git a/releasenotes/source/locale/zh_CN/LC_MESSAGES/releasenotes.po b/releasenotes/source/locale/zh_CN/LC_MESSAGES/releasenotes.po new file mode 100644 index 0000000..cab67c9 --- /dev/null +++ b/releasenotes/source/locale/zh_CN/LC_MESSAGES/releasenotes.po @@ -0,0 +1,34 @@ +# Bin , 2017. #zanata +# TigerFang , 2017. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Neutron FWaaS Dashboard Release Notes\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-02-22 06:54+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-28 02:43+0000\n" +"Last-Translator: TigerFang \n" +"Language-Team: Chinese (China)\n" +"Language: zh_CN\n" +"X-Generator: Zanata 4.3.3\n" +"Plural-Forms: nplurals=1; plural=0\n" + +msgid "0.1.0" +msgstr "0.1.0" + +msgid "Bug Fixes" +msgstr "修复的问题" + +msgid "Current Series Release Notes" +msgstr "当前版本发布声明" + +msgid "New Features" +msgstr "新功能" + +msgid "Pike Series Release Notes" +msgstr "Pike版本发布声明" + +msgid "Prelude" +msgstr "前奏" diff --git a/releasenotes/source/pike.rst b/releasenotes/source/pike.rst new file mode 100644 index 0000000..e43bfc0 --- /dev/null +++ b/releasenotes/source/pike.rst @@ -0,0 +1,6 @@ +=================================== + Pike Series Release Notes +=================================== + +.. release-notes:: + :branch: stable/pike diff --git a/releasenotes/source/queens.rst b/releasenotes/source/queens.rst new file mode 100644 index 0000000..36ac616 --- /dev/null +++ b/releasenotes/source/queens.rst @@ -0,0 +1,6 @@ +=================================== + Queens Series Release Notes +=================================== + +.. release-notes:: + :branch: stable/queens diff --git a/releasenotes/source/rocky.rst b/releasenotes/source/rocky.rst new file mode 100644 index 0000000..40dd517 --- /dev/null +++ b/releasenotes/source/rocky.rst @@ -0,0 +1,6 @@ +=================================== + Rocky Series Release Notes +=================================== + +.. release-notes:: + :branch: stable/rocky diff --git a/releasenotes/source/stein.rst b/releasenotes/source/stein.rst new file mode 100644 index 0000000..efaceb6 --- /dev/null +++ b/releasenotes/source/stein.rst @@ -0,0 +1,6 @@ +=================================== + Stein Series Release Notes +=================================== + +.. release-notes:: + :branch: stable/stein diff --git a/releasenotes/source/train.rst b/releasenotes/source/train.rst new file mode 100644 index 0000000..5839003 --- /dev/null +++ b/releasenotes/source/train.rst @@ -0,0 +1,6 @@ +========================== +Train Series Release Notes +========================== + +.. release-notes:: + :branch: stable/train diff --git a/releasenotes/source/unreleased.rst b/releasenotes/source/unreleased.rst new file mode 100644 index 0000000..875030f --- /dev/null +++ b/releasenotes/source/unreleased.rst @@ -0,0 +1,5 @@ +============================ +Current Series Release Notes +============================ + +.. release-notes:: diff --git a/releasenotes/source/ussuri.rst b/releasenotes/source/ussuri.rst new file mode 100644 index 0000000..e21e50e --- /dev/null +++ b/releasenotes/source/ussuri.rst @@ -0,0 +1,6 @@ +=========================== +Ussuri Series Release Notes +=========================== + +.. release-notes:: + :branch: stable/ussuri diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..312c39b --- /dev/null +++ b/requirements.txt @@ -0,0 +1,7 @@ +# The order of packages is significant, because pip processes them in the order +# of appearance. Changing the order has an impact on the overall integration +# process, which may cause wedges in the gate later. + +pbr!=2.1.0,>=2.0.0 # Apache-2.0 +horizon>=17.1.0 # Apache-2.0 +python-neutronclient>=6.7.0 # Apache-2.0 diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..6260640 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,26 @@ +[metadata] +name = neutron-fwaas-dashboard +summary = Neutron FWaaS Dashboard +description-file = + README.rst +author = OpenStack +author-email = openstack-discuss@lists.openstack.org +home-page = https://docs.openstack.org/neutron-fwaas-dashboard/latest/ +python-requires = >=3.6 +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 :: Implementation :: CPython + Programming Language :: Python :: 3 :: Only + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + +[files] +packages = + neutron_fwaas_dashboard diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..cd35c3c --- /dev/null +++ b/setup.py @@ -0,0 +1,20 @@ +# 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. + +import setuptools + +setuptools.setup( + setup_requires=['pbr>=2.0.0'], + pbr=True) diff --git a/test-requirements.txt b/test-requirements.txt new file mode 100644 index 0000000..890f8dd --- /dev/null +++ b/test-requirements.txt @@ -0,0 +1,17 @@ +# 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 should appear first in case something else depends on pep8 +hacking>=3.0.1,<3.1.0 # Apache-2.0 + +coverage!=4.4,>=4.0 # Apache-2.0 +flake8-import-order==0.13 # LGPLv3 +sphinx>=2.0.0,!=2.1.0 # BSD +openstackdocstheme>=2.2.1 # Apache-2.0 +reno>=3.1.0 # Apache-2.0 + +# integration tests requirements +selenium>=2.50.1 # Apache-2.0 +xvfbwrapper>=0.1.3 #license: MIT +testtools>=2.2.0 # MIT diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..6871c98 --- /dev/null +++ b/tox.ini @@ -0,0 +1,92 @@ +[tox] +envlist = py38,pep8 +minversion = 2.3.2 +skipsdist = True + +[testenv] +usedevelop = True +basepython = python3 +setenv = VIRTUAL_ENV={envdir} + NOSE_WITH_OPENSTACK=1 + NOSE_OPENSTACK_COLOR=1 + NOSE_OPENSTACK_RED=0.05 + NOSE_OPENSTACK_YELLOW=0.025 + NOSE_OPENSTACK_SHOW_ELAPSED=1 +deps = + -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} + -r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt +commands = + {[unit_tests]commands} + +[unit_tests] +commands = python manage.py test {posargs} --settings=neutron_fwaas_dashboard.test.settings --exclude-tag integration + +[testenv:pep8] +commands = + flake8 {posargs} + {envpython} {toxinidir}/manage.py extract_messages --module neutron_fwaas_dashboard --verbosity 0 --check-only + +[testenv:venv] +commands = {posargs} + +[testenv:cover] +commands = + coverage erase + coverage run {toxinidir}/manage.py test neutron_fwaas_dashboard --settings=neutron_fwaas_dashboard.test.settings --exclude-tag integration {posargs} + coverage xml --omit '.tox/cover/*' -o 'cover/coverage.xml' + coverage html --omit '.tox/cover/*' -d 'cover/htmlcov' + +[testenv:integration] +# Run integration tests only +passenv = AVCONV_INSTALLED +setenv = + PYTHONHASHSEED=0 + INTEGRATION_TESTS=1 + SELENIUM_HEADLESS=1 + HORIZON_INTEGRATION_TESTS_CONFIG_FILE=neutron_fwaas_dashboard/test/integration/horizon.conf +basepython = python3 +commands = {envpython} {toxinidir}/manage.py test neutron_fwaas_dashboard --tag integration {posargs} + +[testenv:docs] +commands = + sphinx-build -W -b html doc/source doc/build/html + +[testenv:pdf-docs] +envdir = {toxworkdir}/docs +whitelist_externals = + make +commands = + sphinx-build -W -b latex doc/source doc/build/pdf + make -C doc/build/pdf + +[testenv:releasenotes] +commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html + +[flake8] +exclude = .venv,.git,.tox,dist,*lib/python*,*egg,build,node_modules,.tmp +# Enable the following hacking rules which are disabled by default +# H203 Use assertIs(Not)None to check for None +# H904 Delay string interpolations at logging calls +enable-extensions=H203,H904 +# W504 line break after binary operator +# (W503 and W504 are incompatible and we need to choose one of them. +# Existing codes follows W503, so we disable W504.) +# F405 TEMPLATES may be undefined, or defined from star imports +# (because it is not easy to avoid this in openstack_dashboard.test.settings) +ignore = W504,F405 +max-complexity = 20 + +# flake8-import-order configurations +import-order-style = pep8 +application-import-names = neutron_fwaas_dashboard + +[flake8:local-plugins] +extension = + M322 = horizon.hacking.checks:no_mutable_default_args + +[testenv:lower-constraints] +deps = + -c{toxinidir}/lower-constraints.txt + -r{toxinidir}/test-requirements.txt + -r{toxinidir}/requirements.txt