diff --git a/.gitignore b/.gitignore index 3e20723ad..abdf7b022 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,6 @@ run_tests.err.log run_tests.log .autogenerated .coverage -.testrepository/ +.stestr/ .tox/ .venv/ diff --git a/.gitreview b/.gitreview index 994b07a43..faf5d1f5b 100644 --- a/.gitreview +++ b/.gitreview @@ -1,4 +1,4 @@ [gerrit] -host=review.openstack.org +host=review.opendev.org port=29418 project=openstack/python-neutronclient.git diff --git a/.pylintrc b/.pylintrc index bd677fd6a..7e4a0716c 100644 --- a/.pylintrc +++ b/.pylintrc @@ -20,7 +20,7 @@ variable-rgx=[a-z_][a-z0-9_]{0,30}$ argument-rgx=[a-z_][a-z0-9_]{1,30}$ # Method names should be at least 3 characters long -# and be lowecased with underscores +# and be lowercased with underscores method-rgx=([a-z_][a-z0-9_]{2,50}|setUp|tearDown)$ # Module names matching quantum-* are ok (files in bin/) diff --git a/.stestr.conf b/.stestr.conf new file mode 100644 index 000000000..875bb1461 --- /dev/null +++ b/.stestr.conf @@ -0,0 +1,3 @@ +[DEFAULT] +test_path=${OS_TEST_PATH:-./neutronclient/tests/unit} +top_dir=./ diff --git a/.testr.conf b/.testr.conf deleted file mode 100644 index 01dee7036..000000000 --- a/.testr.conf +++ /dev/null @@ -1,4 +0,0 @@ -[DEFAULT] -test_command=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 ${PYTHON:-python} -m subunit.run discover -t ./ ${OS_TEST_PATH:-./neutronclient/tests/unit} $LISTOPT $IDOPTION -test_id_option=--load-list $IDFILE -test_list_option=--list diff --git a/.zuul.yaml b/.zuul.yaml new file mode 100644 index 000000000..7c8822186 --- /dev/null +++ b/.zuul.yaml @@ -0,0 +1,39 @@ +- project: + templates: + - openstack-cover-jobs + - openstack-python3-jobs + - publish-openstack-docs-pti + - check-requirements + - lib-forward-testing-python3 + - release-notes-jobs-python3 + - openstackclient-plugin-jobs + experimental: + jobs: + - neutronclient-grenade-neutron-lib: + irrelevant-files: + - ^(test-|)requirements.txt$ + - ^setup.cfg$ + +- job: + name: neutronclient-grenade-neutron-lib + parent: grenade + description: | + neutron-lib grenade job. + The version of this job on the current branch is py3 based, + while any branch before ussuri needs to use the py2 version, + which is defined in openstack-zuul-jobs with the old name + (legacy-grenade-dsvm-neutron-libs). + Users of this job needs to pay attention of the version used. + Former names for this job were: + * legacy-grenade-dsvm-neutron-libs + * neutron-lib-grenade-dsvm + required-projects: + - openstack/keystoneauth + - openstack/neutron + - openstack/neutron-lib + - openstack/python-cinderclient + - openstack/python-glanceclient + - openstack/python-ironicclient + - openstack/python-keystoneclient + - openstack/python-neutronclient + - openstack/python-novaclient diff --git a/HACKING.rst b/HACKING.rst index b8b3ff96c..7230971af 100644 --- a/HACKING.rst +++ b/HACKING.rst @@ -2,7 +2,7 @@ Neutron Style Commandments ================================ - Step 1: Read the OpenStack Style Commandments - http://docs.openstack.org/developer/hacking/ + https://docs.openstack.org/hacking/latest/ - Step 2: Read on diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index c0f014e74..000000000 --- a/MANIFEST.in +++ /dev/null @@ -1,6 +0,0 @@ -include tox.ini -include LICENSE README.rst HACKING.rst -include AUTHORS -include ChangeLog -include tools/* -recursive-include tests * diff --git a/README.rst b/README.rst index cceea78f5..eaac15c2c 100644 --- a/README.rst +++ b/README.rst @@ -1,17 +1,28 @@ +======================== +Team and repository tags +======================== + +.. image:: https://governance.openstack.org/tc/badges/python-neutronclient.svg + :target: https://governance.openstack.org/tc/reference/tags/index.html + +.. Change things from this point on + Python bindings to the Neutron API ================================== .. image:: https://img.shields.io/pypi/v/python-neutronclient.svg - :target: https://pypi.python.org/pypi/python-neutronclient/ + :target: https://pypi.org/project/python-neutronclient/ :alt: Latest Version -.. image:: https://img.shields.io/pypi/dm/python-neutronclient.svg - :target: https://pypi.python.org/pypi/python-neutronclient/ - :alt: Downloads - This is a client library for Neutron built on the Neutron API. It -provides a Python API (the ``neutronclient`` module) and a command-line tool -(``neutron``). +provides a Python API (the ``neutronclient`` module). + +.. note:: This project has been deprecated. The CLI code has been deleted + and is not accessible anymore. The Python bindings are still in use by + other projects but no new features will be added to this project. + All projects under Openstack governance migrating to use OpenstackSDK. + Any new feature should be proposed to OpenStack SDK and OpenStack + Client. * License: Apache License, Version 2.0 * `PyPi`_ - package installation @@ -22,10 +33,11 @@ provides a Python API (the ``neutronclient`` module) and a command-line tool * `Source`_ * `Developer's Guide`_ -.. _PyPi: https://pypi.python.org/pypi/python-neutronclient -.. _Online Documentation: http://docs.openstack.org/developer/python-neutronclient +.. _PyPi: https://pypi.org/project/python-neutronclient +.. _Online Documentation: https://docs.openstack.org/python-neutronclient/latest/ .. _Launchpad project: https://launchpad.net/python-neutronclient .. _Blueprints: https://blueprints.launchpad.net/python-neutronclient .. _Bugs: https://bugs.launchpad.net/python-neutronclient -.. _Source: https://git.openstack.org/cgit/openstack/python-neutronclient +.. _Source: https://opendev.org/openstack/python-neutronclient .. _Developer's Guide: http://docs.openstack.org/infra/manual/developers.html +.. _Release Notes: https://docs.openstack.org/releasenotes/python-neutronclient diff --git a/babel.cfg b/babel.cfg deleted file mode 100644 index 15cd6cb76..000000000 --- a/babel.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[python: **.py] - diff --git a/doc/requirements.txt b/doc/requirements.txt new file mode 100644 index 000000000..718a2395a --- /dev/null +++ b/doc/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. +openstackdocstheme>=2.2.0 # Apache-2.0 +reno>=3.1.0 # Apache-2.0 +sphinx>=2.0.0,!=2.1.0 # BSD +cliff>=3.4.0 # Apache-2.0 diff --git a/doc/source/cli/index.rst b/doc/source/cli/index.rst new file mode 100644 index 000000000..4b1f4720a --- /dev/null +++ b/doc/source/cli/index.rst @@ -0,0 +1,55 @@ +.. + Licensed under the Apache License, Version 2.0 (the "License"); you may + not use this file except in compliance with the License. You may obtain + a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + License for the specific language governing permissions and limitations + under the License. + + + Convention for heading levels in Neutron devref: + ======= Heading 0 (reserved for the title in a document) + ------- Heading 1 + ~~~~~~~ Heading 2 + +++++++ Heading 3 + ''''''' Heading 4 + (Avoid deeper levels because they do not render well.) + +========= +Using CLI +========= + +There is `OpenStackClient (OSC) +`__ +which support the Networking API + +OpenStackClient +--------------- + +OpenStackClient provides +`the basic network commands `__ +and python-neutronclient provides :doc:`extensions ` +(aka OSC plugins) for advanced networking services. + +.. toctree:: + :maxdepth: 1 + + Basic network commands + Network commands for advanced networking services + Mapping Guide from neutron CLI + +neutron CLI +----------- + +.. warning:: + + neutron CLI is removed. Use openstack CLI instead. See `openstack CLI command list + `__ + and :doc:`its extensions for advanced networking services `. + The command mapping from neutron CLI to openstack CLI is available + `here `__. diff --git a/doc/source/cli/osc/v2/bgp-dynamic-routing.rst b/doc/source/cli/osc/v2/bgp-dynamic-routing.rst new file mode 100644 index 000000000..c86bb45ee --- /dev/null +++ b/doc/source/cli/osc/v2/bgp-dynamic-routing.rst @@ -0,0 +1,47 @@ +=================== +BGP Dynamic Routing +=================== + +BGP dynamic routing enables announcement of project subnet prefixes +via BGP. Admins create BGP speakers and BGP peers. BGP peers can be +associated with BGP speakers, thereby enabling peering sessions with +operator infrastructure. BGP speakers can be associated with networks, +which controls which routes are announced to peers. + +Network v2 + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: bgp speaker create + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: bgp speaker delete + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: bgp speaker list + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: bgp speaker set + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: bgp speaker show + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: bgp speaker add network + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: bgp speaker remove network + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: bgp speaker add peer + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: bgp speaker remove peer + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: bgp speaker list advertised routes + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: bgp peer * + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: bgp dragent * diff --git a/doc/source/cli/osc/v2/network-log.rst b/doc/source/cli/osc/v2/network-log.rst new file mode 100644 index 000000000..df3a32729 --- /dev/null +++ b/doc/source/cli/osc/v2/network-log.rst @@ -0,0 +1,15 @@ +=========== +network log +=========== + +A **network log** is a container to group security groups or ports for logging. +Specified resources can be logged via these event (``ALL``, ``ACCEPT`` or +``DROP``). + +Network v2 + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: network loggable resources list + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: network log * diff --git a/doc/source/cli/osc/v2/networking-sfc.rst b/doc/source/cli/osc/v2/networking-sfc.rst new file mode 100644 index 000000000..4f4242d74 --- /dev/null +++ b/doc/source/cli/osc/v2/networking-sfc.rst @@ -0,0 +1,39 @@ +============== +networking sfc +============== + +**Service Function Chaining** is a mechanism for overriding the basic destination based forwarding +that is typical of IP networks. Service Function Chains consist of an ordered sequence of +Service Functions (SFs). SFs are virtual machines (or potentially physical devices) that perform a +network function such as firewall, content cache, packet inspection, or any other function that +requires processing of packets in a flow from point A to point B even though the SFs are not +literally between point A and B from a routing table perspective. + +Network v2 + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: sfc flow classifier * + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: sfc port chain * + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: sfc port pair create + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: sfc port pair delete + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: sfc port pair list + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: sfc port pair set + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: sfc port pair show + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: sfc port pair group * + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: sfc service graph * diff --git a/doc/source/cli/osc/v2/subnet-onboard.rst b/doc/source/cli/osc/v2/subnet-onboard.rst new file mode 100644 index 000000000..006970372 --- /dev/null +++ b/doc/source/cli/osc/v2/subnet-onboard.rst @@ -0,0 +1,17 @@ +======================= +network onboard subnets +======================= + +**network onboard subnets** enables a subnet to be adopted or +"onboarded" into an existing subnet pool. The CIDR of the subnet +is checked for uniqueness across any applicable address scopes +and all subnets allocated from the target subnet pool. Once +onboarded, the subnet CIDR is added to the prefix list of the +subnet pool and the subnet appears as though it has been allocated +from the subnet pool. The subnet also begins participating in the +applicable address scope if the subnet pool belongs to one. + +Network v2 + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: network onboard subnets diff --git a/doc/source/cli/osc/v2/vpn-endpoint-group.rst b/doc/source/cli/osc/v2/vpn-endpoint-group.rst new file mode 100644 index 000000000..9581d8b4a --- /dev/null +++ b/doc/source/cli/osc/v2/vpn-endpoint-group.rst @@ -0,0 +1,11 @@ +================== +VPN Endpoint Group +================== + +The **Endpoint Group** is used to configure multiple local and remote subnets +in vpnservice object. + +Network v2 + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: vpn endpoint group * diff --git a/doc/source/cli/osc/v2/vpn-ike-policy.rst b/doc/source/cli/osc/v2/vpn-ike-policy.rst new file mode 100644 index 000000000..3e38a5c7c --- /dev/null +++ b/doc/source/cli/osc/v2/vpn-ike-policy.rst @@ -0,0 +1,12 @@ +============== +VPN IKE Policy +============== + +The **IKE Policy** is used for phases one and two negotiation of the +VPN connection. You can specify both the authentication and encryption +algorithms for connections. + +Network v2 + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: vpn ike policy * diff --git a/doc/source/cli/osc/v2/vpn-ipsec-policy.rst b/doc/source/cli/osc/v2/vpn-ipsec-policy.rst new file mode 100644 index 000000000..c8a5f3beb --- /dev/null +++ b/doc/source/cli/osc/v2/vpn-ipsec-policy.rst @@ -0,0 +1,11 @@ +================ +VPN IPsec Policy +================ + +The **IPsec Policy** specifies the authentication and encryption algorithms +and encapsulation mode to use for the established VPN connection. + +Network v2 + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: vpn ipsec policy * diff --git a/doc/source/cli/osc/v2/vpn-ipsec-site-connection.rst b/doc/source/cli/osc/v2/vpn-ipsec-site-connection.rst new file mode 100644 index 000000000..0244e73ee --- /dev/null +++ b/doc/source/cli/osc/v2/vpn-ipsec-site-connection.rst @@ -0,0 +1,10 @@ +========================= +VPN IPsec Site Connection +========================= + +Creates a site-to-site **IPsec Site Connection** for a VPN service. + +Network v2 + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: vpn ipsec site connection * diff --git a/doc/source/cli/osc/v2/vpn-service.rst b/doc/source/cli/osc/v2/vpn-service.rst new file mode 100644 index 000000000..b412555c8 --- /dev/null +++ b/doc/source/cli/osc/v2/vpn-service.rst @@ -0,0 +1,11 @@ +=========== +VPN Service +=========== + +The **VPN Service** is associated with a router. After you +create the service, it can contain multiple VPN connections. + +Network v2 + +.. autoprogram-cliff:: openstack.neutronclient.v2 + :command: vpn service * diff --git a/doc/source/cli/osc_plugins.rst b/doc/source/cli/osc_plugins.rst new file mode 100644 index 000000000..0cd7e26ba --- /dev/null +++ b/doc/source/cli/osc_plugins.rst @@ -0,0 +1,37 @@ +.. + Licensed under the Apache License, Version 2.0 (the "License"); you may + not use this file except in compliance with the License. You may obtain + a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + License for the specific language governing permissions and limitations + under the License. + + + Convention for heading levels in Neutron devref: + ======= Heading 0 (reserved for the title in a document) + ------- Heading 1 + ~~~~~~~ Heading 2 + +++++++ Heading 3 + ''''''' Heading 4 + (Avoid deeper levels because they do not render well.) + +Advanced Network Commands in OpenStack Client +============================================= + +The following list covers the extended commands for advanced network +services available in ``openstack`` command. + +These commands can be referenced by doing ``openstack help`` and +the detail of individual command can be referred by +``openstack help ``. + +.. toctree:: + :glob: + :maxdepth: 2 + + osc/v2/* diff --git a/doc/source/conf.py b/doc/source/conf.py index bc6dad770..70fd0de2f 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -1,17 +1,23 @@ # -*- coding: utf-8 -*- # -project = 'python-neutronclient' - # -- General configuration --------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', - 'oslosphinx', - 'reno.sphinxext', +extensions = [ + 'sphinx.ext.autodoc', + 'reno.sphinxext', + 'openstackdocstheme', + 'cliff.sphinxext', ] +# openstackdocstheme options +openstackdocs_repo_name = 'openstack/python-neutronclient' +openstackdocs_pdf_link = True +openstackdocs_bug_project = 'python-neutronclient' +openstackdocs_bug_tag = 'doc' + # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -22,7 +28,7 @@ master_doc = 'index' # General information about the project. -copyright = u'OpenStack Foundation' +copyright = 'OpenStack Foundation' # If true, '()' will be appended to :func: etc. cross-reference text. add_function_parentheses = True @@ -38,18 +44,30 @@ # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. -# html_theme = 'nature' +html_theme = 'openstackdocs' # Output file base name for HTML help builder. -htmlhelp_basename = '%sdoc' % project +htmlhelp_basename = 'neutronclientdoc' +# -- Options for LaTeX output ------------------------------------------------ -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, -# documentclass [howto/manual]). latex_documents = [ - ('index', - '%s.tex' % project, - u'%s Documentation' % project, - u'OpenStack Foundation', 'manual'), + ('index', 'doc-python-neutronclient.tex', + 'python-neutronclient Documentation', + 'Neutron Contributors', 'manual'), ] + +# Disable usage of xindy https://bugzilla.redhat.com/show_bug.cgi?id=1643664 +latex_use_xindy = False + +latex_domain_indices = False + +latex_elements = { + 'makeindex': '', + 'printindex': '', + 'preamble': r'\setcounter{tocdepth}{5}', +} + +# -- Options for cliff.sphinxext plugin --------------------------------------- + +autoprogram_cliff_application = 'openstack' diff --git a/doc/source/usage/osc_cli_plugins.rst b/doc/source/contributor/index.rst similarity index 78% rename from doc/source/usage/osc_cli_plugins.rst rename to doc/source/contributor/index.rst index 343c3a9d3..fae3f6b82 100644 --- a/doc/source/usage/osc_cli_plugins.rst +++ b/doc/source/contributor/index.rst @@ -20,14 +20,15 @@ ''''''' Heading 4 (Avoid deeper levels because they do not render well.) -Using Network CLI extensions to OpenStack Client -================================================ +================= +Contributor Guide +================= -List of released CLI commands available in openstack client. These commands -can be referenced by doing ``openstack help network``. +In the Contributor Guide, you will find information on neutronclient's +lower level programming details or APIs as well as the transition to +OpenStack client. .. toctree:: - :glob: :maxdepth: 2 - osc/v2/* + transition_to_osc diff --git a/doc/source/devref/transition_to_osc.rst b/doc/source/contributor/transition_to_osc.rst similarity index 53% rename from doc/source/devref/transition_to_osc.rst rename to doc/source/contributor/transition_to_osc.rst index 92fa92600..9bb952902 100644 --- a/doc/source/devref/transition_to_osc.rst +++ b/doc/source/contributor/transition_to_osc.rst @@ -27,9 +27,9 @@ This document details the transition roadmap for moving the neutron client's OpenStack Networking API support, both the Python library and the ``neutron`` command-line interface (CLI), to the `OpenStack Client (OSC) `_ -and the `OpenStack Python SDK `_. +and the `OpenStack Python SDK `_. This transition is being guided by the -`Deprecate individual CLIs in favour of OSC `_ +`Deprecate individual CLIs in favour of OSC `_ OpenStack spec. See the `Neutron RFE `_, `OSC neutron support etherpad `_ and details below for the overall progress of this transition. @@ -47,7 +47,7 @@ It will be available along side the networking support provided by the OpenStack Python SDK. Users of the neutron client's command extensions will need to transition to the -`OSC plugin system `_ +`OSC plugin system `_ before the ``neutron`` CLI is removed. Such users will maintain their OSC plugin commands within their own project and will be responsible for deprecating and removing their ``neutron`` CLI extension. @@ -56,27 +56,28 @@ Transition Steps ---------------- 1. **Done:** OSC adds OpenStack Python SDK as a dependency. See the following - patch set: https://review.openstack.org/#/c/138745/ + patch set: https://review.opendev.org/#/c/138745/ 2. **Done:** OSC switches its networking support for the - `network `_ + `network `_ command object to use the OpenStack Python SDK instead of the neutron client's Python library. See the following patch set: - https://review.openstack.org/#/c/253348/ + https://review.opendev.org/#/c/253348/ 3. **Done:** OSC removes its python-neutronclient dependency. - See the following patch set: https://review.openstack.org/#/c/255545/ + See the following patch set: https://review.opendev.org/#/c/255545/ 4. **In Progress:** OpenStack Python SDK releases version 1.0 to guarantee backwards compatibility of its networking support and OSC updates its dependencies to include OpenStack Python SDK version 1.0 or later. + See the following blueprint: https://blueprints.launchpad.net/python-openstackclient/+spec/network-command-sdk-support 5. **Done:** OSC switches its networking support for the - `ip floating `_, - `ip floating pool `_, - `ip fixed `_, - `security group `_, and - `security group rule `_ + `ip floating `_, + `ip floating pool `_, + `ip fixed `_, + `security group `_, and + `security group rule `_ command objects to use the OpenStack Python SDK instead of the nova client's Python library when neutron is enabled. When nova network is enabled, then the nova client's Python library will continue to @@ -90,100 +91,94 @@ Transition Steps * **Done** `Security Group Rule CRUD `_ -6. **In Progress:** OSC continues enhancing its networking support. +6. **Done** OSC continues enhancing its networking support. At this point and when applicable, enhancements to the ``neutron`` - CLI must also be made to the ``openstack`` CLI and the OpenStack Python SDK. - Enhancements to the networking support in the OpenStack Python SDK will be - handled via bugs. Users of the neutron client's command extensions should - start their transition to the OSC plugin system. - See the developer guide section below for more information on this step. - -7. **Not Started:** Deprecate the ``neutron`` CLI once the criteria below have - been meet. Running the CLI after it has been deprecated will issue a warning - messages such as the following: - ``DeprecationWarning: The neutron CLI is deprecated in favor of python-openstackclient.`` - In addition, only security fixes will be made to the CLI after it has been - deprecated. + CLI must also be made to the ``openstack`` CLI and possibly the + OpenStack Python SDK. Users of the neutron client's command extensions + should start their transition to the OSC plugin system. See the + developer guide section below for more information on this step. + +7. **Done** Deprecate the ``neutron`` CLI. Running the CLI after + it has been `deprecated `_ + will issue a warning message: + ``neutron CLI is deprecated and will be removed in the Z cycle. Use openstack CLI instead.`` + In addition, no new features will be added to the CLI, though fixes to + the CLI will be assessed on a case by case basis. + +8. **Done** Remove the ``neutron`` CLI after two deprecation cycles + once the criteria below have been met. * The networking support provide by the ``openstack`` CLI is functionally equivalent to the ``neutron`` CLI and it contains sufficient functional and unit test coverage. - * `Neutron Stadium `_ - projects, Neutron documentation and `DevStack `_ + * `Neutron Stadium `_ + projects, Neutron documentation and `DevStack `_ use ``openstack`` CLI instead of ``neutron`` CLI. * Most users of the neutron client's command extensions have transitioned to the OSC plugin system and use the ``openstack`` CLI instead of the ``neutron`` CLI. -8. **Not Started:** Remove the ``neutron`` CLI after two deprecation cycles. - Developer Guide --------------- -The ``neutron`` CLI version 4.x, without extensions, supports over 200 -commands while the ``openstack`` CLI version 2.6.0 supports over 50 -networking commands. Of the 50 commands, some do not have all of the options -or arguments of their ``neutron`` CLI equivalent. With this large functional -gap, a couple critical questions for developers during this transition are "Which -CLI do I change?" and "Where does my CLI belong?" The answer depends on the -state of a command and the state of the overall transition. Details are -outlined in the tables below. Early stages of the transition will require dual -maintenance. Eventually, dual maintenance will be reduced to critical bug fixes -only with feature requests only being made to the ``openstack`` CLI. - -**Which CLI do I change?** - -+----------------------+------------------------+-------------------------------------------------+ -| ``neutron`` Command | ``openstack`` Command | CLI to Change | -+======================+========================+=================================================+ -| Exists | Doesn't Exist | ``neutron`` | -+----------------------+------------------------+-------------------------------------------------+ -| Exists | In Progress | ``neutron`` and ``openstack`` | -| | | (update related blueprint or bug) | -+----------------------+------------------------+-------------------------------------------------+ -| Exists | Exists | ``openstack`` | -| | | (assumes command parity resulting in | -| | | ``neutron`` being deprecated) | -+----------------------+------------------------+-------------------------------------------------+ -| Doesn't Exist | Doesn't Exist | ``openstack`` | -+----------------------+------------------------+-------------------------------------------------+ +The ``neutron`` CLI tool is now removed and all new CLI changes should be done +in the ``OpenStackClient (OSC)`` and, if needed, also in the ``OpenStack SDK``. **Where does my CLI belong?** +If you are developing an API in any of the `neutron repos `_ +the client-side support must be generally located in either the openstackclient or neutronclient +repos. Whether the actual code goes into one or the other repo it depends on the nature of the +feature, its maturity level, and/or the depth of feedback required during the development. + +The table below provides an idea of what goes where. Generally speaking, we consider Core anything +that is L2 and L3 related or that it has been located in the neutron repo for quite sometime, e.g. +QoS or Metering, or that it is available in each neutron deployment irrespective of its configuration +(e.g. auto-allocated-topology). Any client feature that falls into this categorization will need to +be contributed in OSC. Any other that does not, will need to go into neutronclient, assuming that +its server-side is located in a neutron controlled repo. This is a general guideline, when in doubt, +please reach out to a member of the neutron core team for clarifications. + +---------------------------+-------------------+-------------------------------------------------+ | Networking Commands | OSC Plugin | OpenStack Project for ``openstack`` Commands | +===========================+===================+=================================================+ | Core | No | python-openstackclient | +---------------------------+-------------------+-------------------------------------------------+ -| Dynamic Routing | Yes | python-neutronclient | -| | | (``neutronclient/osc/v2/dynamic_routing``) | -+---------------------------+-------------------+-------------------------------------------------+ -| FWaaS v1 | N/A | None (deprecated) | -+---------------------------+-------------------+-------------------------------------------------+ -| FWaaS v2 | Yes | python-neutronclient | -| | | (``neutronclient/osc/v2/fwaas``) | -+---------------------------+-------------------+-------------------------------------------------+ -| LBaaS v1 | N/A | None (deprecated) | -+---------------------------+-------------------+-------------------------------------------------+ -| LBaaS v2 | Yes | python-neutronclient | -| | | (``neutronclient/osc/v2/lbaas``) | +| Extension | Yes | python-neutronclient | +| (i.e. neutron stadium) | | (``neutronclient/osc/v2/``) | +---------------------------+-------------------+-------------------------------------------------+ | Other | Yes | Applicable project owning networking resource | +---------------------------+-------------------+-------------------------------------------------+ -| VPNaaS | Yes | python-neutronclient | -| | | (``neutronclient/osc/v2/vpnaas``) | -+---------------------------+-------------------+-------------------------------------------------+ + +When a repo stops being under neutron governance, its client-side counterpart will have to go through +deprecation. Bear in mind that for grandfathered extensions like FWaaS v1, VPNaaS, and LBaaS v1, this +is not required as the neutronclient is already deprecated on its own. + +**Which Python library do I change?** + ++-------------------------------------------------+-----------------------------------------------+ +| OpenStack Project for ``openstack`` Commands | Python Library to Change | ++=================================================+===============================================+ +| python-openstackclient | openstacksdk | ++-------------------------------------------------+-----------------------------------------------+ +| Other | Applicable project owning network resource | ++-------------------------------------------------+-----------------------------------------------+ **Important:** The actual name of the command object and/or action in OSC may differ from those used by neutron in order to follow the OSC command structure and to avoid -name conflicts. Developers should get new command objects and actions approved by -the OSC team before proceeding with the implementation. - -The "Core" group includes network resources that provide ``neutron`` project features -(i.e. not advanced service or other features). Examples in the "Core" group include: -network, subnet, port, etc. +name conflicts. The `network` prefix must be used to avoid name conflicts if the +command object name is highly likely to have an ambiguous meaning. Developers should +get new command objects and actions approved by the OSC team before proceeding with the +implementation. + +The "Core" group includes network resources that provide core ``neutron`` project +features (e.g. network, subnet, port, etc.) and not advanced features in the +``neutron`` project (e.g. trunk, etc.) or advanced services in separate projects +(FWaaS, LBaaS, VPNaaS, dynamic routing, etc.). +The "Other" group applies projects other than the core ``neutron`` project. +Contact the neutron PTL or core team with questions on network resource classification. When adding or updating an ``openstack`` networking command to python-openstackclient, changes may first be required to the @@ -192,16 +187,15 @@ properties and/or actions. Once the OpenStack Python SDK changes are merged, the related OSC changes can be merged. The OSC changes may require an update to the OSC openstacksdk version in the `requirements.txt `_ -file. ``openstack`` networking commands outside python-openstackclient -are encouraged but not required to use the OpenStack Python SDK. +file. When adding an ``openstack`` networking command to python-openstackclient, you can optionally propose an -`OSC command spec `_ +`OSC command spec `_ which documents the new command interface before proceeding with the implementation. Users of the neutron client's command extensions must adopt the -`OSC plugin `_ +`OSC plugin `_ system for this transition. Such users will maintain their OSC plugin within their own project and should follow the guidance in the table above to determine which command to change. @@ -211,17 +205,17 @@ Developer References * See `OSC neutron support etherpad `_ to determine if an ``openstack`` command is in progress. -* See `OSC command list `_ +* See `OSC command list `_ to determine if an ``openstack`` command exists. -* See `OSC command spec list `_ +* See `OSC command spec list `_ to determine if an ``openstack`` command spec exists. -* See `OSC plugin command list `_ +* See `OSC plugin command list `_ to determine if an ``openstack`` plugin command exists. -* See `OSC command structure `_ +* See `OSC command structure `_ to determine the current ``openstack`` command objects, plugin objects and actions. -* See `OSC human interface guide `_ +* See `OSC human interface guide `_ for guidance on creating new OSC command interfaces. -* See `OSC plugin `_ +* See `OSC plugin `_ for information on the OSC plugin system to be used for ``neutron`` CLI extensions. * Create an OSC blueprint: https://blueprints.launchpad.net/python-openstackclient/ * Report an OSC bug: https://bugs.launchpad.net/python-openstackclient/+filebug diff --git a/doc/source/devref/cli_option_guideline.rst b/doc/source/devref/cli_option_guideline.rst deleted file mode 100644 index 49a840525..000000000 --- a/doc/source/devref/cli_option_guideline.rst +++ /dev/null @@ -1,263 +0,0 @@ -.. - Licensed under the Apache License, Version 2.0 (the "License"); you may - not use this file except in compliance with the License. You may obtain - a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - License for the specific language governing permissions and limitations - under the License. - - - Convention for heading levels in Neutron devref: - ======= Heading 0 (reserved for the title in a document) - ------- Heading 1 - ~~~~~~~ Heading 2 - +++++++ Heading 3 - ''''''' Heading 4 - (Avoid deeper levels because they do not render well.) - -CLI Option Guideline -==================== - -This document describes the conventions of neutron CLI options. - -General conventions -------------------- - -#. Option names should be delimited by a hyphen instead of a underscore. - This is the common guidelines across all OpenStack CLIs. - - * Good: ``--ip-version`` - * Not Good: ``--ip_version`` - -#. Use at least one required option for ``*-create`` command. If all options - are optional, we typically use ``name`` field as a required option. - -#. When you need to specify an ID of a resource, it is better to provide - another way to specify the resource like ``name`` or other reasonable field. - -#. If an attribute name in API is ``foo_id``, the corresponding option - should be ``--foo`` instead of ``--foo-id``. - - * It is because we usually support ID and ``name`` to specify a resource. - -#. Do not use ``nargs='?'`` without a special reason. - - * The behavior of ``nargs='?'`` option for python argparse is - bit tricky and may lead to unexpected option parsing different - from the help message. The detail is described in the - :ref:`Background section ` below. - -#. (option) Avoid using positional options as much as possible. - - * Positional arguments should be limited to attributes which will - be required in the long future. - -#. We honor existing options and should keep compatibilities when adding or - changing options. - -Options for boolean value -------------------------- - -Use the form of ``--option-name {True|False}``. - -* For a new option, it is recommended. -* It is suggested to use ``common.utils.add_boolean_argument`` in an - implementation. It allows ``true``/``false`` in addition to ``True``/``False``. -* For existing options, migration to the recommended form is not necessarily - required. All backward-compatibility should be kept without reasonable - reasons. - -Options for dict value ----------------------- - -Some API attributes take a dictionary. - -``--foo key1=val1,key2=val2`` is usually used. - -This means ``{"key1": "val1", "key2": "val2"}`` is passed in the API layer. - -Examples: - -* ``--host-route destination=CIDR,nexthop=IP_ADDR`` for a subnet -* ``--fixed-ip subnet_id=SUBNET,ip_address=IP_ADDR`` for a port. - -Options for list value ----------------------- - -Some attributes take a list. - -In this case, we usually use: - -* Define an option per element (Use a singular form as an option name) -* Allow to specify the option multiple times - -For Example, **port-create** has ``--security-group`` option. -``--security-group SG1 --security-group SG2`` generates -``{"security_groups: ["SG1", "SG2"]}`` in the API layer. - -This convention applies to a case of a list of dict. -``--allocation-pool`` and ``--host-route`` for a subnet are examples. - -Compatibility with extra arguments ----------------------------------- - -*extra arguments* supports various types of option specifications. -At least the following patterns needs to be considered when defining -a new option. For more detail, see :ref:`cli_extra_arguments`. - -* Normal options with value -* Boolean options : ``--foo True``, ``--bar=False`` -* List options : ``--bars list=true val1 val2``, ``--bars val1 val2`` -* Dict options : ``--foo type=dict key1=va1,key2=val2`` -* List of Dict options : ``--bars list=true type=dict key1=val1,key2=val2 key3=val3,key4=val4`` -* ``action=clear`` - -For normal options with value, there are four patterns to specify an option -as extra arguments. - -* ``--admin-state-up True`` (a space between option name and value) -* ``--admin-state-up=True`` (= between option name and value) -* ``--admin_state_up True`` (underscore is used as delimiter) -* ``--admin_state_up=True`` (underscore is used as delimiter) - -.. _background: - -Background ----------- - -There are a lot of opinions on which form of options are better or not. -This section tries to capture the reason of the current choice. - -Use at least one required option -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -As a convention, **neutron** CLI requires one required argument. - -If all options are optional in the API level and we have ``name`` field, -we usually use ``name`` as a required parameter. -Requiring at least one argument has the following benefits: - -* If we run ``neutron *-create`` without a required argument, we will have a - brief help message without detail option help. It is convenient. -* We can avoid miss operation by just hitting ``neutron *-create``. - Requiring at least one parameter is a good balance. - -Even though we can change this convention to allow to create a resource -without ``name`` field, it will bring confusions to existing users. - -There may be opinion that it is inconsistent with API level requirement -or Horizon behavior, but even if neutron CLI requires ``name`` field -there is no bad impact on regular users. Considering possible confusion -if we change it, it looks better to keep it as-is. - -Options for Boolean value -~~~~~~~~~~~~~~~~~~~~~~~~~ - -* ``--enable-foo``/``--disable-foo`` or similar patterns (including - ``--admin-state-down``) is not suggested because we need two exclusive - options for one attribute in REST API. It is meaningless. - -* It is not recommended to have an option only to specify non-default value. - For example, we have ``--shared`` or ``--admin-state-down`` options for - net-create. This form only works for ``*-create`` and does not work for - ``*-update``. It leads to having different options for ``*-create`` and - ``*-update``. - -* A flag option like ``--enable-dhcp`` (without value) also has a problem when - considering the compatibility with *extra argument*. We can specify - ``-enable-dhcp True/False`` or ``--enable-dhcp=True/False`` in the *extra - argument* mechanism. If we introduce ``--enable-dhcp`` (without value), - the form of ``-enable-dhcp True/False`` cannot be used now. - This is another reason we don't use a flag style option for a boolean parameter. - -.. _background-nargs: - -Avoid using nargs in positional or optional arguments -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The behavior of ``nargs='?'`` option for python argparse is bit tricky. -When we use ``nargs='?'`` and if the order of command-line options is -changed then the command-line parser may fail to parse the arguments -correctly. Two examples of such failures are provided below. - -Example 1: -This example shows how the actual behavior can differ from the provided -help message. In the below block, help message at ``[5]`` says ``--bb CC`` -is a valid format but the argument parsing for the same format fails at ``[7]``. - -.. code-block:: console - - In [1]: import argparse - In [2]: parser = argparse.ArgumentParser() - In [3]: parser.add_argument('--bb', nargs='?') - In [4]: parser.add_argument('cc') - - In [5]: parser.print_help() - usage: ipython [-h] [--bb [BB]] cc - - positional arguments: - cc - - optional arguments: - -h, --help show this help message and exit - --bb [BB] - - In [6]: parser.parse_args('--bb 1 X'.split()) - Out[6]: Namespace(bb='1', cc='X') - - In [7]: parser.parse_args('--bb X'.split()) - usage: ipython [-h] [--bb [BB]] cc - ipython: error: too few arguments - An exception has occurred, use %tb to see the full traceback. - - SystemExit: 2 - - -Example 2: -This example shows how fragile ``nargs='?'`` can be when user specifies -options in different order from the help message. - -.. code-block:: console - - In [1]: import argparse - In [2]: parser = argparse.ArgumentParser() - In [3]: parser.add_argument('--a', help='option a') - In [4]: parser.add_argument('--b', help='option b') - In [5]: parser.add_argument('x', help='positional arg X') - In [6]: parser.add_argument('y', nargs='?', help='positional arg Y') - In [7]: parser.print_help() - usage: ipython [-h] [--a A] [--b B] x [y] - - positional arguments: - x positional arg X - y positional arg Y - - optional arguments: - -h, --help show this help message and exit - --a A option a - --b B option b - - In [8]: parser.parse_args('--a 1 --b 2 X Y'.split()) - Out[8]: Namespace(a='1', b='2', x='X', y='Y') - - In [9]: parser.parse_args('X Y --a 1 --b 2'.split()) - Out[9]: Namespace(a='1', b='2', x='X', y='Y') - - In [10]: parser.parse_args('X --a 1 --b 2 Y'.split()) - usage: ipython [-h] [--a A] [--b B] x [y] - ipython: error: unrecognized arguments: Y - An exception has occurred, use %tb to see the full traceback. - - SystemExit: 2 - - To exit: use 'exit', 'quit', or Ctrl-D. - To exit: use 'exit', 'quit', or Ctrl-D. - -Note: Most CLI users don't care about the order of the command-line -options. Hence, such fragile behavior should be avoided. - diff --git a/doc/source/devref/client_command_extensions.rst b/doc/source/devref/client_command_extensions.rst deleted file mode 100644 index 4b74cdf77..000000000 --- a/doc/source/devref/client_command_extensions.rst +++ /dev/null @@ -1,97 +0,0 @@ -.. - Licensed under the Apache License, Version 2.0 (the "License"); you may - not use this file except in compliance with the License. You may obtain - a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - License for the specific language governing permissions and limitations - under the License. - - - Convention for heading levels in Neutron devref: - ======= Heading 0 (reserved for the title in a document) - ------- Heading 1 - ~~~~~~~ Heading 2 - +++++++ Heading 3 - ''''''' Heading 4 - (Avoid deeper levels because they do not render well.) - -Client command extension support -================================= - -The client command extension adds support for extending the neutron client while -considering ease of creation. -Extensions strongly conform to preexisting neutron commands (/neutron/v2_0/). - -A sample extension can be seen at: -neutronclient/neutron/v2_0/contrib/_fox_sockets.py - -Minimum requirements from an extension --------------------------------------- - -* NeutronClientExtension subclasses must have a shell_command class variable - if the command is to be available to the CLI (shell.py) - - Example: neutronclient.neutron.v2_0.contrib._fox_sockets.FoxInSocketsList - -Minimum requirements to use canonical neutron CRUD commands framework ----------------------------------------------------------------------- - -Neutron commands are cliff commands, commands in extension can use their -own way to finish their tasks. But if they want to make use of the canonical -neutron CRUD commands framework, the extension should: - -* have a class that subclasses NeutronClientExtension to provide the - requisite resource name, version support, and resource collection and - object paths for a resource the commands will process. - - Example: neutronclient.neutron.v2_0.contrib._fox_sockets.FoxInSocket - -* have a class that subclasses from the ClientExtensionList to provide - resource object list function. This is because most commands - need the list function to get object ID via - neutronclient.neutron.v2_0.__init__.find_resource_by_id. - - Example: neutronclient.neutron.v2_0.contrib._fox_sockets.FoxInSocketsList - -* if needed, subclass ClientExtensionUpdate to implement update of the resource - object. - - Example: neutronclient.neutron.v2_0.contrib._fox_sockets.FoxInSocketsUpdate - -* if needed, subclass ClientExtensionDelete to implement deletion of the resource - object. - - Example: neutronclient.neutron.v2_0.contrib._fox_sockets.FoxInSocketsDelete - -* if needed, subclass ClientExtensionShow to get the detail of the resource - object. - - Example: neutronclient.neutron.v2_0.contrib._fox_sockets.FoxInSocketsShow - -Precedence of command loading ------------------------------- - -* hard coded commands are loaded first -* external commands (installed in the environment) are loaded then - -Commands that have the same name will be overwritten by commands that are -loaded later. To change the execution of a command for your particular -extension you only need to override the execute method. - -Currently this extension support is limited to top-level resources. -Parent/child relationships may be added if desired. - -neutronclient.extension entry_point ------------------------------------ - -To activate the commands in a specific extension module, add an entry in -setup.cfg under neutronclient.extension. For example:: - - [entry_points] - neutronclient.extension = - fox_sockets = neutronclient.neutron.v2_0.contrib._fox_sockets diff --git a/doc/source/index.rst b/doc/source/index.rst index be4c77057..c36d482ed 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -19,37 +19,44 @@ ''''''' Heading 4 (Avoid deeper levels because they do not render well.) -Python bindings to the OpenStack Networking API -=============================================== +================================== +python-neutronclient documentation +================================== -This is a client for OpenStack Networking API. There is a :doc:`Python API -` (the neutronclient module), and a :doc:`command-line script -` (installed as **neutron**). Each implements the entire OpenStack -Networking API. +This is a client for OpenStack Networking API. It provides +:doc:`Python API bindings ` (the neutronclient module). -Using neutronclient -------------------- +There is +`OpenStack Client (OSC) `__. +CLI which support the Networking API. + +User Documentation +------------------ .. toctree:: :maxdepth: 2 - usage/cli - usage/library - usage/osc_cli_plugins + cli/index + reference/index -Developer Guide ---------------- +Contributor Guide +----------------- -In the Developer Guide, you will find information on Neutron’s client -lower level programming details or APIs as well as the transition to -OpenStack client. +In the :doc:`Contributor Guide `, you will find +information on neutronclient's lower level programming details or APIs +as well as the transition to OpenStack client. .. toctree:: :maxdepth: 2 - devref/client_command_extensions - devref/cli_option_guideline - devref/transition_to_osc + contributor/index + +.. note:: + + neutron CLI has been deprecated from Ocata release. + We do not add, change and drop any existing commands any more. + We only accept changes on OSC plugin, neutronclient python bindings + and bug fixes on the deprecated CLI (``neutron`` command). History ------- diff --git a/doc/source/reference/index.rst b/doc/source/reference/index.rst new file mode 100644 index 000000000..5678d6578 --- /dev/null +++ b/doc/source/reference/index.rst @@ -0,0 +1,95 @@ +.. + Licensed under the Apache License, Version 2.0 (the "License"); you may + not use this file except in compliance with the License. You may obtain + a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + License for the specific language governing permissions and limitations + under the License. + + + Convention for heading levels in Neutron devref: + ======= Heading 0 (reserved for the title in a document) + ------- Heading 1 + ~~~~~~~ Heading 2 + +++++++ Heading 3 + ''''''' Heading 4 + (Avoid deeper levels because they do not render well.) + +neutronclient Python API +======================== + +Basic Usage +----------- + +First create a client instance using a keystoneauth Session. For more +information on this keystoneauth API, see `Using Sessions`_. + +.. _Using Sessions: https://docs.openstack.org/keystoneauth/latest/using-sessions.html + +.. code-block:: python + + from keystoneauth1 import identity + from keystoneauth1 import session + from neutronclient.v2_0 import client + username='username' + password='password' + project_name='demo' + project_domain_id='default' + user_domain_id='default' + auth_url='http://auth.example.com:5000/v3' + auth = identity.Password(auth_url=auth_url, + username=username, + password=password, + project_name=project_name, + project_domain_id=project_domain_id, + user_domain_id=user_domain_id) + sess = session.Session(auth=auth) + neutron = client.Client(session=sess) + +If you are using Identity v2.0 API (DEPRECATED), create an auth plugin using +the appropriate parameters and `keystoneauth1.identity` will handle Identity +API version discovery. Then you can create a Session and a Neutronclient just +like the previous example. + +.. code-block:: python + + auth = identity.Password(auth_url=auth_url, + username=username, + password=password, + project_name=project_name) + # create a Session and a Neutronclient + +Now you can call various methods on the client instance. + +.. code-block:: python + + network = {'name': 'mynetwork', 'admin_state_up': True} + neutron.create_network({'network':network}) + networks = neutron.list_networks(name='mynetwork') + print networks + network_id = networks['networks'][0]['id'] + neutron.delete_network(network_id) + +Alternatively, you can create a client instance using an auth token +and a service endpoint URL directly. + +.. code-block:: python + + from neutronclient.v2_0 import client + neutron = client.Client(endpoint_url='http://192.168.206.130:9696/', + token='d3f9226f27774f338019aa2611112ef6') + +You can get ``X-Openstack-Request-Id`` as ``request_ids`` from the result. + +.. code-block:: python + + network = {'name': 'mynetwork', 'admin_state_up': True} + neutron.create_network({'network':network}) + networks = neutron.list_networks(name='mynetwork') + print networks.request_ids + # -> ['req-978a0160-7ab0-44f0-8a93-08e9a4e785fa'] diff --git a/doc/source/usage/cli.rst b/doc/source/usage/cli.rst deleted file mode 100644 index 93a5e6675..000000000 --- a/doc/source/usage/cli.rst +++ /dev/null @@ -1,390 +0,0 @@ -.. - Licensed under the Apache License, Version 2.0 (the "License"); you may - not use this file except in compliance with the License. You may obtain - a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - License for the specific language governing permissions and limitations - under the License. - - - Convention for heading levels in Neutron devref: - ======= Heading 0 (reserved for the title in a document) - ------- Heading 1 - ~~~~~~~ Heading 2 - +++++++ Heading 3 - ''''''' Heading 4 - (Avoid deeper levels because they do not render well.) - -Command-line Interface -====================== - -The **neutron** shell utility interacts with OpenStack Networking API from the -command-line. It supports the entire features of OpenStack Networking API. - -Basic Usage ------------ - -In order to use the CLI, you must provide your OpenStack username, password, -tenant, and auth endpoint. Use the corresponding configuration options -(``--os-username``, ``--os-password``, ``--os-tenant-name``, and -``--os-auth-url``), but it is easier to set them in environment variables. - -.. code-block:: shell - - export OS_USERNAME=user - export OS_PASSWORD=pass - export OS_TENANT_NAME=tenant - export OS_AUTH_URL=http://auth.example.com:5000/v2.0 - -Once you've configured your authentication parameters, you can run **neutron** -commands. All commands take the form of: - -.. code-block:: none - - neutron [arguments...] - -Run **neutron help** to get a full list of all possible commands, and run -**neutron help ** to get detailed help for that command. - -Using with os-client-config -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -`os-client-config `_ -provides more convenient way to manage a collection of client configurations -and you can easily switch multiple OpenStack-based configurations. - -To use os-client-config, you first need to prepare -``~/.config/openstack/clouds.yaml`` like the following. - -.. code-block:: yaml - - clouds: - devstack: - auth: - auth_url: http://auth.example.com:5000 - password: your-secret - project_domain_id: default - project_name: demo - user_domain_id: default - username: demo - identity_api_version: '3' - region_name: RegionOne - devstack-admin: - auth: - auth_url: http://auth.example.com:35357 - password: another-secret - project_domain_id: default - project_name: admin - user_domain_id: default - username: admin - identity_api_version: '3' - region_name: RegionOne - -Then, you need to specify a configuration name defined in the above clouds.yaml. - -.. code-block:: shell - - export OS_CLOUD=devstack - -For more detail information, see the -`os-client-config `_ -documentation. - -Using with keystone token -~~~~~~~~~~~~~~~~~~~~~~~~~ - -The command-line tool will attempt to re-authenticate using your provided -credentials for every request. You can override this behavior by manually -supplying an auth token using ``--os-url`` and ``--os-auth-token``. You can -alternatively set these environment variables. - -.. code-block:: shell - - export OS_URL=http://neutron.example.org:9696/ - export OS_TOKEN=3bcc3d3a03f44e3d8377f9247b0ad155 - -Using noauth mode -~~~~~~~~~~~~~~~~~ - -If neutron server does not require authentication, besides these two arguments -or environment variables (We can use any value as token.), we need manually -supply ``--os-auth-strategy`` or set the environment variable. - -.. code-block:: shell - - export OS_AUTH_STRATEGY=noauth - -Display options ---------------- - -Filtering -~~~~~~~~~ - -Neutron API supports filtering in the listing operation. -**neutron** CLI supports this feature too. - -To specify a filter in ``*-list`` command, you need to pass a pair of an -attribute name and an expected value with the format of ``-- ``. -The example below retrieves ports owned by compute instances. - -.. code-block:: console - - $ neutron port-list --device_owner network:dhcp - +--------------------------------------+------+-------------------+-------------------------------------------------------------------------------------------------------------+ - | id | name | mac_address | fixed_ips | - +--------------------------------------+------+-------------------+-------------------------------------------------------------------------------------------------------------+ - | 8953d683-29ad-4be3-b73f-060727c7849b | | fa:16:3e:4b:9e:0a | {"subnet_id": "6b832dfe-f271-443c-abad-629961414a73", "ip_address": "10.0.0.2"} | - | | | | {"subnet_id": "cdcc616b-0cff-482f-96f5-06fc63d21247", "ip_address": "fd12:877c:1d66:0:f816:3eff:fe4b:9e0a"} | - +--------------------------------------+------+-------------------+-------------------------------------------------------------------------------------------------------------+ - -You can also specify multiple filters. -The example below retrieves security group rules applied to IPv4 traffic -which belongs to a security group bfa493f9-2b03-46d2-8399-b9b038a53bc1. - -.. code-block:: console - - $ neutron security-group-rule-list --security-group-id bfa493f9-2b03-46d2-8399-b9b038a53bc1 --ethertype IPv4 - +--------------------------------------+----------------+-----------+-----------+---------------+-----------------+ - | id | security_group | direction | ethertype | protocol/port | remote | - +--------------------------------------+----------------+-----------+-----------+---------------+-----------------+ - | 65489805-0400-4bce-9bd9-16a81952263c | default | egress | IPv4 | any | any | - | 9429f336-4947-4643-bbd9-24528cc65648 | default | ingress | IPv4 | any | default (group) | - +--------------------------------------+----------------+-----------+-----------+---------------+-----------------+ - -.. note:: - - Looking up UUID from name is not supported when specifying a filter. - You need to use UUID to specify a specific resource. - -.. note:: - - Filtering for dictionary or list attributes is not supported. - -Changing displayed columns -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If you want displayed columns in a list operation, ``-c`` option can be used. -``-c`` can be specified multiple times and the column order will be same as -the order of ``-c`` options. - -.. code-block:: console - - $ neutron port-list -c id -c device_owner -c fixed_ips - +--------------------------------------+--------------------------+-------------------------------------------------------------------------------------------------------------+ - | id | device_owner | fixed_ips | - +--------------------------------------+--------------------------+-------------------------------------------------------------------------------------------------------------+ - | 41ca1b9b-4bbd-4aa8-bcaa-31d3d5704205 | network:router_interface | {"subnet_id": "6b832dfe-f271-443c-abad-629961414a73", "ip_address": "10.0.0.1"} | - | 8953d683-29ad-4be3-b73f-060727c7849b | network:dhcp | {"subnet_id": "6b832dfe-f271-443c-abad-629961414a73", "ip_address": "10.0.0.2"} | - | | | {"subnet_id": "cdcc616b-0cff-482f-96f5-06fc63d21247", "ip_address": "fd12:877c:1d66:0:f816:3eff:fe4b:9e0a"} | - | a9da29f8-4504-4526-a5ce-cd3624fbd173 | neutron:LOADBALANCER | {"subnet_id": "6b832dfe-f271-443c-abad-629961414a73", "ip_address": "10.0.0.3"} | - | | | {"subnet_id": "cdcc616b-0cff-482f-96f5-06fc63d21247", "ip_address": "fd12:877c:1d66:0:f816:3eff:feb1:ab71"} | - | d6a1ff96-0a99-416f-a4d6-65d9614cf64e | compute:nova | {"subnet_id": "6b832dfe-f271-443c-abad-629961414a73", "ip_address": "10.0.0.4"} | - | | | {"subnet_id": "cdcc616b-0cff-482f-96f5-06fc63d21247", "ip_address": "fd12:877c:1d66:0:f816:3eff:fe2c:348e"} | - | f4789225-26d0-409f-8047-82d2c7a87a95 | network:router_interface | {"subnet_id": "cdcc616b-0cff-482f-96f5-06fc63d21247", "ip_address": "fd12:877c:1d66::1"} | - +--------------------------------------+--------------------------+-------------------------------------------------------------------------------------------------------------+ - -.. _cli_extra_arguments: - -Extra arguments for create/update operation -------------------------------------------- - -**neutron** CLI has a mechanism called the *extra arguments* for ``*-create`` -and ``*-update`` commands. It allows users to specify a set of *unknown -options* which are not defined as options and not shown in the help text. -**Unknown options MUST be placed at the end of the command line.** -*unknown options* will be directly passed to the API layer. By this mechanism, -you can pass an attribute which is not defined in the upstream **neutron** -CLI. For example, when you are developing a new feature which add a new -attribute to an existing resource, it is useful because we can test your -feature without changing the existing neutron CLI. - -For example, if you run the following command:: - - neutron resource-update --key1 value1 --key2 value2 - -where ``resource`` is some resource name and ``--key1`` and ``--key2`` are -unknown options, then the following JSON will be sent to the neutron API:: - - PUT /v2.0/resources/ - - { - "resource": { - "key2": "value2", - "key1": "value1" - } - } - -Key interpretation -~~~~~~~~~~~~~~~~~~ - -This means an option name (``--key1`` in this case) must be one of valid -resources of a corresponding resource. An option name ``--foo_bar`` is -recognized as an attribute name ``foo_bar``. ``--foo-bar`` is also interpreted -as an attribute name ``foo_bar``. - -Value interpretation -~~~~~~~~~~~~~~~~~~~~ - -By default, if the number of values is 1, the option value is interpreted as a -string and is passed to the API layer as specified in a command-line. - -If the number of values is greater than 1, the option value is interpreted as a -list and the result in the API layer will be same as when specifying a list as -described below. - - neutron resource-update --key1 val1 val2 val3 --key2 val4 - -In the above example, a value of ``key1`` is interpreted as -``["val1", "val2", "val3"]`` and a value of ``key2`` is interpreted -as ``val4``. - -The extra argument mechanism supports more complex value like a list or a dict. - -Specify a list value -++++++++++++++++++++ - -A command-line:: - - neutron resource-update --key list=true val1 val2 val3 - -will send the following in the API layer:: - - { - "key": [ - "val1", - "val2", - "val3" - ] - } - -.. note:: - - If you want to specify a list value, it is recommended to specify - ``list=true``. When ``list=true`` is specified, specified values are - interpreted as a list even regardless of the number of values. - - If ``list=true`` is not specified, specified values are interpreted - depends on the number of values how. If the number of values is more than 2, - the specified values are interpreted as a list. If 1, the value - is interpreted as a string. - -Specify a dict value -++++++++++++++++++++ - -A command-line:: - - neutron resource-update --key type=dict key1=val1,key2=val2,key3=val3 - -will send the following in the API layer:: - - { - "key": { - "key1": "val1", - "key2": "val2", - "key3": "val3" - } - } - -.. note:: - - ``type=bool True/False`` and ``type=int 10`` are also supported. - -Specify a list of dicts -+++++++++++++++++++++++ - -A command-line:: - - neutron resource-update --key type=dict list=true key1=val1 key2=val2 key3=val3 - -will send the following in the API layer:: - - { - "key": [ - {"key1": "val1"}, - {"key2": "val2"}, - {"key3": "val3"} - ] - } - -Passing None as a value -~~~~~~~~~~~~~~~~~~~~~~~ - -There is a case where we would like to pass ``None`` (``null`` in JSON) -in the API layer. To do this:: - - neutron resource-update --key action=clear - -The following body will be in the API layer:: - - {"key": null} - -.. note:: - - If ``action=clear`` is specified, ``list=true`` or ``type=dict`` is ignored. - It means when ``action=clear`` is specified ``None`` is always sent. - -Debugging ---------- - -Display API-level communication -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -``-v`` (or ``--verbose``, ``--debug``) option displays a detail interaction -with your neutron server. It is useful to debug what happens in the API level. - -Here is an sample output of ``net-show`` command. - -The first line show what parameters are recognized by neutronclient. -It is sometimes useful to check if command-line parameters you specify are recognized properly. - -.. code-block:: console - - $ neutron -v net-show mynetwork - DEBUG: neutronclient.neutron.v2_0.network.ShowNetwork get_data(Namespace(columns=[], fields=[], formatter='table', id=u'mynetwork', max_width=0, noindent=False, prefix='', request_format='json', show_details=False, variables=[])) - -Next, neutronclient sends an authentication request to keystone to get a token -which is used in further operations. - -.. code-block:: console - - DEBUG: keystoneauth.session REQ: curl -g -i -X GET http://172.16.18.47:5000 -H "Accept: application/json" -H "User-Agent: keystoneauth1" - DEBUG: keystoneauth.session RESP: [300] Content-Length: 593 Vary: X-Auth-Token Keep-Alive: timeout=5, max=100 Server: Apache/2.4.7 (Ubuntu) Connection: Keep-Alive Date: Fri, 27 Nov 2015 20:10:54 GMT Content-Type: application/json - RESP BODY: {"versions": {"values": [{"status": "stable", "updated": "2015-03-30T00:00:00Z", "media-types": [{"base": "application/json", "type": "application/vnd.openstack.identity-v3+json"}], "id": "v3.4", "links": [{"href": "http://172.16.18.47:5000/v3/", "rel": "self"}]}, {"status": "stable", "updated": "2014-04-17T00:00:00Z", "media-types": [{"base": "application/json", "type": "application/vnd.openstack.identity-v2.0+json"}], "id": "v2.0", "links": [{"href": "http://172.16.18.47:5000/v2.0/", "rel": "self"}, {"href": "http://docs.openstack.org/", "type": "text/html", "rel": "describedby"}]}]}} - - DEBUG: keystoneauth.identity.v3.base Making authentication request to http://172.16.18.47:5000/v3/auth/tokens - -Neutronclient looks up a network ID corresponding to a given network name. - -.. code-block:: console - - DEBUG: keystoneauth.session REQ: curl -g -i -X GET http://172.16.18.47:9696/v2.0/networks.json?fields=id&name=mynetwork -H "User-Agent: python-neutronclient" -H "Accept: application/json" -H "X-Auth-Token: {SHA1}39300e7398d53a02afd183f13cb6afaef95ec4e5" - DEBUG: keystoneauth.session RESP: [200] Date: Fri, 27 Nov 2015 20:10:55 GMT Connection: keep-alive Content-Type: application/json; charset=UTF-8 Content-Length: 62 X-Openstack-Request-Id: req-ccebf6e4-4f52-4874-a1ab-5499abcba378 - RESP BODY: {"networks": [{"id": "3698d3c7-d581-443e-bf86-53c4e3a738f7"}]} - -Finally, neutronclient retrieves a detail of a given network using the resolved ID. - -.. code-block:: console - - DEBUG: keystoneauth.session REQ: curl -g -i -X GET http://172.16.18.47:9696/v2.0/networks/3698d3c7-d581-443e-bf86-53c4e3a738f7.json -H "User-Agent: python-neutronclient" -H "Accept: application/json" -H "X-Auth-Token: {SHA1}39300e7398d53a02afd183f13cb6afaef95ec4e5" - DEBUG: keystoneauth.session RESP: [200] Date: Fri, 27 Nov 2015 20:10:55 GMT Connection: keep-alive Content-Type: application/json; charset=UTF-8 Content-Length: 272 X-Openstack-Request-Id: req-261add00-d6d3-4ea7-becc-105b60ac7369 - RESP BODY: {"network": {"status": "ACTIVE", "subnets": [], "name": "mynetwork", "admin_state_up": true, "tenant_id": "8f0ebf767043483a987736c8c684178d", "mtu": 0, "router:external": false, "shared": false, "port_security_enabled": true, "id": "3698d3c7-d581-443e-bf86-53c4e3a738f7"}} - - +-----------------------+--------------------------------------+ - | Field | Value | - +-----------------------+--------------------------------------+ - | admin_state_up | True | - | id | 3698d3c7-d581-443e-bf86-53c4e3a738f7 | - | mtu | 0 | - | name | mynetwork | - | port_security_enabled | True | - | router:external | False | - | shared | False | - | status | ACTIVE | - | subnets | | - | tenant_id | 8f0ebf767043483a987736c8c684178d | - +-----------------------+--------------------------------------+ diff --git a/doc/source/usage/library.rst b/doc/source/usage/library.rst deleted file mode 100644 index 481a08b45..000000000 --- a/doc/source/usage/library.rst +++ /dev/null @@ -1,71 +0,0 @@ -.. - Licensed under the Apache License, Version 2.0 (the "License"); you may - not use this file except in compliance with the License. You may obtain - a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - License for the specific language governing permissions and limitations - under the License. - - - Convention for heading levels in Neutron devref: - ======= Heading 0 (reserved for the title in a document) - ------- Heading 1 - ~~~~~~~ Heading 2 - +++++++ Heading 3 - ''''''' Heading 4 - (Avoid deeper levels because they do not render well.) - -neutronclient Python API -======================== - -Basic Usage ------------ - -First create a client instance. - -.. code-block:: python - - >>> from neutronclient.v2_0 import client - >>> username='adminUser' - >>> password='secretword' - >>> project_name='openstackDemo' - >>> auth_url='http://192.168.206.130:5000/v2.0' - >>> neutron = client.Client(username=username, - ... password=password, - ... project_name=project_name, - ... auth_url=auth_url) - -Now you can call various methods on the client instance. - -.. code-block:: python - - >>> network = {'name': 'mynetwork', 'admin_state_up': True} - >>> neutron.create_network({'network':network}) - >>> networks = neutron.list_networks(name='mynetwork') - >>> print networks - >>> network_id = networks['networks'][0]['id'] - >>> neutron.delete_network(network_id) - -Alternatively, you can create a client instance using an auth token -and a service endpoint URL directly. - -.. code-block:: python - - >>> from neutronclient.v2_0 import client - >>> neutron = client.Client(endpoint_url='http://192.168.206.130:9696/', - ... token='d3f9226f27774f338019aa2611112ef6') - -You can get ``X-Openstack-Request-Id`` as ``request_ids`` from the result. - -.. code-block:: python - - >>> network = {'name': 'mynetwork', 'admin_state_up': True} - >>> neutron.create_network({'network':network}) - >>> networks = neutron.list_networks(name='mynetwork') - >>> print networks.request_ids - ['req-978a0160-7ab0-44f0-8a93-08e9a4e785fa'] diff --git a/doc/source/usage/osc/v2/network-trunk.rst b/doc/source/usage/osc/v2/network-trunk.rst deleted file mode 100644 index 5a79310a4..000000000 --- a/doc/source/usage/osc/v2/network-trunk.rst +++ /dev/null @@ -1,171 +0,0 @@ -============= -network trunk -============= - -A **network trunk** is a container to group logical ports from different -networks and provide a single trunked vNIC for servers. It consists of -one parent port which is a regular VIF and multiple subports which allow -the server to connect to more networks. - -Network v2 - -network subport list --------------------- - -List all subports for a given network trunk - -.. program:: network subport list -.. code:: bash - - os network subport list - --trunk - -.. option:: --trunk - - List subports belonging to this trunk (name or ID) (required) - -network trunk create --------------------- - -Create a network trunk for a given project - -.. program:: network trunk create -.. code:: bash - - os network trunk create - --parent-port - [--subport ] - [--enable | --disable] - [--project [--project-domain ]] - - -.. option:: --parent-port - - Parent port belonging to this trunk (name or ID) (required) - -.. option:: --subport - - Subport to add. Subport is of form 'port=,segmentation-type=,segmentation-ID=' - (--subport) option can be repeated - -.. option:: --enable - - Enable trunk (default) - -.. option:: --disable - - Disable trunk - -.. option:: --project - - Owner's project (name or ID) - -.. option:: --project-domain - - Domain the project belongs to (name or ID). - This can be used in case collisions between project names exist. - -network trunk delete --------------------- - -Delete a given network trunk - -.. program:: network trunk delete -.. code:: bash - - os network trunk delete - [ ...] - -.. _network_trunk_delete-trunk: -.. describe:: - - Trunk(s) to delete (name or ID) - -network trunk list ------------------- - -List all network trunks - -.. program:: network trunk list -.. code:: bash - - os network trunk list - [--long] - -.. option:: --long - - List additional fields in output - -network trunk set ------------------ - -Set network trunk properties - -.. program:: network trunk set -.. code:: bash - - os network trunk set - [--name ] - [--subport ] - [--enable | --disable] - - -.. option:: --name - - Set trunk name - -.. option:: --subport - - Subport to add. Subport is of form 'port=,segmentation-type=,segmentation-ID=' - (--subport) option can be repeated - -.. option:: --enable - - Enable trunk - -.. option:: --disable - - Disable trunk - -.. _network_trunk_set-trunk: -.. describe:: - - Trunk to modify (name or ID) - -network trunk show ------------------- - -Show information of a given network trunk - -.. program:: network trunk show -.. code:: bash - - os network trunk show - - -.. _network_trunk_show-trunk: -.. describe:: - - Trunk to display (name or ID) - -network trunk unset -------------------- - -Unset subports from a given network trunk - -.. program:: network trunk unset -.. code:: bash - - os network trunk unset - --subport - - -.. option:: --subport - - Subport to delete (name or ID of the port) (required) - (--subport) option can be repeated - -.. _network_trunk_unset-trunk: -.. describe:: - - Unset subports from this trunk (name or ID) diff --git a/neutron_test.sh b/neutron_test.sh deleted file mode 100755 index d6dc75409..000000000 --- a/neutron_test.sh +++ /dev/null @@ -1,165 +0,0 @@ -#!/bin/bash -set -x -function die() { - local exitcode=$? - set +o xtrace - echo $@ - cleanup - exit $exitcode -} - -net_name=mynet1 -subnet_name=mysubnet1 -port_name=myport1 -function cleanup() { - echo Removing test port, subnet and net... - neutron port-delete $port_name - neutron subnet-delete $subnet_name - neutron net-delete $net_name -} - -noauth_tenant_id=me -if [ "$1" == "noauth" ]; then - NOAUTH="--tenant_id $noauth_tenant_id" -else - NOAUTH= -fi - -echo "NOTE: User should be admin in order to perform all operations." -sleep 3 - -# test the CRUD of network -network=$net_name -neutron net-create $NOAUTH $network || die "fail to create network $network" -temp=`neutron net-list -- --name $network --fields id | wc -l` -echo $temp -if [ $temp -ne 5 ]; then - die "networks with name $network is not unique or found" -fi -network_id=`neutron net-list -- --name $network --fields id | tail -n 2 | head -n 1 | cut -d' ' -f 2` -echo "ID of network with name $network is $network_id" - -neutron net-show $network || die "fail to show network $network" -neutron net-show $network_id || die "fail to show network $network_id" - -neutron net-update $network --admin_state_up False || die "fail to update network $network" -neutron net-update $network_id --admin_state_up True || die "fail to update network $network_id" - -neutron net-list -c id -- --id fakeid || die "fail to list networks with column selection on empty list" - -# test the CRUD of subnet -subnet=$subnet_name -cidr=10.0.1.0/24 -neutron subnet-create $NOAUTH $network $cidr --name $subnet || die "fail to create subnet $subnet" -tempsubnet=`neutron subnet-list -- --name $subnet --fields id | wc -l` -echo $tempsubnet -if [ $tempsubnet -ne 5 ]; then - die "subnets with name $subnet is not unique or found" -fi -subnet_id=`neutron subnet-list -- --name $subnet --fields id | tail -n 2 | head -n 1 | cut -d' ' -f 2` -echo "ID of subnet with name $subnet is $subnet_id" -neutron subnet-show $subnet || die "fail to show subnet $subnet" -neutron subnet-show $subnet_id || die "fail to show subnet $subnet_id" - -neutron subnet-update $subnet --dns_nameservers list=true 1.1.1.11 1.1.1.12 || die "fail to update subnet $subnet" -neutron subnet-update $subnet_id --dns_nameservers list=true 2.2.2.21 2.2.2.22 || die "fail to update subnet $subnet_id" - -# test the crud of ports -port=$port_name -neutron port-create $NOAUTH $network --name $port || die "fail to create port $port" -tempport=`neutron port-list -- --name $port --fields id | wc -l` -echo $tempport -if [ $tempport -ne 5 ]; then - die "ports with name $port is not unique or found" -fi -port_id=`neutron port-list -- --name $port --fields id | tail -n 2 | head -n 1 | cut -d' ' -f 2` -echo "ID of port with name $port is $port_id" -neutron port-show $port || die "fail to show port $port" -neutron port-show $port_id || die "fail to show port $port_id" -neutron port-update $port --device_id deviceid1 || die "fail to update port $port" -neutron port-update $port_id --device_id deviceid2 || die "fail to update port $port_id" -neutron port-update $port_id --allowed-address-pair ip_address=1.1.1.11,mac_address=10:00:00:00:00:00 --allowed-address-pair ip_address=1.1.1.12,mac_address=10:00:00:00:00:01 || die "fail to update port $port_id --allowed-address-pair" -neutron port-show $port || die "fail to show port $port" -neutron port-show $port_id || die "fail to show port $port_id" -neutron port-update $port_id --no-allowed-address-pairs || die "fail to update port $port_id --no-allowed-address-pairs" -neutron port-show $port || die "fail to show port $port" -neutron port-show $port_id || die "fail to show port $port_id" -neutron port-delete $port_id - -# test the create port with allowed-address-pairs -port=$port_name -neutron port-create $NOAUTH $network --name $port -- --allowed-address-pairs type=dict list=true ip_address=1.1.1.11,mac_address=10:00:00:00:00:00 ip_address=1.1.1.12,mac_address=10:00:00:00:00:01 || die "fail to create port $port" -tempport=`neutron port-list -- --name $port --fields id | wc -l` -echo $tempport -if [ $tempport -ne 5 ]; then - die "ports with name $port is not unique or found" -fi -port_id=`neutron port-list -- --name $port --fields id | tail -n 2 | head -n 1 | cut -d' ' -f 2` -echo "ID of port with name $port is $port_id" -neutron port-show $port || die "fail to show port $port" -neutron port-show $port_id || die "fail to show port $port_id" -neutron port-update $port_id --no-allowed-address-pairs || die "fail to update port $port_id --no-allowed-address-pairs" -neutron port-show $port_id - -# test quota commands RUD -DEFAULT_NETWORKS=10 -DEFAULT_PORTS=50 -tenant_id=tenant_a -tenant_id_b=tenant_b -neutron quota-update --tenant_id $tenant_id --network 30 || die "fail to update quota for tenant $tenant_id" -neutron quota-update --tenant_id $tenant_id_b --network 20 || die "fail to update quota for tenant $tenant_id" -networks=`neutron quota-list -c network -c tenant_id | grep $tenant_id | awk '{print $2}'` -if [ $networks -ne 30 ]; then - die "networks quota should be 30" -fi -networks=`neutron quota-list -c network -c tenant_id | grep $tenant_id_b | awk '{print $2}'` -if [ $networks -ne 20 ]; then - die "networks quota should be 20" -fi -networks=`neutron quota-show --tenant_id $tenant_id | grep network | awk -F'|' '{print $3}'` -if [ $networks -ne 30 ]; then - die "networks quota should be 30" -fi -neutron quota-delete --tenant_id $tenant_id || die "fail to delete quota for tenant $tenant_id" -networks=`neutron quota-show --tenant_id $tenant_id | grep network | awk -F'|' '{print $3}'` -if [ $networks -ne $DEFAULT_NETWORKS ]; then - die "networks quota should be $DEFAULT_NETWORKS" -fi -# update self -if [ "t$NOAUTH" = "t" ]; then - # with auth - neutron quota-update --port 99 || die "fail to update quota for self" - ports=`neutron quota-show | grep port | awk -F'|' '{print $3}'` - if [ $ports -ne 99 ]; then - die "ports quota should be 99" - fi - - ports=`neutron quota-list -c port | grep 99 | awk '{print $2}'` - if [ $ports -ne 99 ]; then - die "ports quota should be 99" - fi - neutron quota-delete || die "fail to delete quota for tenant self" - ports=`neutron quota-show | grep port | awk -F'|' '{print $3}'` - if [ $ports -ne $DEFAULT_PORTS ]; then - die "ports quota should be $DEFAULT_PORTS" - fi -else - # without auth - neutron quota-update --port 100 - if [ $? -eq 0 ]; then - die "without valid context on server, quota update command should fail." - fi - neutron quota-show - if [ $? -eq 0 ]; then - die "without valid context on server, quota show command should fail." - fi - neutron quota-delete - if [ $? -eq 0 ]; then - die "without valid context on server, quota delete command should fail." - fi - neutron quota-list || die "fail to update quota for self" -fi - -cleanup -echo "Success! :)" - diff --git a/neutronclient/_i18n.py b/neutronclient/_i18n.py index f5aa76bcf..50ac3c5bf 100644 --- a/neutronclient/_i18n.py +++ b/neutronclient/_i18n.py @@ -17,7 +17,7 @@ _translators = oslo_i18n.TranslatorFactory(domain=DOMAIN) -# The primary translation function using the well-known name "_" +# The translation function using the well-known name "_" _ = _translators.primary # The contextual translation function using the name "_C" @@ -26,16 +26,6 @@ # The plural translation function using the name "_P" _P = _translators.plural_form -# Translators for log levels. -# -# The abbreviated names are meant to reflect the usual use of a short -# name like '_'. The "L" is for "log" and the other letter comes from -# the level. -_LI = _translators.log_info -_LW = _translators.log_warning -_LE = _translators.log_error -_LC = _translators.log_critical - def get_available_languages(): return oslo_i18n.get_available_languages(DOMAIN) diff --git a/neutronclient/client.py b/neutronclient/client.py index 10a189ba0..e25f575f3 100644 --- a/neutronclient/client.py +++ b/neutronclient/client.py @@ -14,22 +14,22 @@ # under the License. # -try: - import json -except ImportError: - import simplejson as json import logging import os import debtcollector.renames from keystoneauth1 import access from keystoneauth1 import adapter +from oslo_serialization import jsonutils +from oslo_utils import importutils import requests from neutronclient._i18n import _ from neutronclient.common import exceptions from neutronclient.common import utils +osprofiler_web = importutils.try_import("osprofiler.web") + _logger = logging.getLogger(__name__) if os.environ.get('NEUTRONCLIENT_DEBUG'): @@ -43,6 +43,7 @@ logging.getLogger("requests").setLevel(_requests_log_level) MAX_URI_LEN = 8192 USER_AGENT = 'python-neutronclient' +REQ_ID_HEADER = 'X-OpenStack-Request-ID' class HTTPClient(object): @@ -60,9 +61,9 @@ def __init__(self, username=None, user_id=None, token=None, region_name=None, timeout=None, endpoint_url=None, insecure=False, endpoint_type='publicURL', - auth_strategy='keystone', ca_cert=None, log_credentials=False, - service_type='network', - **kwargs): + auth_strategy='keystone', ca_cert=None, cert=None, + log_credentials=False, service_type='network', + global_request_id=None, **kwargs): self.username = username self.user_id = user_id @@ -80,6 +81,8 @@ def __init__(self, username=None, user_id=None, self.endpoint_url = endpoint_url self.auth_strategy = auth_strategy self.log_credentials = log_credentials + self.global_request_id = global_request_id + self.cert = cert if insecure: self.verify_cert = False else: @@ -102,14 +105,25 @@ def _cs_request(self, *args, **kwargs): try: resp, body = self.request(*args, **kargs) except requests.exceptions.SSLError as e: - raise exceptions.SslCertificateValidationError(reason=e) + raise exceptions.SslCertificateValidationError(reason=str(e)) except Exception as e: # Wrap the low-level connection error (socket timeout, redirect # limit, decompression error, etc) into our custom high-level # connection exception (it is excepted in the upper layers of code) _logger.debug("throwing ConnectionFailed : %s", e) - raise exceptions.ConnectionFailed(reason=e) + raise exceptions.ConnectionFailed(reason=str(e)) utils.http_log_resp(_logger, resp, body) + + # log request-id for each api call + request_id = resp.headers.get('x-openstack-request-id') + if request_id: + _logger.debug('%(method)s call to neutron for ' + '%(url)s used request id ' + '%(response_request_id)s', + {'method': resp.request.method, + 'url': resp.url, + 'response_request_id': request_id}) + if resp.status_code == 401: raise exceptions.Unauthorized(message=body) return resp, body @@ -139,7 +153,14 @@ def request(self, url, method, body=None, headers=None, **kwargs): if body: headers.setdefault('Content-Type', content_type) + if self.global_request_id: + headers.setdefault(REQ_ID_HEADER, self.global_request_id) + headers['User-Agent'] = USER_AGENT + # NOTE(dbelova): osprofiler_web.get_trace_id_headers does not add any + # headers in case if osprofiler is not initialized. + if osprofiler_web: + headers.update(osprofiler_web.get_trace_id_headers()) resp = requests.request( method, @@ -147,6 +168,7 @@ def request(self, url, method, body=None, headers=None, **kwargs): data=body, headers=headers, verify=self.verify_cert, + cert=self.cert, timeout=self.timeout, **kwargs) @@ -167,7 +189,7 @@ def do_request(self, url, method, **kwargs): # might be because the auth token expired, so try to # re-authenticate and try again. If it still fails, bail. try: - kwargs.setdefault('headers', {}) + kwargs['headers'] = kwargs.get('headers') or {} if self.auth_token is None: self.auth_token = "" kwargs['headers']['X-Auth-Token'] = self.auth_token @@ -176,7 +198,7 @@ def do_request(self, url, method, **kwargs): return resp, body except exceptions.Unauthorized: self.authenticate() - kwargs.setdefault('headers', {}) + kwargs['headers'] = kwargs.get('headers') or {} kwargs['headers']['X-Auth-Token'] = self.auth_token resp, body = self._cs_request( self.endpoint_url + url, method, **kwargs) @@ -216,14 +238,14 @@ def _authenticate_keystone(self): token_url = self.auth_url + "/tokens" resp, resp_body = self._cs_request(token_url, "POST", - body=json.dumps(body), + body=jsonutils.dumps(body), content_type="application/json", allow_redirects=True) if resp.status_code != 200: raise exceptions.Unauthorized(message=resp_body) if resp_body: try: - resp_body = json.loads(resp_body) + resp_body = jsonutils.loads(resp_body) except ValueError: pass else: @@ -259,7 +281,7 @@ def _get_endpoint_url(self): self.authenticate() return self.endpoint_url - body = json.loads(body) + body = jsonutils.loads(body) for endpoint in body.get('endpoints', []): if (endpoint['type'] == 'network' and endpoint.get('region') == self.region_name): @@ -276,6 +298,9 @@ def get_auth_info(self): 'auth_user_id': self.auth_user_id, 'endpoint_url': self.endpoint_url} + def get_auth_ref(self): + return getattr(self, 'auth_ref', None) + class SessionClient(adapter.Adapter): @@ -285,9 +310,14 @@ def request(self, *args, **kwargs): content_type = kwargs.pop('content_type', None) or 'application/json' - headers = kwargs.setdefault('headers', {}) + headers = kwargs.get('headers') or {} headers.setdefault('Accept', content_type) + # NOTE(dbelova): osprofiler_web.get_trace_id_headers does not add any + # headers in case if osprofiler is not initialized. + if osprofiler_web: + headers.update(osprofiler_web.get_trace_id_headers()) + try: kwargs.setdefault('data', kwargs.pop('body')) except KeyError: @@ -296,6 +326,7 @@ def request(self, *args, **kwargs): if kwargs.get('data'): headers.setdefault('Content-Type', content_type) + kwargs['headers'] = headers resp = super(SessionClient, self).request(*args, **kwargs) return resp, resp.text @@ -346,6 +377,9 @@ def get_auth_info(self): return auth_info + def get_auth_ref(self): + return self.session.auth.get_auth_ref(self.session) + # FIXME(bklei): Should refactor this to use kwargs and only # explicitly list arguments that are not None. @@ -367,8 +401,10 @@ def construct_http_client(username=None, log_credentials=None, auth_strategy='keystone', ca_cert=None, + cert=None, service_type='network', session=None, + global_request_id=None, **kwargs): if session: @@ -377,6 +413,7 @@ def construct_http_client(username=None, return SessionClient(session=session, service_type=service_type, region_name=region_name, + global_request_id=global_request_id, **kwargs) else: # FIXME(bklei): username and password are now optional. Need @@ -396,5 +433,7 @@ def construct_http_client(username=None, endpoint_type=endpoint_type, service_type=service_type, ca_cert=ca_cert, + cert=cert, log_credentials=log_credentials, - auth_strategy=auth_strategy) + auth_strategy=auth_strategy, + global_request_id=global_request_id) diff --git a/neutronclient/common/clientmanager.py b/neutronclient/common/clientmanager.py index b79ee5b31..56d3d3c12 100644 --- a/neutronclient/common/clientmanager.py +++ b/neutronclient/common/clientmanager.py @@ -17,17 +17,12 @@ """Manage access to the clients, including authenticating when needed. """ -import logging - import debtcollector.renames from neutronclient import client from neutronclient.neutron import client as neutron_client -LOG = logging.getLogger(__name__) - - class ClientCache(object): """Descriptor class for caching created client handles.""" diff --git a/neutronclient/common/exceptions.py b/neutronclient/common/exceptions.py index d165a3bc0..443f78155 100644 --- a/neutronclient/common/exceptions.py +++ b/neutronclient/common/exceptions.py @@ -13,6 +13,8 @@ # License for the specific language governing permissions and limitations # under the License. +from oslo_utils import encodeutils + from neutronclient._i18n import _ """ @@ -29,6 +31,14 @@ """ +# NOTE: This method is defined here to avoid +# an import loop between common.utils and this module. +def _safe_decode_dict(kwargs): + for k, v in kwargs.items(): + kwargs[k] = encodeutils.safe_decode(v) + return kwargs + + class NeutronException(Exception): """Base Neutron Exception. @@ -42,7 +52,7 @@ def __init__(self, message=None, **kwargs): if message: self.message = message try: - self._error_string = self.message % kwargs + self._error_string = self.message % _safe_decode_dict(kwargs) except Exception: # at least get the core message out if something happened self._error_string = self.message @@ -150,6 +160,10 @@ class IpAddressInUseClient(Conflict): pass +class IpAddressAlreadyAllocatedClient(Conflict): + pass + + class InvalidIpForNetworkClient(BadRequest): pass diff --git a/neutronclient/common/extension.py b/neutronclient/common/extension.py index 90f44a79b..00a035d6c 100644 --- a/neutronclient/common/extension.py +++ b/neutronclient/common/extension.py @@ -42,7 +42,6 @@ def execute(self, parsed_args): class ClientExtensionList(NeutronClientExtension, neutronV20.ListCommand): - def take_action(self, parsed_args): # NOTE(mdietz): Calls 'execute' to provide a consistent pattern # for any implementers adding extensions with diff --git a/neutronclient/common/serializer.py b/neutronclient/common/serializer.py index 828e17f3a..d4c8bbbb6 100644 --- a/neutronclient/common/serializer.py +++ b/neutronclient/common/serializer.py @@ -13,19 +13,11 @@ # License for the specific language governing permissions and limitations # under the License. -import logging - from oslo_serialization import jsonutils -import six from neutronclient._i18n import _ from neutronclient.common import exceptions as exception -LOG = logging.getLogger(__name__) - -if six.PY3: - long = int - class ActionDispatcher(object): """Maps method name to local methods through action name.""" @@ -55,7 +47,7 @@ class JSONDictSerializer(DictSerializer): def default(self, data): def sanitizer(obj): - return six.text_type(obj) + return str(obj) return jsonutils.dumps(data, default=sanitizer) diff --git a/neutronclient/common/utils.py b/neutronclient/common/utils.py index 882eb2c5c..93f92fd19 100644 --- a/neutronclient/common/utils.py +++ b/neutronclient/common/utils.py @@ -21,12 +21,10 @@ import functools import hashlib import logging -import netaddr import os from oslo_utils import encodeutils from oslo_utils import importutils -import six from neutronclient._i18n import _ from neutronclient.common import exceptions @@ -130,7 +128,18 @@ def str2dict(strdict, required_keys=None, optional_keys=None): """ result = {} if strdict: + i = 0 + kvlist = [] for kv in strdict.split(','): + if '=' in kv: + kvlist.append(kv) + i += 1 + elif i == 0: + msg = _("missing value for key '%s'") + raise argparse.ArgumentTypeError(msg % kv) + else: + kvlist[i - 1] = "%s,%s" % (kvlist[i - 1], kv) + for kv in kvlist: key, sep, value = kv.partition('=') if not sep: msg = _("invalid key-value '%s', expected format: key=value") @@ -170,12 +179,12 @@ def http_log_req(_logger, args, kwargs): else: string_parts.append(' %s' % element) - for (key, value) in six.iteritems(kwargs['headers']): + for (key, value) in kwargs['headers'].items(): if key in SENSITIVE_HEADERS: v = value.encode('utf-8') - h = hashlib.sha1(v) + h = hashlib.sha256(v) d = h.hexdigest() - value = "{SHA1}%s" % d + value = "{SHA256}%s" % d header = ' -H "%s: %s"' % (key, value) string_parts.append(header) @@ -195,7 +204,7 @@ def http_log_resp(_logger, resp, body): def _safe_encode_without_obj(data): - if isinstance(data, six.string_types): + if isinstance(data, str): return encodeutils.safe_encode(data) return data @@ -226,11 +235,3 @@ def add_boolean_argument(parser, name, **kwargs): choices=['True', 'true', 'False', 'false'], default=default, **kwargs) - - -def is_valid_cidr(cidr): - try: - netaddr.IPNetwork(cidr) - return True - except Exception: - return False diff --git a/neutronclient/i18n.py b/neutronclient/i18n.py deleted file mode 100644 index 776628fc7..000000000 --- a/neutronclient/i18n.py +++ /dev/null @@ -1,27 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -# TODO(amotoki): Remove this file at the beginning of Nxx cycle. - -from debtcollector import moves - -from neutronclient import _i18n - -message = ("moved to neutronclient._i18n; please migrate to local " - "oslo_i18n usage, as defined at " - "http://docs.openstack.org/developer/oslo.i18n/usage.html") - -_ = moves.moved_function(_i18n._, '_', __name__, message=message) -_LC = moves.moved_function(_i18n._LC, '_LC', __name__, message=message) -_LE = moves.moved_function(_i18n._LE, '_LE', __name__, message=message) -_LW = moves.moved_function(_i18n._LW, '_LW', __name__, message=message) -_LI = moves.moved_function(_i18n._LI, '_LI', __name__, message=message) diff --git a/neutronclient/neutron/client.py b/neutronclient/neutron/client.py index 7961a4ca2..d859d815d 100644 --- a/neutronclient/neutron/client.py +++ b/neutronclient/neutron/client.py @@ -28,7 +28,7 @@ def make_client(instance): """Returns an neutron client.""" neutron_client = utils.get_client_class( API_NAME, - instance._api_version[API_NAME], + instance._api_version, API_VERSIONS, ) instance.initialize() diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py index 4468bfb3a..849a93d9e 100644 --- a/neutronclient/neutron/v2_0/__init__.py +++ b/neutronclient/neutron/v2_0/__init__.py @@ -14,8 +14,6 @@ # under the License. # -from __future__ import print_function - import abc import argparse import functools @@ -25,7 +23,6 @@ from cliff import lister from cliff import show from oslo_serialization import jsonutils -import six from neutronclient._i18n import _ from neutronclient.common import exceptions @@ -132,9 +129,9 @@ def _process_previous_argument(current_arg, _value_number, current_type_str, values_specs): if current_arg is not None: if _value_number == 0 and (current_type_str or _list_flag): - # This kind of argument should have value - raise exceptions.CommandError( - _("Invalid values_specs %s") % ' '.join(values_specs)) + # This kind of argument should have value + raise exceptions.CommandError( + _("Invalid values_specs %s") % ' '.join(values_specs)) if _value_number > 1 or _list_flag or current_type_str == 'list': current_arg.update({'nargs': '+'}) elif _value_number == 0: @@ -185,6 +182,14 @@ def parse_args_to_dict(values_specs): current_item = None # the str after 'type=' current_type_str = None + # dict of allowed types + allowed_type_dict = { + 'bool': utils.str2bool, + 'dict': utils.str2dict, + 'int': int, + 'str': str, + } + for _item in values_specs_copy: if _item.startswith('--'): # Deal with previous argument if any @@ -215,12 +220,16 @@ def parse_args_to_dict(values_specs): _("Invalid values_specs %s") % ' '.join(values_specs)) if 'type' not in current_arg: current_type_str = _item.split('=', 2)[1] - current_arg.update({'type': eval(current_type_str)}) - if current_type_str == 'bool': - current_arg.update({'type': utils.str2bool}) - elif current_type_str == 'dict': - current_arg.update({'type': utils.str2dict}) - continue + if current_type_str in allowed_type_dict: + current_arg['type'] = allowed_type_dict[current_type_str] + continue + else: + raise exceptions.CommandError( + _("Invalid value_specs {valspec}: type {curtypestr}" + " is not supported").format( + valspec=' '.join(values_specs), + curtypestr=current_type_str)) + elif _item == 'list=true': _list_flag = True continue @@ -240,7 +249,6 @@ def parse_args_to_dict(values_specs): if _item.startswith('---'): raise exceptions.CommandError( _("Invalid values_specs %s") % ' '.join(values_specs)) - _values_specs.append(_item) # Deal with last one argument @@ -250,12 +258,12 @@ def parse_args_to_dict(values_specs): # Populate the parser with arguments _parser = argparse.ArgumentParser(add_help=False) - for opt, optspec in six.iteritems(_options): + for opt, optspec in _options.items(): _parser.add_argument(opt, **optspec) _args = _parser.parse_args(_values_specs) result_dict = {} - for opt in six.iterkeys(_options): + for opt in _options.keys(): _opt = opt.split('--', 2)[1] _opt = _opt.replace('-', '_') _value = getattr(_args, _opt) @@ -274,7 +282,7 @@ def _merge_args(qCmd, parsed_args, _extra_values, value_specs): @param values_specs: the unparsed unknown parts """ temp_values = _extra_values.copy() - for key, value in six.iteritems(temp_values): + for key, value in temp_values.items(): if hasattr(parsed_args, key): arg_value = getattr(parsed_args, key) if arg_value is not None and value is not None: @@ -310,8 +318,7 @@ def __new__(cls, name, bases, cls_dict): name, bases, cls_dict) -@six.add_metaclass(NeutronCommandMeta) -class NeutronCommand(command.Command): +class NeutronCommand(command.Command, metaclass=NeutronCommandMeta): values_specs = [] json_indent = None @@ -336,7 +343,7 @@ def get_parser(self, prog_name): parser = super(NeutronCommand, self).get_parser(prog_name) parser.add_argument( '--request-format', - help=_('DEPRECATED! Only JSON request format is supported.'), + help=argparse.SUPPRESS, default='json', choices=['json', ], ) parser.add_argument( @@ -350,10 +357,9 @@ def cleanup_output_data(self, data): pass def format_output_data(self, data): - self.cleanup_output_data(data) # Modify data to make it more readable if self.resource in data: - for k, v in six.iteritems(data[self.resource]): + for k, v in data[self.resource].items(): if isinstance(v, list): value = '\n'.join(jsonutils.dumps( i, indent=self.json_indent) if isinstance(i, dict) @@ -405,7 +411,9 @@ def take_action(self, parsed_args): data = obj_creator(self.parent_id, body) else: data = obj_creator(body) - self.format_output_data(data) + self.cleanup_output_data(data) + if parsed_args.formatter == 'table': + self.format_output_data(data) info = self.resource in data and data[self.resource] or None if info: if parsed_args.formatter == 'table': @@ -413,7 +421,7 @@ def take_action(self, parsed_args): file=self.app.stdout) else: info = {'': ''} - return zip(*sorted(six.iteritems(info))) + return zip(*sorted(info.items())) class UpdateCommand(NeutronCommand): @@ -528,18 +536,20 @@ def _bulk_delete(self, obj_deleter, neutron_client, parsed_args_ids): % {'id': ", ".join(successful_delete), 'resource': self.cmd_resource}, file=self.app.stdout) - if non_existent: - print((_("Unable to find %(resource)s(s) with id(s) " - "'%(id)s'") % - {'resource': self.cmd_resource, - 'id': ", ".join(non_existent)}), - file=self.app.stdout) - if multiple_ids: - print((_("Multiple %(resource)s(s) matches found for name(s)" - " '%(id)s'. Please use an ID to be more specific.")) % - {'resource': self.cmd_resource, - 'id': ", ".join(multiple_ids)}, - file=self.app.stdout) + if non_existent or multiple_ids: + err_msgs = [] + if non_existent: + err_msgs.append((_("Unable to find %(resource)s(s) with id(s) " + "'%(id)s'.") % + {'resource': self.cmd_resource, + 'id': ", ".join(non_existent)})) + if multiple_ids: + err_msgs.append((_("Multiple %(resource)s(s) matches found " + "for name(s) '%(id)s'. Please use an ID " + "to be more specific.") % + {'resource': self.cmd_resource, + 'id': ", ".join(multiple_ids)})) + raise exceptions.NeutronCLIError(message='\n'.join(err_msgs)) def delete_item(self, obj_deleter, neutron_client, item_id): if self.allow_names: @@ -712,16 +722,48 @@ def setup_columns(self, info, parsed_args): # if no -c(s) by user and list_columns, we use columns in # both list_columns and returned resource. # Also Keep their order the same as in list_columns - _columns = [x for x in self.list_columns if x in _columns] + _columns = self._setup_columns_with_tenant_id(self.list_columns, + _columns) - formatters = self._formatters - if hasattr(self, '_formatters_csv') and parsed_args.formatter == 'csv': + if parsed_args.formatter == 'table': + formatters = self._formatters + elif (parsed_args.formatter == 'csv' and + hasattr(self, '_formatters_csv')): formatters = self._formatters_csv + else: + # For other formatters, we use raw value returned from neutron + formatters = {} return (_columns, (utils.get_item_properties( s, _columns, formatters=formatters, ) for s in info), ) + def _setup_columns_with_tenant_id(self, display_columns, avail_columns): + _columns = [x for x in display_columns if x in avail_columns] + if 'tenant_id' in display_columns: + return _columns + if 'tenant_id' not in avail_columns: + return _columns + if not self.is_admin_role(): + return _columns + try: + pos_id = _columns.index('id') + except ValueError: + pos_id = 0 + try: + pos_name = _columns.index('name') + except ValueError: + pos_name = 0 + _columns.insert(max(pos_id, pos_name) + 1, 'tenant_id') + return _columns + + def is_admin_role(self): + client = self.get_client() + auth_ref = client.httpclient.get_auth_ref() + if not auth_ref: + return False + return 'admin' in auth_ref.role_names + def take_action(self, parsed_args): self.set_extra_attrs(parsed_args) data = self.retrieve_list(parsed_args) @@ -774,9 +816,11 @@ def take_action(self, parsed_args): data = obj_shower(_id, self.parent_id, **params) else: data = obj_shower(_id, **params) - self.format_output_data(data) + self.cleanup_output_data(data) + if parsed_args.formatter == 'table': + self.format_output_data(data) resource = data[self.resource] if self.resource in data: - return zip(*sorted(six.iteritems(resource))) + return zip(*sorted(resource.items())) else: return None diff --git a/neutronclient/neutron/v2_0/agentscheduler.py b/neutronclient/neutron/v2_0/agentscheduler.py index 4476b1dd3..e92b10be1 100644 --- a/neutronclient/neutron/v2_0/agentscheduler.py +++ b/neutronclient/neutron/v2_0/agentscheduler.py @@ -14,8 +14,6 @@ # under the License. # -from __future__ import print_function - from neutronclient._i18n import _ from neutronclient.neutron import v2_0 as neutronV20 from neutronclient.neutron.v2_0 import network diff --git a/neutronclient/neutron/v2_0/auto_allocated_topology.py b/neutronclient/neutron/v2_0/auto_allocated_topology.py index 3f0f58baf..a24959e16 100644 --- a/neutronclient/neutron/v2_0/auto_allocated_topology.py +++ b/neutronclient/neutron/v2_0/auto_allocated_topology.py @@ -15,6 +15,7 @@ # import argparse + from cliff import show from oslo_serialization import jsonutils @@ -79,3 +80,33 @@ def take_action(self, parsed_args): return zip(*sorted(data[self.resource].items())) else: return None + + +class DeleteAutoAllocatedTopology(v2_0.NeutronCommand): + """Delete the auto-allocated topology of a given tenant.""" + + resource = 'auto_allocated_topology' + + def get_parser(self, prog_name): + parser = super(DeleteAutoAllocatedTopology, self).get_parser(prog_name) + parser.add_argument( + '--tenant-id', metavar='tenant-id', + help=_('The owner tenant ID.')) + # Allow people to do + # neutron auto-allocated-topology-delete + # (Only useful to users who can look at other tenants' topologies.) + # We use a different name for this arg because the default will + # override whatever is in the named arg otherwise. + parser.add_argument( + 'pos_tenant_id', + help=argparse.SUPPRESS, nargs='?') + return parser + + def take_action(self, parsed_args): + client = self.get_client() + tenant_id = parsed_args.tenant_id or parsed_args.pos_tenant_id + client.delete_auto_allocated_topology(tenant_id) + # It tenant is None, let's be clear on what it means. + tenant_id = tenant_id or 'None (i.e. yours)' + print(_('Deleted topology for tenant %s.') % tenant_id, + file=self.app.stdout) diff --git a/neutronclient/neutron/v2_0/bgp/dragentscheduler.py b/neutronclient/neutron/v2_0/bgp/dragentscheduler.py index 81ce8f1ec..d1e1de63f 100644 --- a/neutronclient/neutron/v2_0/bgp/dragentscheduler.py +++ b/neutronclient/neutron/v2_0/bgp/dragentscheduler.py @@ -14,8 +14,6 @@ # under the License. # -from __future__ import print_function - from neutronclient._i18n import _ from neutronclient.neutron import v2_0 as neutronV20 from neutronclient.neutron.v2_0.bgp import speaker as bgp_speaker diff --git a/neutronclient/neutron/v2_0/bgp/speaker.py b/neutronclient/neutron/v2_0/bgp/speaker.py index 1a70734e5..34ce22eb8 100644 --- a/neutronclient/neutron/v2_0/bgp/speaker.py +++ b/neutronclient/neutron/v2_0/bgp/speaker.py @@ -14,8 +14,6 @@ # under the License. # -from __future__ import print_function - from neutronclient._i18n import _ from neutronclient.common import utils from neutronclient.common import validators @@ -24,7 +22,7 @@ # Allowed BGP Autonomous number range MIN_AS_NUM = 1 -MAX_AS_NUM = 65535 +MAX_AS_NUM = 4294967295 def get_network_id(client, id_or_name): @@ -191,7 +189,7 @@ def take_action(self, parsed_args): neutron_client.remove_peer_from_bgp_speaker(_speaker_id, {'bgp_peer_id': _peer_id}) print(_('Removed BGP peer %(peer)s from BGP speaker %(speaker)s.') % - {'peer': parsed_args.bgp_peer, + {'peer': parsed_args.bgp_peer, 'speaker': parsed_args.bgp_speaker}, file=self.app.stdout) diff --git a/neutronclient/neutron/v2_0/flavor/flavor.py b/neutronclient/neutron/v2_0/flavor/flavor.py index 3058da656..36a052ef2 100644 --- a/neutronclient/neutron/v2_0/flavor/flavor.py +++ b/neutronclient/neutron/v2_0/flavor/flavor.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -from __future__ import print_function - import argparse from neutronclient._i18n import _ diff --git a/neutronclient/neutron/v2_0/floatingip.py b/neutronclient/neutron/v2_0/floatingip.py index 9f0ce089b..5f4ced40a 100644 --- a/neutronclient/neutron/v2_0/floatingip.py +++ b/neutronclient/neutron/v2_0/floatingip.py @@ -14,8 +14,6 @@ # under the License. # -from __future__ import print_function - import argparse from neutronclient._i18n import _ diff --git a/neutronclient/neutron/v2_0/fw/firewallpolicy.py b/neutronclient/neutron/v2_0/fw/firewallpolicy.py index 0c99948eb..3d9331cd6 100644 --- a/neutronclient/neutron/v2_0/fw/firewallpolicy.py +++ b/neutronclient/neutron/v2_0/fw/firewallpolicy.py @@ -14,8 +14,6 @@ # under the License. # -from __future__ import print_function - import argparse from neutronclient._i18n import _ diff --git a/neutronclient/neutron/v2_0/lb/healthmonitor.py b/neutronclient/neutron/v2_0/lb/healthmonitor.py index c2bd70cc9..33dfbf0bc 100644 --- a/neutronclient/neutron/v2_0/lb/healthmonitor.py +++ b/neutronclient/neutron/v2_0/lb/healthmonitor.py @@ -14,8 +14,6 @@ # under the License. # -from __future__ import print_function - from neutronclient._i18n import _ from neutronclient.neutron import v2_0 as neutronV20 @@ -66,8 +64,7 @@ def add_known_arguments(self, parser): parser.add_argument( '--delay', required=True, - help=_('The time in milliseconds between sending probes to ' - 'members.')) + help=_('The time in seconds between sending probes to members.')) parser.add_argument( '--max-retries', required=True, @@ -76,8 +73,8 @@ def add_known_arguments(self, parser): parser.add_argument( '--timeout', required=True, - help=_('Maximum number of milliseconds for a monitor to wait for ' - 'a connection to be established before it times out. The ' + help=_('Maximum number of seconds for a monitor to wait for a ' + 'connection to be established before it times out. The ' 'value must be less than the delay value.')) parser.add_argument( '--type', @@ -145,10 +142,11 @@ def get_parser(self, prog_name): parser = super(DisassociateHealthMonitor, self).get_parser(prog_name) parser.add_argument( 'health_monitor_id', metavar='HEALTH_MONITOR_ID', - help=_('Health monitor to associate.')) + help=_('Health monitor to disassociate.')) parser.add_argument( 'pool_id', metavar='POOL', - help=_('ID of the pool to be associated with the health monitor.')) + help=_('ID of the pool to be disassociated with the health ' + 'monitor.')) return parser def take_action(self, parsed_args): diff --git a/neutronclient/neutron/v2_0/lb/pool.py b/neutronclient/neutron/v2_0/lb/pool.py index afc0cc13e..5684e8e6a 100644 --- a/neutronclient/neutron/v2_0/lb/pool.py +++ b/neutronclient/neutron/v2_0/lb/pool.py @@ -14,9 +14,6 @@ # under the License. # - -import six - from neutronclient._i18n import _ from neutronclient.neutron import v2_0 as neutronV20 @@ -119,6 +116,6 @@ def take_action(self, parsed_args): self.format_output_data(data) stats = data['stats'] if 'stats' in data: - return zip(*sorted(six.iteritems(stats))) + return zip(*sorted(stats.items())) else: return None diff --git a/neutronclient/neutron/v2_0/lb/v2/l7rule.py b/neutronclient/neutron/v2_0/lb/v2/l7rule.py index 1286559f2..3c468cabf 100644 --- a/neutronclient/neutron/v2_0/lb/v2/l7rule.py +++ b/neutronclient/neutron/v2_0/lb/v2/l7rule.py @@ -39,32 +39,32 @@ def add_known_arguments(self, parser): def _add_common_args(parser, is_create=True): - parser.add_argument( - '--type', - required=is_create, - type=utils.convert_to_uppercase, - choices=['HOST_NAME', 'PATH', 'FILE_TYPE', 'HEADER', 'COOKIE'], - help=_('Rule type.')) - parser.add_argument( - '--compare-type', - required=is_create, - type=utils.convert_to_uppercase, - choices=['REGEX', 'STARTS_WITH', 'ENDS_WITH', - 'CONTAINS', 'EQUAL_TO'], - help=_('Rule compare type.')) - parser.add_argument( - '--invert-compare', - dest='invert', - action='store_true', - help=_('Invert the compare type.')) - parser.add_argument( - '--key', - help=_('Key to compare.' - ' Relevant for HEADER and COOKIE types only.')) - parser.add_argument( - '--value', - required=is_create, - help=_('Value to compare.')) + parser.add_argument( + '--type', + required=is_create, + type=utils.convert_to_uppercase, + choices=['HOST_NAME', 'PATH', 'FILE_TYPE', 'HEADER', 'COOKIE'], + help=_('Rule type.')) + parser.add_argument( + '--compare-type', + required=is_create, + type=utils.convert_to_uppercase, + choices=['REGEX', 'STARTS_WITH', 'ENDS_WITH', + 'CONTAINS', 'EQUAL_TO'], + help=_('Rule compare type.')) + parser.add_argument( + '--invert-compare', + dest='invert', + action='store_true', + help=_('Invert the compare type.')) + parser.add_argument( + '--key', + help=_('Key to compare.' + ' Relevant for HEADER and COOKIE types only.')) + parser.add_argument( + '--value', + required=is_create, + help=_('Value to compare.')) def _common_args2body(client, parsed_args, is_create=True): diff --git a/neutronclient/neutron/v2_0/lb/v2/listener.py b/neutronclient/neutron/v2_0/lb/v2/listener.py index 1adac9789..481943e6b 100644 --- a/neutronclient/neutron/v2_0/lb/v2/listener.py +++ b/neutronclient/neutron/v2_0/lb/v2/listener.py @@ -45,7 +45,8 @@ def _add_common_args(parser): '--connection-limit', type=int, help=_('The maximum number of connections per second allowed for ' - 'the vip. Positive integer or -1 for unlimited (default).')) + 'the listener. Positive integer or -1 ' + 'for unlimited (default).')) parser.add_argument( '--default-pool', help=_('Default pool for the listener.')) diff --git a/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py b/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py index 8cc6dffb3..c40809fad 100644 --- a/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py +++ b/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py @@ -124,7 +124,6 @@ class RetrieveLoadBalancerStats(neutronV20.ShowCommand): def take_action(self, parsed_args): neutron_client = self.get_client() - neutron_client.format = parsed_args.request_format loadbalancer_id = neutronV20.find_resourceid_by_name_or_id( self.get_client(), 'loadbalancer', parsed_args.id) params = {} diff --git a/neutronclient/neutron/v2_0/lb/v2/member.py b/neutronclient/neutron/v2_0/lb/v2/member.py index aeae1cb28..ee3c090a9 100644 --- a/neutronclient/neutron/v2_0/lb/v2/member.py +++ b/neutronclient/neutron/v2_0/lb/v2/member.py @@ -15,8 +15,6 @@ # License for the specific language governing permissions and limitations # under the License. # -import argparse - from neutronclient._i18n import _ from neutronclient.common import utils from neutronclient.neutron import v2_0 as neutronV20 @@ -40,17 +38,16 @@ def add_known_arguments(self, parser): def _add_common_args(parser): - parser.add_argument( - '--name', - help=_('Name of the member.')) - parser.add_argument( - '--weight', - help=_('Weight of the member in the pool (default:1, [0..256]).')) + parser.add_argument( + '--name', + help=_('Name of the member.')) + parser.add_argument( + '--weight', + help=_('Weight of the member in the pool (default:1, [0..256]).')) def _parse_common_args(body, parsed_args): - neutronV20.update_dict(parsed_args, body, - ['weight', 'name']) + neutronV20.update_dict(parsed_args, body, ['weight', 'name']) class ListMember(LbaasMemberMixin, neutronV20.ListCommand): @@ -128,27 +125,20 @@ class UpdateMember(neutronV20.UpdateCommand): shadow_resource = 'lbaas_member' def add_known_arguments(self, parser): - parser.add_argument( - '--admin-state-down', - dest='admin_state', action='store_false', - default=argparse.SUPPRESS, - help=_('[DEPRECATED in Mitaka] Set admin state up to false.')) parser.add_argument( 'pool', metavar='POOL', help=_('ID or name of the pool that this member belongs to.')) utils.add_boolean_argument( parser, '--admin-state-up', - dest='admin_state', help=_('Update the administrative state of ' 'the member (True meaning "Up").')) - # ToDo(reedip): After Mitaka, remove admin-state-down _add_common_args(parser) def args2body(self, parsed_args): self.parent_id = _get_pool_id(self.get_client(), parsed_args.pool) body = {} - if hasattr(parsed_args, 'admin_state'): - body['admin_state_up'] = parsed_args.admin_state + if hasattr(parsed_args, "admin_state_up"): + body['admin_state_up'] = parsed_args.admin_state_up _parse_common_args(body, parsed_args) return {self.resource: body} diff --git a/neutronclient/neutron/v2_0/lb/v2/pool.py b/neutronclient/neutron/v2_0/lb/v2/pool.py index b2bd326a9..0af383970 100644 --- a/neutronclient/neutron/v2_0/lb/v2/pool.py +++ b/neutronclient/neutron/v2_0/lb/v2/pool.py @@ -39,7 +39,7 @@ def _get_listener_id(client, listener_id_or_name): client, 'listener', listener_id_or_name) -def _add_common_args(parser): +def _add_common_args(parser, is_create=True): parser.add_argument( '--description', help=_('Description of the pool.')) @@ -47,7 +47,7 @@ def _add_common_args(parser): '--name', help=_('The name of the pool.')) parser.add_argument( '--lb-algorithm', - required=True, + required=is_create, type=utils.convert_to_uppercase, choices=['ROUND_ROBIN', 'LEAST_CONNECTIONS', 'SOURCE_IP'], help=_('The algorithm used to distribute load between the members ' @@ -57,8 +57,7 @@ def _add_common_args(parser): def _parse_common_args(parsed_args): body = {} neutronV20.update_dict(parsed_args, - body, ['description', 'lb_algorithm', 'name', - 'session_persistence']) + body, ['description', 'lb_algorithm', 'name']) return body @@ -67,7 +66,7 @@ class ListPool(neutronV20.ListCommand): resource = 'pool' shadow_resource = 'lbaas_pool' - list_columns = ['id', 'name', 'lb_method', 'protocol', + list_columns = ['id', 'name', 'lb_algorithm', 'protocol', 'admin_state_up'] pagination_support = True sorting_support = True @@ -142,7 +141,8 @@ def args2body(self, parsed_args): body['loadbalancer_id'] = loadbalancer_id body['admin_state_up'] = parsed_args.admin_state neutronV20.update_dict(parsed_args, body, - ['tenant_id', 'protocol']) + ['tenant_id', 'protocol', + 'session_persistence']) return {self.resource: body} @@ -157,17 +157,26 @@ def add_known_arguments(self, parser): parser, '--admin-state-up', help=_('Update the administrative state of ' 'the pool (True meaning "Up").')) - parser.add_argument( + session_group = parser.add_mutually_exclusive_group() + session_group.add_argument( '--session-persistence', metavar='type=TYPE[,cookie_name=COOKIE_NAME]', type=utils.str2dict_type(required_keys=['type'], optional_keys=['cookie_name']), help=_('The type of session persistence to use and associated ' 'cookie name.')) - _add_common_args(parser) + session_group.add_argument( + '--no-session-persistence', + action='store_true', + help=_('Clear session persistence for the pool.')) + _add_common_args(parser, False) def args2body(self, parsed_args): body = _parse_common_args(parsed_args) + if parsed_args.no_session_persistence: + body['session_persistence'] = None + elif parsed_args.session_persistence: + body['session_persistence'] = parsed_args.session_persistence neutronV20.update_dict(parsed_args, body, ['admin_state_up']) return {self.resource: body} diff --git a/neutronclient/neutron/v2_0/metering.py b/neutronclient/neutron/v2_0/metering.py index df81724fc..8f6d2174e 100644 --- a/neutronclient/neutron/v2_0/metering.py +++ b/neutronclient/neutron/v2_0/metering.py @@ -13,6 +13,7 @@ # under the License. from neutronclient._i18n import _ +from neutronclient.common import utils from neutronclient.neutron import v2_0 as neutronv20 @@ -76,6 +77,7 @@ class ShowMeteringLabelRule(neutronv20.ShowCommand): """Show information of a given metering label rule.""" resource = 'metering_label_rule' + allow_names = False class CreateMeteringLabelRule(neutronv20.CreateCommand): @@ -93,6 +95,7 @@ def add_known_arguments(self, parser): parser.add_argument( '--direction', default='ingress', choices=['ingress', 'egress'], + type=utils.convert_to_lowercase, help=_('Direction of traffic, default: ingress.')) parser.add_argument( '--excluded', @@ -115,3 +118,4 @@ class DeleteMeteringLabelRule(neutronv20.DeleteCommand): """Delete a given metering label.""" resource = 'metering_label_rule' + allow_names = False diff --git a/neutronclient/neutron/v2_0/network.py b/neutronclient/neutron/v2_0/network.py index 7a89f5a23..6c68b623a 100644 --- a/neutronclient/neutron/v2_0/network.py +++ b/neutronclient/neutron/v2_0/network.py @@ -33,12 +33,20 @@ def _format_subnets(network): return '' +def args2body_common(body, parsed_args): + neutronV20.update_dict(parsed_args, body, + ['name', 'description']) + + class ListNetwork(neutronV20.ListCommand): """List networks that belong to a given tenant.""" # Length of a query filter on subnet id # id=& (with len(uuid)=36) subnet_id_filter_len = 40 + # Length of a marker in pagination + # &marker= (with len(uuid)=36) + marker_len = 44 resource = 'network' _formatters = {'subnets': _format_subnets, } list_columns = ['id', 'name', 'subnets'] @@ -110,6 +118,8 @@ def _get_subnet_list(sub_ids): subnet_count = len(subnet_ids) max_size = ((self.subnet_id_filter_len * subnet_count) - uri_len_exc.excess) + if self.pagination_support: + max_size -= self.marker_len chunk_size = max_size // self.subnet_id_filter_len subnets = [] for i in range(0, subnet_count, chunk_size): @@ -193,8 +203,8 @@ def add_known_arguments(self, parser): dns.add_dns_argument_create(parser, self.resource, 'domain') def args2body(self, parsed_args): - body = {'name': parsed_args.name, - 'admin_state_up': parsed_args.admin_state} + body = {'admin_state_up': parsed_args.admin_state} + args2body_common(body, parsed_args) neutronV20.update_dict(parsed_args, body, ['shared', 'tenant_id', 'vlan_transparent', @@ -202,11 +212,9 @@ def args2body(self, parsed_args): 'provider:physical_network', 'provider:segmentation_id', 'description']) - self.args2body_qos_policy(parsed_args, body) availability_zone.args2body_az_hint(parsed_args, body) dns.args2body_dns_create(parsed_args, body, 'domain') - return {'network': body} @@ -222,11 +230,18 @@ class UpdateNetwork(neutronV20.UpdateCommand, qos_policy.UpdateQosPolicyMixin): resource = 'network' def add_known_arguments(self, parser): + parser.add_argument( + '--name', + help=_('Name of the network.')) + parser.add_argument( + '--description', + help=_('Description of this network.')) self.add_arguments_qos_policy(parser) dns.add_dns_argument_update(parser, self.resource, 'domain') def args2body(self, parsed_args): body = {} + args2body_common(body, parsed_args) self.args2body_qos_policy(parsed_args, body) dns.args2body_dns_update(parsed_args, body, 'domain') return {'network': body} diff --git a/neutronclient/neutron/v2_0/network_ip_availability.py b/neutronclient/neutron/v2_0/network_ip_availability.py index d1332a21c..ba7af8f80 100644 --- a/neutronclient/neutron/v2_0/network_ip_availability.py +++ b/neutronclient/neutron/v2_0/network_ip_availability.py @@ -12,7 +12,6 @@ # from cliff import show -import six from neutronclient._i18n import _ from neutronclient.neutron import v2_0 as neutronV20 @@ -68,6 +67,6 @@ def take_action(self, parsed_args): self.format_output_data(data) resource = data[self.resource] if self.resource in data: - return zip(*sorted(six.iteritems(resource))) + return zip(*sorted(resource.items())) else: return None diff --git a/neutronclient/neutron/v2_0/port.py b/neutronclient/neutron/v2_0/port.py index b279cf601..3fe73544b 100644 --- a/neutronclient/neutron/v2_0/port.py +++ b/neutronclient/neutron/v2_0/port.py @@ -33,13 +33,6 @@ def _format_fixed_ips(port): return '' -def _format_fixed_ips_csv(port): - try: - return jsonutils.dumps(port['fixed_ips']) - except (TypeError, KeyError): - return '' - - def _add_updatable_args(parser): parser.add_argument( '--name', @@ -94,7 +87,6 @@ class ListPort(neutronV20.ListCommand): resource = 'port' _formatters = {'fixed_ips': _format_fixed_ips, } - _formatters_csv = {'fixed_ips': _format_fixed_ips_csv, } list_columns = ['id', 'name', 'mac_address', 'fixed_ips'] pagination_support = True sorting_support = True @@ -203,7 +195,7 @@ def add_arguments_allowedaddresspairs(self, parser): group_aap = parser.add_mutually_exclusive_group() group_aap.add_argument( '--allowed-address-pair', - metavar='ip_address=IP_ADDR[,mac_address=MAC_ADDR]', + metavar='ip_address=IP_ADDR|CIDR[,mac_address=MAC_ADDR]', default=[], action='append', dest='allowed_address_pairs', @@ -211,6 +203,9 @@ def add_arguments_allowedaddresspairs(self, parser): required_keys=['ip_address'], optional_keys=['mac_address']), help=_('Allowed address pair associated with the port. ' + '"ip_address" parameter is required. IP address or ' + 'CIDR can be specified for "ip_address". ' + '"mac_address" parameter is optional. ' 'You can repeat this option.')) group_aap.add_argument( '--no-allowed-address-pairs', @@ -250,14 +245,16 @@ def add_known_arguments(self, parser): parser.add_argument( '--vnic-type', metavar='', + '| normal | baremetal | smart-nic>', choices=['direct', 'direct-physical', 'macvtap', - 'normal', 'baremetal'], + 'normal', 'baremetal', 'smart-nic'], + type=utils.convert_to_lowercase, help=_('VNIC type for this port.')) parser.add_argument( '--vnic_type', choices=['direct', 'direct-physical', 'macvtap', - 'normal', 'baremetal'], + 'normal', 'baremetal', 'smart-nic'], + type=utils.convert_to_lowercase, help=argparse.SUPPRESS) parser.add_argument( '--binding-profile', diff --git a/neutronclient/neutron/v2_0/qos/bandwidth_limit_rule.py b/neutronclient/neutron/v2_0/qos/bandwidth_limit_rule.py index 1cb36cd8e..27e7b626a 100644 --- a/neutronclient/neutron/v2_0/qos/bandwidth_limit_rule.py +++ b/neutronclient/neutron/v2_0/qos/bandwidth_limit_rule.py @@ -27,18 +27,18 @@ def add_bandwidth_limit_arguments(parser): parser.add_argument( '--max-kbps', - help=_('max bandwidth in kbps.')) + help=_('Maximum bandwidth in kbps.')) parser.add_argument( '--max-burst-kbps', - help=_('max burst bandwidth in kbps.')) + help=_('Maximum burst bandwidth in kbps.')) def update_bandwidth_limit_args2body(parsed_args, body): max_kbps = parsed_args.max_kbps max_burst_kbps = parsed_args.max_burst_kbps if not (max_kbps or max_burst_kbps): - raise exceptions.CommandError(_("Must provide max_kbps" - " or max_burst_kbps option.")) + raise exceptions.CommandError(_("Must provide max-kbps" + " or max-burst-kbps option.")) neutronv20.update_dict(parsed_args, body, ['max_kbps', 'max_burst_kbps', 'tenant_id']) diff --git a/neutronclient/neutron/v2_0/qos/minimum_bandwidth_rule.py b/neutronclient/neutron/v2_0/qos/minimum_bandwidth_rule.py new file mode 100644 index 000000000..0dda0fb3d --- /dev/null +++ b/neutronclient/neutron/v2_0/qos/minimum_bandwidth_rule.py @@ -0,0 +1,111 @@ +# Copyright (c) 2016 Intel Corporation. +# All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +from neutronclient._i18n import _ +from neutronclient.common import utils +from neutronclient.neutron import v2_0 as neutronv20 +from neutronclient.neutron.v2_0.qos import rule as qos_rule + + +MINIMUM_BANDWIDTH_RULE_RESOURCE = 'minimum_bandwidth_rule' + + +def add_minimum_bandwidth_arguments(parser): + parser.add_argument( + '--min-kbps', + required=True, + type=str, + help=_('QoS minimum bandwidth assurance, expressed in kilobits ' + 'per second.')) + # NOTE(ralonsoh): the only direction implemented is "egress". Please, + # refer to the spec (https://review.opendev.org/#/c/316082/). + parser.add_argument( + '--direction', + # NOTE(ihrachys): though server picks the default for us (egress), it's + # better to require the argument to make the UI more explicit and the + # intentions more clear in the future when we add other values for the + # attribute on server side. + required=True, + type=utils.convert_to_lowercase, + choices=['egress'], + help=_('Traffic direction.')) + + +def update_minimum_bandwidth_args2body(parsed_args, body): + neutronv20.update_dict(parsed_args, body, ['min_kbps', 'direction']) + + +class CreateQoSMinimumBandwidthRule(qos_rule.QosRuleMixin, + neutronv20.CreateCommand): + """Create a qos minimum bandwidth rule.""" + + resource = MINIMUM_BANDWIDTH_RULE_RESOURCE + + def add_known_arguments(self, parser): + super(CreateQoSMinimumBandwidthRule, self).add_known_arguments( + parser) + add_minimum_bandwidth_arguments(parser) + + def args2body(self, parsed_args): + body = {} + update_minimum_bandwidth_args2body(parsed_args, body) + return {self.resource: body} + + +class ListQoSMinimumBandwidthRules(qos_rule.QosRuleMixin, + neutronv20.ListCommand): + """List all qos minimum bandwidth rules belonging to the specified policy. + + """ + + resource = MINIMUM_BANDWIDTH_RULE_RESOURCE + _formatters = {} + pagination_support = True + sorting_support = True + + +class ShowQoSMinimumBandwidthRule(qos_rule.QosRuleMixin, + neutronv20.ShowCommand): + """Show information about the given qos minimum bandwidth rule.""" + + resource = MINIMUM_BANDWIDTH_RULE_RESOURCE + allow_names = False + + +class UpdateQoSMinimumBandwidthRule(qos_rule.QosRuleMixin, + neutronv20.UpdateCommand): + """Update the given qos minimum bandwidth rule.""" + + resource = MINIMUM_BANDWIDTH_RULE_RESOURCE + allow_names = False + + def add_known_arguments(self, parser): + super(UpdateQoSMinimumBandwidthRule, self).add_known_arguments( + parser) + add_minimum_bandwidth_arguments(parser) + + def args2body(self, parsed_args): + body = {} + update_minimum_bandwidth_args2body(parsed_args, body) + return {self.resource: body} + + +class DeleteQoSMinimumBandwidthRule(qos_rule.QosRuleMixin, + neutronv20.DeleteCommand): + """Delete a given qos minimum bandwidth rule.""" + + resource = MINIMUM_BANDWIDTH_RULE_RESOURCE + allow_names = False diff --git a/neutronclient/neutron/v2_0/quota.py b/neutronclient/neutron/v2_0/quota.py index 061f87371..a55e29f6d 100644 --- a/neutronclient/neutron/v2_0/quota.py +++ b/neutronclient/neutron/v2_0/quota.py @@ -14,15 +14,12 @@ # under the License. # -from __future__ import print_function - import abc import argparse from cliff import lister from cliff import show from oslo_serialization import jsonutils -import six from neutronclient._i18n import _ from neutronclient.common import exceptions @@ -88,7 +85,7 @@ def take_action(self, parsed_args): info = data[collection] _columns = len(info) > 0 and sorted(info[0].keys()) or [] return (_columns, (utils.get_item_properties(s, _columns) - for s in info)) + for s in info)) class ShowQuotaBase(neutronV20.NeutronCommand, show.ShowOne): @@ -121,7 +118,7 @@ def take_action(self, parsed_args): tenant_id = get_tenant_id(parsed_args, neutron_client) data = self.retrieve_data(tenant_id, neutron_client) if self.resource in data: - return zip(*sorted(six.iteritems(data[self.resource]))) + return zip(*sorted(data[self.resource].items())) return @@ -140,7 +137,7 @@ def retrieve_data(self, tenant_id, neutron_client): class UpdateQuota(neutronV20.NeutronCommand, show.ShowOne): - """Define tenant's quotas not to use defaults.""" + """Update a given tenant's quotas.""" resource = 'quota' @@ -184,7 +181,17 @@ def get_parser(self, prog_name): help=_('The limit of pool members.')) parser.add_argument( '--health-monitor', metavar='health_monitors', + dest='healthmonitor', help=_('The limit of health monitors.')) + parser.add_argument( + '--loadbalancer', metavar='loadbalancers', + help=_('The limit of load balancers.')) + parser.add_argument( + '--listener', metavar='listeners', + help=_('The limit of listeners.')) + parser.add_argument( + '--rbac-policy', metavar='rbac_policies', + help=_('The limit of RBAC policies.')) parser.add_argument( 'pos_tenant_id', help=argparse.SUPPRESS, nargs='?') @@ -197,18 +204,23 @@ def _validate_int(self, name, value): except Exception: message = (_('Quota limit for %(name)s must be an integer') % {'name': name}) - raise exceptions.NeutronClientException(message=message) + raise exceptions.CommandError(message=message) return return_value def args2body(self, parsed_args): quota = {} for resource in ('network', 'subnet', 'port', 'router', 'floatingip', 'security_group', 'security_group_rule', - 'vip', 'pool', 'member', 'health_monitor'): + 'vip', 'pool', 'member', 'healthmonitor', + 'loadbalancer', 'listener', 'rbac_policy'): if getattr(parsed_args, resource): quota[resource] = self._validate_int( resource, getattr(parsed_args, resource)) + if not quota: + raise exceptions.CommandError( + message=_('Must specify a valid resource with new quota ' + 'value')) return {self.resource: quota} def take_action(self, parsed_args): @@ -226,7 +238,7 @@ def take_action(self, parsed_args): tenant_id = get_tenant_id(parsed_args, neutron_client) data = obj_updator(tenant_id, body) if self.resource in data: - for k, v in six.iteritems(data[self.resource]): + for k, v in data[self.resource].items(): if isinstance(v, list): value = "" for _item in v: @@ -239,6 +251,6 @@ def take_action(self, parsed_args): data[self.resource][k] = value elif v is None: data[self.resource][k] = '' - return zip(*sorted(six.iteritems(data[self.resource]))) + return zip(*sorted(data[self.resource].items())) else: return diff --git a/neutronclient/neutron/v2_0/rbac.py b/neutronclient/neutron/v2_0/rbac.py index 46f254af8..8b356d3ec 100644 --- a/neutronclient/neutron/v2_0/rbac.py +++ b/neutronclient/neutron/v2_0/rbac.py @@ -14,6 +14,7 @@ # under the License. from neutronclient._i18n import _ +from neutronclient.common import utils from neutronclient.neutron import v2_0 as neutronV20 # key=object_type: value={key=resource, value=cmd_resource} @@ -66,19 +67,21 @@ def add_known_arguments(self, parser): parser.add_argument( '--type', choices=RBAC_OBJECTS.keys(), required=True, + type=utils.convert_to_lowercase, help=_('Type of the object that RBAC policy affects.')) parser.add_argument( '--target-tenant', + default='*', help=_('ID of the tenant to which the RBAC ' 'policy will be enforced.')) parser.add_argument( '--action', choices=['access_as_external', 'access_as_shared'], + type=utils.convert_to_lowercase, required=True, help=_('Action for the RBAC policy.')) def args2body(self, parsed_args): neutron_client = self.get_client() - neutron_client.format = parsed_args.request_format _object_id, _object_type = get_rbac_obj_params(neutron_client, parsed_args.type, parsed_args.name) diff --git a/neutronclient/neutron/v2_0/router.py b/neutronclient/neutron/v2_0/router.py index 200feee18..1f78cb85c 100644 --- a/neutronclient/neutron/v2_0/router.py +++ b/neutronclient/neutron/v2_0/router.py @@ -14,8 +14,6 @@ # under the License. # -from __future__ import print_function - import argparse from oslo_serialization import jsonutils @@ -71,6 +69,9 @@ def add_known_arguments(self, parser): parser.add_argument( '--description', help=_('Description of router.')) + parser.add_argument( + '--flavor', + help=_('ID or name of flavor.')) utils.add_boolean_argument( parser, '--distributed', dest='distributed', help=_('Create a distributed router.')) @@ -82,6 +83,10 @@ def add_known_arguments(self, parser): def args2body(self, parsed_args): body = {'admin_state_up': parsed_args.admin_state} + if parsed_args.flavor: + _flavor_id = neutronV20.find_resourceid_by_name_or_id( + self.get_client(), 'flavor', parsed_args.flavor) + body['flavor_id'] = _flavor_id neutronV20.update_dict(parsed_args, body, ['name', 'tenant_id', 'distributed', 'ha', 'description']) @@ -175,7 +180,7 @@ def take_action(self, parsed_args): resource, value = parsed_args.interface.split('=', 1) if resource not in ['subnet', 'port']: exceptions.CommandError(_('You must specify either subnet or ' - 'port for INTERFACE parameter.')) + 'port for INTERFACE parameter.')) else: resource = 'subnet' value = parsed_args.interface @@ -227,6 +232,9 @@ def get_parser(self, prog_name): parser.add_argument( 'external_network', metavar='EXTERNAL-NETWORK', help=_('ID or name of the external network for the gateway.')) + parser.add_argument( + '--enable-snat', action='store_true', + help=_('Enable source NAT on the router gateway.')) parser.add_argument( '--disable-snat', action='store_true', help=_('Disable source NAT on the router gateway.')) @@ -249,6 +257,8 @@ def take_action(self, parsed_args): _ext_net_id = neutronV20.find_resourceid_by_name_or_id( neutron_client, 'network', parsed_args.external_network) router_dict = {'network_id': _ext_net_id} + if parsed_args.enable_snat: + router_dict['enable_snat'] = True if parsed_args.disable_snat: router_dict['enable_snat'] = False if parsed_args.fixed_ip: diff --git a/neutronclient/neutron/v2_0/securitygroup.py b/neutronclient/neutron/v2_0/securitygroup.py index 8cb89ac1c..3c0bafa0e 100644 --- a/neutronclient/neutron/v2_0/securitygroup.py +++ b/neutronclient/neutron/v2_0/securitygroup.py @@ -18,6 +18,7 @@ from neutronclient._i18n import _ from neutronclient.common import exceptions +from neutronclient.common import utils from neutronclient.neutron import v2_0 as neutronV20 @@ -314,6 +315,7 @@ def add_known_arguments(self, parser): 'which the rule is added.')) parser.add_argument( '--direction', + type=utils.convert_to_lowercase, default='ingress', choices=['ingress', 'egress'], help=_('Direction of traffic: ingress/egress.')) parser.add_argument( @@ -321,6 +323,7 @@ def add_known_arguments(self, parser): help=_('IPv4/IPv6')) parser.add_argument( '--protocol', + type=utils.convert_to_lowercase, help=_('Protocol of packet. Allowed values are ' '[icmp, icmpv6, tcp, udp] and ' 'integer representations [0-255].')) diff --git a/neutronclient/neutron/v2_0/subnet.py b/neutronclient/neutron/v2_0/subnet.py index 107b52543..633826422 100644 --- a/neutronclient/neutron/v2_0/subnet.py +++ b/neutronclient/neutron/v2_0/subnet.py @@ -184,10 +184,12 @@ def add_known_arguments(self, parser): help=_('CIDR of subnet to create.')) parser.add_argument( '--ipv6-ra-mode', + type=utils.convert_to_lowercase, choices=['dhcpv6-stateful', 'dhcpv6-stateless', 'slaac'], help=_('IPv6 RA (Router Advertisement) mode.')) parser.add_argument( '--ipv6-address-mode', + type=utils.convert_to_lowercase, choices=['dhcpv6-stateful', 'dhcpv6-stateless', 'slaac'], help=_('IPv6 address mode.')) parser.add_argument( diff --git a/neutronclient/neutron/v2_0/subnetpool.py b/neutronclient/neutron/v2_0/subnetpool.py index 7d83c1188..fdefc8a47 100644 --- a/neutronclient/neutron/v2_0/subnetpool.py +++ b/neutronclient/neutron/v2_0/subnetpool.py @@ -99,6 +99,7 @@ def add_known_arguments(self, parser): def args2body(self, parsed_args): body = {'prefixes': parsed_args.prefixes} updatable_args2body(parsed_args, body) + neutronV20.update_dict(parsed_args, body, ['tenant_id']) if parsed_args.shared: body['shared'] = True diff --git a/neutronclient/neutron/v2_0/tag.py b/neutronclient/neutron/v2_0/tag.py index 774849cd8..ff59b74ec 100644 --- a/neutronclient/neutron/v2_0/tag.py +++ b/neutronclient/neutron/v2_0/tag.py @@ -16,7 +16,7 @@ # List of resources can be set tag -TAG_RESOURCES = ['network'] +TAG_RESOURCES = ['network', 'subnet', 'port', 'router', 'subnetpool'] def _convert_resource_args(client, parsed_args): @@ -50,6 +50,9 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.get_client() + if not parsed_args.tag: + raise exceptions.CommandError( + _('Cannot add an empty value as tag')) resource_type, resource_id = _convert_resource_args(client, parsed_args) client.add_tag(resource_type, resource_id, parsed_args.tag) diff --git a/neutronclient/neutron/v2_0/vpn/ikepolicy.py b/neutronclient/neutron/v2_0/vpn/ikepolicy.py index 2a5d91b3b..20a96aaea 100644 --- a/neutronclient/neutron/v2_0/vpn/ikepolicy.py +++ b/neutronclient/neutron/v2_0/vpn/ikepolicy.py @@ -14,12 +14,64 @@ # under the License. # +import argparse + from neutronclient._i18n import _ from neutronclient.common import utils from neutronclient.neutron import v2_0 as neutronv20 from neutronclient.neutron.v2_0.vpn import utils as vpn_utils +def add_common_args(parser, is_create=True): + parser.add_argument( + '--description', + help=_('Description of the IKE policy.')) + parser.add_argument( + '--auth-algorithm', + type=utils.convert_to_lowercase, + default='sha1' if is_create else argparse.SUPPRESS, + choices=['sha1', 'sha256', 'sha384', 'sha512'], + help=_('Authentication algorithm, default:sha1.')) + parser.add_argument( + '--encryption-algorithm', + default='aes-128' if is_create else argparse.SUPPRESS, + type=utils.convert_to_lowercase, + help=_('Encryption algorithm, default:aes-128.')) + parser.add_argument( + '--phase1-negotiation-mode', + default='main' if is_create else argparse.SUPPRESS, + choices=['main'], + type=utils.convert_to_lowercase, + help=_('IKE Phase1 negotiation mode, default:main.')) + parser.add_argument( + '--ike-version', + default='v1' if is_create else argparse.SUPPRESS, + choices=['v1', 'v2'], + type=utils.convert_to_lowercase, + help=_('IKE version for the policy, default:v1.')) + parser.add_argument( + '--pfs', + default='group5' if is_create else argparse.SUPPRESS, + type=utils.convert_to_lowercase, + help=_('Perfect Forward Secrecy, default:group5.')) + parser.add_argument( + '--lifetime', + metavar="units=UNITS,value=VALUE", + type=utils.str2dict_type(optional_keys=['units', 'value']), + help=vpn_utils.lifetime_help("IKE")) + + +def parse_common_args2body(parsed_args, body): + neutronv20.update_dict(parsed_args, body, + ['auth_algorithm', 'encryption_algorithm', + 'phase1_negotiation_mode', 'ike_version', + 'pfs', 'name', 'description', 'tenant_id']) + if parsed_args.lifetime: + vpn_utils.validate_lifetime_dict(parsed_args.lifetime) + body['lifetime'] = parsed_args.lifetime + return body + + class ListIKEPolicy(neutronv20.ListCommand): """List IKE policies that belong to a tenant.""" @@ -44,50 +96,13 @@ class CreateIKEPolicy(neutronv20.CreateCommand): resource = 'ikepolicy' def add_known_arguments(self, parser): - parser.add_argument( - '--description', - help=_('Description of the IKE policy')) - parser.add_argument( - '--auth-algorithm', - default='sha1', choices=['sha1', 'sha256'], - help=_('Authentication algorithm in lowercase. ' - 'Default:sha1')) - parser.add_argument( - '--encryption-algorithm', - default='aes-128', - help=_('Encryption algorithm in lowercase, default:aes-128')) - parser.add_argument( - '--phase1-negotiation-mode', - default='main', choices=['main'], - help=_('IKE Phase1 negotiation mode in lowercase, default:main')) - parser.add_argument( - '--ike-version', - default='v1', choices=['v1', 'v2'], - help=_('IKE version in lowercase, default:v1')) - parser.add_argument( - '--pfs', - default='group5', choices=['group2', 'group5', 'group14'], - help=_('Perfect Forward Secrecy in lowercase, default:group5')) - parser.add_argument( - '--lifetime', - metavar="units=UNITS,value=VALUE", - type=utils.str2dict_type(optional_keys=['units', 'value']), - help=vpn_utils.lifetime_help("IKE")) parser.add_argument( 'name', metavar='NAME', help=_('Name of the IKE policy.')) + add_common_args(parser) def args2body(self, parsed_args): - - body = {} - neutronv20.update_dict(parsed_args, body, - ['auth_algorithm', 'encryption_algorithm', - 'phase1_negotiation_mode', 'ike_version', - 'pfs', 'name', 'description', 'tenant_id']) - if parsed_args.lifetime: - vpn_utils.validate_lifetime_dict(parsed_args.lifetime) - body['lifetime'] = parsed_args.lifetime - return {'ikepolicy': body} + return {'ikepolicy': parse_common_args2body(parsed_args, body={})} class UpdateIKEPolicy(neutronv20.UpdateCommand): @@ -98,18 +113,12 @@ class UpdateIKEPolicy(neutronv20.UpdateCommand): def add_known_arguments(self, parser): parser.add_argument( - '--lifetime', - metavar="units=UNITS,value=VALUE", - type=utils.str2dict_type(optional_keys=['units', 'value']), - help=vpn_utils.lifetime_help("IKE")) + '--name', + help=_('Updated name of the IKE policy.')) + add_common_args(parser, is_create=False) def args2body(self, parsed_args): - - body = {} - if parsed_args.lifetime: - vpn_utils.validate_lifetime_dict(parsed_args.lifetime) - body['lifetime'] = parsed_args.lifetime - return {'ikepolicy': body} + return {'ikepolicy': parse_common_args2body(parsed_args, body={})} class DeleteIKEPolicy(neutronv20.DeleteCommand): diff --git a/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py b/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py index 503d90163..03a635f6e 100644 --- a/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py +++ b/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py @@ -14,6 +14,8 @@ # under the License. # +import argparse + from oslo_serialization import jsonutils from neutronclient._i18n import _ @@ -51,7 +53,13 @@ class ShowIPsecSiteConnection(neutronv20.ShowCommand): class IPsecSiteConnectionMixin(object): - def add_known_arguments(self, parser): + def add_known_arguments(self, parser, is_create=True): + parser.add_argument( + '--name', + help=_('Set friendly name for the connection.')) + parser.add_argument( + '--description', + help=_('Set a description for the connection.')) parser.add_argument( '--dpd', metavar="action=ACTION,interval=INTERVAL,timeout=TIMEOUT", @@ -61,11 +69,39 @@ def add_known_arguments(self, parser): parser.add_argument( '--local-ep-group', help=_('Local endpoint group ID/name with subnet(s) for ' - 'the IPsec connection.')) + 'IPSec connection.')) parser.add_argument( '--peer-ep-group', help=_('Peer endpoint group ID/name with CIDR(s) for ' - 'the IPsec connection.')) + 'IPSec connection.')) + parser.add_argument( + '--peer-cidr', + action='append', dest='peer_cidrs', + help=_('[DEPRECATED in Mitaka] Remote subnet(s) in CIDR format. ' + 'Cannot be specified when using endpoint groups. Only ' + 'applicable, if subnet provided for VPN service.')) + parser.add_argument( + '--peer-id', + required=is_create, + help=_('Peer router identity for authentication. Can be ' + 'IPv4/IPv6 address, e-mail address, key id, or FQDN.')) + parser.add_argument( + '--peer-address', + required=is_create, + help=_('Peer gateway public IPv4/IPv6 address or FQDN.')) + parser.add_argument( + '--psk', + required=is_create, + help=_('Pre-shared key string.')) + parser.add_argument( + '--mtu', + default='1500' if is_create else argparse.SUPPRESS, + help=_('MTU size for the connection, default:1500.')) + parser.add_argument( + '--initiator', + default='bi-directional' if is_create else argparse.SUPPRESS, + choices=['bi-directional', 'response-only'], + help=_('Initiator state in lowercase, default:bi-directional')) def args2body(self, parsed_args, body=None): """Add in conditional args and then return all conn info.""" @@ -85,6 +121,19 @@ def args2body(self, parsed_args, body=None): self.get_client(), 'endpoint_group', parsed_args.peer_ep_group) body['peer_ep_group_id'] = _peer_epg + if hasattr(parsed_args, 'mtu') and int(parsed_args.mtu) < 68: + message = _("Invalid MTU value: MTU must be " + "greater than or equal to 68.") + raise exceptions.CommandError(message) + # ToDo (Reedip) : Remove below check when peer-cidr is removed + if parsed_args.peer_cidrs and parsed_args.local_ep_group: + message = _("You cannot specify both endpoint groups and peer " + "CIDR(s).") + raise exceptions.CommandError(message) + neutronv20.update_dict(parsed_args, body, + ['peer_id', 'mtu', 'initiator', 'psk', + 'peer_address', 'name', 'description', + 'peer_cidrs']) return {self.resource: body} @@ -98,21 +147,6 @@ def add_known_arguments(self, parser): '--admin-state-down', default=True, action='store_false', help=_('Set admin state up to false.')) - parser.add_argument( - '--name', - help=_('Set a name for the connection.')) - parser.add_argument( - '--description', - help=_('Set a description for the connection.')) - parser.add_argument( - '--mtu', - default='1500', - help=_('MTU size for the connection, default:1500')) - parser.add_argument( - '--initiator', - default='bi-directional', choices=['bi-directional', - 'response-only'], - help=_('Initiator state in lowercase, default:bi-directional')) parser.add_argument( '--vpnservice-id', metavar='VPNSERVICE', required=True, @@ -125,25 +159,6 @@ def add_known_arguments(self, parser): '--ipsecpolicy-id', metavar='IPSECPOLICY', required=True, help=_('IPsec policy ID associated with this connection.')) - parser.add_argument( - '--peer-address', - required=True, - help=_('Peer gateway public IPv4/IPv6 address or FQDN.')) - parser.add_argument( - '--peer-id', - required=True, - help=_('Peer router identity for authentication. Can be ' - 'IPv4/IPv6 address, e-mail address, key id, or FQDN.')) - parser.add_argument( - '--peer-cidr', - action='append', dest='peer_cidrs', - help=_('[DEPRECATED in Mitaka] Remote subnet(s) in CIDR format. ' - 'Cannot be specified when using endpoint groups. Only ' - 'applicable, if subnet provided for VPN service.')) - parser.add_argument( - '--psk', - required=True, - help=_('Pre-shared key string.')) super(CreateIPsecSiteConnection, self).add_known_arguments(parser) def args2body(self, parsed_args): @@ -156,39 +171,23 @@ def args2body(self, parsed_args): _ipsecpolicy_id = neutronv20.find_resourceid_by_name_or_id( self.get_client(), 'ipsecpolicy', parsed_args.ipsecpolicy_id) - if int(parsed_args.mtu) < 68: - message = _("Invalid MTU value: MTU must be " - "greater than or equal to 68") - raise exceptions.CommandError(message) - if (bool(parsed_args.local_ep_group) != - bool(parsed_args.peer_ep_group)): - message = _("You must specify both local and peer endpoint " - "groups.") - raise exceptions.CommandError(message) - if parsed_args.peer_cidrs and parsed_args.local_ep_group: - message = _("You cannot specify both endpoint groups and peer " - "CIDR(s).") - raise exceptions.CommandError(message) - if not parsed_args.peer_cidrs and not parsed_args.local_ep_group: - message = _("You must specify endpoint groups or peer CIDR(s).") - raise exceptions.CommandError(message) body = { 'vpnservice_id': _vpnservice_id, 'ikepolicy_id': _ikepolicy_id, 'ipsecpolicy_id': _ipsecpolicy_id, 'admin_state_up': parsed_args.admin_state_down, } - neutronv20.update_dict(parsed_args, body, - ['peer_id', 'mtu', 'initiator', 'psk', - 'peer_address']) - if parsed_args.name: - body['name'] = parsed_args.name - if parsed_args.description: - body['description'] = parsed_args.description if parsed_args.tenant_id: body['tenant_id'] = parsed_args.tenant_id - if parsed_args.peer_cidrs: - body['peer_cidrs'] = parsed_args.peer_cidrs + + if (bool(parsed_args.local_ep_group) != + bool(parsed_args.peer_ep_group)): + message = _("You must specify both local and peer endpoint " + "groups.") + raise exceptions.CommandError(message) + if not parsed_args.peer_cidrs and not parsed_args.local_ep_group: + message = _("You must specify endpoint groups or peer CIDR(s).") + raise exceptions.CommandError(message) return super(CreateIPsecSiteConnection, self).args2body(parsed_args, body) @@ -200,6 +199,19 @@ class UpdateIPsecSiteConnection(IPsecSiteConnectionMixin, resource = 'ipsec_site_connection' help_resource = 'IPsec site connection' + def add_known_arguments(self, parser): + utils.add_boolean_argument( + parser, '--admin-state-up', + help=_('Update the administrative state. (True meaning "Up")')) + super(UpdateIPsecSiteConnection, self).add_known_arguments(parser, + False) + + def args2body(self, parsed_args): + body = {} + neutronv20.update_dict(parsed_args, body, ['admin_state_up']) + return super(UpdateIPsecSiteConnection, self).args2body(parsed_args, + body) + class DeleteIPsecSiteConnection(neutronv20.DeleteCommand): """Delete a given IPsec site connection.""" diff --git a/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py b/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py index 5da45158e..ea70155b7 100644 --- a/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py +++ b/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py @@ -14,12 +14,64 @@ # under the License. # +import argparse + from neutronclient._i18n import _ from neutronclient.common import utils from neutronclient.neutron import v2_0 as neutronv20 from neutronclient.neutron.v2_0.vpn import utils as vpn_utils +def add_common_args(parser, is_create=True): + parser.add_argument( + '--auth-algorithm', + default='sha1' if is_create else argparse.SUPPRESS, + type=utils.convert_to_lowercase, + choices=['sha1', 'sha256', 'sha384', 'sha512'], + help=_('Authentication algorithm for IPsec policy, default:sha1.')) + parser.add_argument( + '--description', + help=_('Description of the IPsec policy.')) + parser.add_argument( + '--encapsulation-mode', + default='tunnel' if is_create else argparse.SUPPRESS, + choices=['tunnel', 'transport'], + type=utils.convert_to_lowercase, + help=_('Encapsulation mode for IPsec policy, default:tunnel.')) + parser.add_argument( + '--encryption-algorithm', + default='aes-128' if is_create else argparse.SUPPRESS, + type=utils.convert_to_lowercase, + help=_('Encryption algorithm for IPsec policy, default:aes-128.')) + parser.add_argument( + '--lifetime', + metavar="units=UNITS,value=VALUE", + type=utils.str2dict_type(optional_keys=['units', 'value']), + help=vpn_utils.lifetime_help("IPsec")) + parser.add_argument( + '--pfs', + default='group5' if is_create else argparse.SUPPRESS, + type=utils.convert_to_lowercase, + help=_('Perfect Forward Secrecy for IPsec policy, default:group5.')) + parser.add_argument( + '--transform-protocol', + default='esp' if is_create else argparse.SUPPRESS, + type=utils.convert_to_lowercase, + choices=['esp', 'ah', 'ah-esp'], + help=_('Transform protocol for IPsec policy, default:esp.')) + + +def parse_common_args2body(parsed_args, body): + neutronv20.update_dict(parsed_args, body, + ['auth_algorithm', 'encryption_algorithm', + 'encapsulation_mode', 'transform_protocol', + 'pfs', 'name', 'description', 'tenant_id']) + if parsed_args.lifetime: + vpn_utils.validate_lifetime_dict(parsed_args.lifetime) + body['lifetime'] = parsed_args.lifetime + return body + + class ListIPsecPolicy(neutronv20.ListCommand): """List IPsec policies that belong to a given tenant connection.""" @@ -42,51 +94,16 @@ class CreateIPsecPolicy(neutronv20.CreateCommand): """Create an IPsec policy.""" resource = 'ipsecpolicy' + help_resource = 'IPsec policy' def add_known_arguments(self, parser): - parser.add_argument( - '--description', - help=_('Description of the IPsec policy.')) - parser.add_argument( - '--transform-protocol', - default='esp', choices=['esp', 'ah', 'ah-esp'], - help=_('Transform protocol in lowercase, default:esp')) - parser.add_argument( - '--auth-algorithm', - default='sha1', choices=['sha1', 'sha256'], - help=_('Authentication algorithm in lowercase, default:sha1')) - parser.add_argument( - '--encryption-algorithm', - default='aes-128', - help=_('Encryption algorithm in lowercase, default:aes-128')) - parser.add_argument( - '--encapsulation-mode', - default='tunnel', choices=['tunnel', 'transport'], - help=_('Encapsulation mode in lowercase, default:tunnel')) - parser.add_argument( - '--pfs', - default='group5', choices=['group2', 'group5', 'group14'], - help=_('Perfect Forward Secrecy in lowercase, default:group5')) - parser.add_argument( - '--lifetime', - metavar="units=UNITS,value=VALUE", - type=utils.str2dict_type(optional_keys=['units', 'value']), - help=vpn_utils.lifetime_help("IPsec")) parser.add_argument( 'name', metavar='NAME', help=_('Name of the IPsec policy.')) + add_common_args(parser) def args2body(self, parsed_args): - - body = {} - neutronv20.update_dict(parsed_args, body, - ['auth_algorithm', 'encryption_algorithm', - 'encapsulation_mode', 'transform_protocol', - 'pfs', 'name', 'description', 'tenant_id']) - if parsed_args.lifetime: - vpn_utils.validate_lifetime_dict(parsed_args.lifetime) - body['lifetime'] = parsed_args.lifetime - return {'ipsecpolicy': body} + return {'ipsecpolicy': parse_common_args2body(parsed_args, body={})} class UpdateIPsecPolicy(neutronv20.UpdateCommand): @@ -97,18 +114,12 @@ class UpdateIPsecPolicy(neutronv20.UpdateCommand): def add_known_arguments(self, parser): parser.add_argument( - '--lifetime', - metavar="units=UNITS,value=VALUE", - type=utils.str2dict_type(optional_keys=['units', 'value']), - help=vpn_utils.lifetime_help("IPsec")) + '--name', + help=_('Updated name of the IPsec policy.')) + add_common_args(parser, is_create=False) def args2body(self, parsed_args): - - body = {} - if parsed_args.lifetime: - vpn_utils.validate_lifetime_dict(parsed_args.lifetime) - body['lifetime'] = parsed_args.lifetime - return {'ipsecpolicy': body} + return {'ipsecpolicy': parse_common_args2body(parsed_args, body={})} class DeleteIPsecPolicy(neutronv20.DeleteCommand): diff --git a/neutronclient/neutron/v2_0/vpn/vpnservice.py b/neutronclient/neutron/v2_0/vpn/vpnservice.py index 9e9ae1221..dae531408 100644 --- a/neutronclient/neutron/v2_0/vpn/vpnservice.py +++ b/neutronclient/neutron/v2_0/vpn/vpnservice.py @@ -15,9 +15,24 @@ # from neutronclient._i18n import _ +from neutronclient.common import utils from neutronclient.neutron import v2_0 as neutronv20 +def add_common_args(parser): + parser.add_argument( + '--name', + help=_('Name for the VPN service.')) + parser.add_argument( + '--description', + help=_('Description for the VPN service.')) + + +def common_args2body(parsed_args, body): + neutronv20.update_dict(parsed_args, body, + ['name', 'description']) + + class ListVPNService(neutronv20.ListCommand): """List VPN service configurations that belong to a given tenant.""" @@ -46,12 +61,6 @@ def add_known_arguments(self, parser): '--admin-state-down', dest='admin_state', action='store_false', help=_('Set admin state up to false.')) - parser.add_argument( - '--name', - help=_('Set a name for the VPN service.')) - parser.add_argument( - '--description', - help=_('Set a description for the VPN service.')) parser.add_argument( 'router', metavar='ROUTER', help=_('Router unique identifier for the VPN service.')) @@ -59,6 +68,7 @@ def add_known_arguments(self, parser): 'subnet', nargs='?', metavar='SUBNET', help=_('[DEPRECATED in Mitaka] Unique identifier for the local ' 'private subnet.')) + add_common_args(parser) def args2body(self, parsed_args): if parsed_args.subnet: @@ -74,9 +84,8 @@ def args2body(self, parsed_args): 'router_id': _router_id, 'admin_state_up': parsed_args.admin_state} neutronv20.update_dict(parsed_args, body, - ['name', 'description', - 'tenant_id']) - + ['tenant_id']) + common_args2body(parsed_args, body) return {self.resource: body} @@ -86,6 +95,20 @@ class UpdateVPNService(neutronv20.UpdateCommand): resource = 'vpnservice' help_resource = 'VPN service' + def add_known_arguments(self, parser): + add_common_args(parser) + utils.add_boolean_argument( + parser, '--admin-state-up', + help=_('Update the admin state for the VPN Service.' + '(True means UP)')) + + def args2body(self, parsed_args): + body = {} + common_args2body(parsed_args, body) + neutronv20.update_dict(parsed_args, body, + ['admin_state_up']) + return {self.resource: body} + class DeleteVPNService(neutronv20.DeleteCommand): """Delete a given VPN service.""" diff --git a/neutronclient/osc/plugin.py b/neutronclient/osc/plugin.py index 1efb6c5f7..c98099f2a 100644 --- a/neutronclient/osc/plugin.py +++ b/neutronclient/osc/plugin.py @@ -11,11 +11,13 @@ # under the License. # +"""OpenStackClient plugin for advanced Networking service.""" + import logging # TODO(rtheis/amotoki): Add functional test infrastructure for OSC # plugin commands. -# TODO(amotoki): Add and update document on OSC pluign. +# TODO(amotoki): Add and update document on OSC plugin. from osc_lib import utils @@ -46,10 +48,10 @@ def make_client(instance): # - endpoint_type (do we need to specify it explicitly?) # - auth (session object contains auth. Is it required?) client = neutron_client(session=instance.session, - region_name=instance._region_name, - endpoint_type=instance._interface, - insecure=instance._insecure, - ca_cert=instance._cacert) + region_name=instance.region_name, + endpoint_type=instance.interface, + insecure=not instance.verify, + ca_cert=instance.cacert) return client diff --git a/neutronclient/osc/utils.py b/neutronclient/osc/utils.py new file mode 100644 index 000000000..39b17b640 --- /dev/null +++ b/neutronclient/osc/utils.py @@ -0,0 +1,130 @@ +# Copyright 2016 NEC Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +"""This module should contain OSC plugin generic methods. + +Methods in this module are candidates adopted to osc-lib. + +Stuffs specific to neutronclient OSC plugin should not be added +to this module. They should go to neutronclient.osc.v2.utils. +""" + +from keystoneclient import exceptions as identity_exc +from keystoneclient.v3 import domains +from keystoneclient.v3 import projects +from osc_lib import utils + +from neutronclient._i18n import _ + + +# TODO(amotoki): Use osc-lib version once osc-lib provides this. +def add_project_owner_option_to_parser(parser): + """Register project and project domain options. + + :param parser: argparse.Argument parser object. + + """ + parser.add_argument( + '--project', + metavar='', + help=_("Owner's project (name or ID)") + ) + # Borrowed from openstackclient.identity.common + # as it is not exposed officially. + parser.add_argument( + '--project-domain', + metavar='', + help=_('Domain the project belongs to (name or ID). ' + 'This can be used in case collisions between project names ' + 'exist.'), + ) + + +# The following methods are borrowed from openstackclient.identity.common +# as it is not exposed officially. +# TODO(amotoki): Use osc-lib version once osc-lib provides this. + + +def find_domain(identity_client, name_or_id): + return _find_identity_resource(identity_client.domains, name_or_id, + domains.Domain) + + +def find_project(identity_client, name_or_id, domain_name_or_id=None): + domain_id = _get_domain_id_if_requested(identity_client, domain_name_or_id) + if not domain_id: + return _find_identity_resource(identity_client.projects, name_or_id, + projects.Project) + else: + return _find_identity_resource(identity_client.projects, name_or_id, + projects.Project, domain_id=domain_id) + + +def _get_domain_id_if_requested(identity_client, domain_name_or_id): + if not domain_name_or_id: + return None + domain = find_domain(identity_client, domain_name_or_id) + return domain.id + + +def _find_identity_resource(identity_client_manager, name_or_id, + resource_type, **kwargs): + """Find a specific identity resource. + + Using keystoneclient's manager, attempt to find a specific resource by its + name or ID. If Forbidden to find the resource (a common case if the user + does not have permission), then return the resource by creating a local + instance of keystoneclient's Resource. + + The parameter identity_client_manager is a keystoneclient manager, + for example: keystoneclient.v3.users or keystoneclient.v3.projects. + + The parameter resource_type is a keystoneclient resource, for example: + keystoneclient.v3.users.User or keystoneclient.v3.projects.Project. + + :param identity_client_manager: the manager that contains the resource + :type identity_client_manager: `keystoneclient.base.CrudManager` + :param name_or_id: the resources's name or ID + :type name_or_id: string + :param resource_type: class that represents the resource type + :type resource_type: `keystoneclient.base.Resource` + + :returns: the resource in question + :rtype: `keystoneclient.base.Resource` + + """ + + try: + identity_resource = utils.find_resource(identity_client_manager, + name_or_id, **kwargs) + if identity_resource is not None: + return identity_resource + except identity_exc.Forbidden: + pass + + return resource_type(None, {'id': name_or_id, 'name': name_or_id}) + + +# The above are borrowed from openstackclient.identity.common. +# DO NOT ADD original methods in neutronclient repo to the above area. + + +def _get_columns(item): + column_map = {} + hidden_columns = ['location', 'tenant_id'] + return utils.get_osc_show_columns_for_sdk_resource( + item, + column_map, + hidden_columns + ) diff --git a/neutronclient/osc/v2/dynamic_routing/bgp_dragent.py b/neutronclient/osc/v2/dynamic_routing/bgp_dragent.py new file mode 100644 index 000000000..91dc2a75b --- /dev/null +++ b/neutronclient/osc/v2/dynamic_routing/bgp_dragent.py @@ -0,0 +1,112 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# 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 osc_lib.command import command +from osc_lib import utils + +from neutronclient._i18n import _ + + +def _format_alive_state(item): + return ':-)' if item else 'XXX' + + +_formatters = { + 'alive': _format_alive_state +} + + +def add_common_args(parser): + parser.add_argument('dragent_id', + metavar='', + help=_("ID of the dynamic routing agent")) + parser.add_argument('bgp_speaker', + metavar='', + help=_("ID or name of the BGP speaker")) + + +class AddBgpSpeakerToDRAgent(command.Command): + """Add a BGP speaker to a dynamic routing agent""" + + def get_parser(self, prog_name): + parser = super(AddBgpSpeakerToDRAgent, self).get_parser(prog_name) + add_common_args(parser) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + speaker_id = client.find_bgp_speaker(parsed_args.bgp_speaker).id + client.add_bgp_speaker_to_dragent(parsed_args.dragent_id, speaker_id) + + +class RemoveBgpSpeakerFromDRAgent(command.Command): + """Removes a BGP speaker from a dynamic routing agent""" + + def get_parser(self, prog_name): + parser = super(RemoveBgpSpeakerFromDRAgent, self).get_parser( + prog_name) + add_common_args(parser) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + speaker_id = client.find_bgp_speaker(parsed_args.bgp_speaker).id + client.remove_bgp_speaker_from_dragent(parsed_args.dragent_id, + speaker_id) + + +class ListDRAgent(command.Lister): + """List dynamic routing agents""" + + resource = 'agent' + list_columns = ['id', 'host', 'admin_state_up', 'alive'] + unknown_parts_flag = False + + def get_parser(self, prog_name): + parser = super(ListDRAgent, + self).get_parser(prog_name) + parser.add_argument('--bgp-speaker', + metavar='', + help=_("List dynamic routing agents hosting a " + "BGP speaker (name or ID)")) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + if parsed_args.bgp_speaker is not None: + speaker_id = client.find_bgp_speaker(parsed_args.bgp_speaker).id + data = client.get_bgp_dragents_hosting_speaker(speaker_id) + else: + attrs = {'agent_type': 'BGP dynamic routing agent'} + data = client.agents(**attrs) + columns = ( + 'id', + 'agent_type', + 'host', + 'availability_zone', + 'is_alive', + 'is_admin_state_up', + 'binary' + ) + column_headers = ( + 'ID', + 'Agent Type', + 'Host', + 'Availability Zone', + 'Alive', + 'State', + 'Binary' + ) + return (column_headers, + (utils.get_item_properties( + s, columns,) for s in data)) diff --git a/neutronclient/osc/v2/dynamic_routing/bgp_peer.py b/neutronclient/osc/v2/dynamic_routing/bgp_peer.py new file mode 100644 index 000000000..0610a3ac0 --- /dev/null +++ b/neutronclient/osc/v2/dynamic_routing/bgp_peer.py @@ -0,0 +1,181 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# 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 osc_lib.command import command +from osc_lib import utils + +from neutronclient._i18n import _ +from neutronclient.common import exceptions +from neutronclient.common import utils as nc_utils +from neutronclient.osc import utils as nc_osc_utils +from neutronclient.osc.v2.dynamic_routing import constants + + +def _get_attrs(client_manager, parsed_args): + attrs = {} + + # Validate password + if 'auth_type' in parsed_args: + if parsed_args.auth_type != 'none': + if 'password' not in parsed_args or parsed_args.password is None: + raise exceptions.CommandError(_('Must provide password if ' + 'auth-type is specified.')) + if ( + parsed_args.auth_type == 'none' and + parsed_args.password is not None + ): + raise exceptions.CommandError(_('Must provide auth-type if ' + 'password is specified.')) + attrs['auth_type'] = parsed_args.auth_type + + if parsed_args.name is not None: + attrs['name'] = parsed_args.name + if 'remote_as' in parsed_args: + attrs['remote_as'] = parsed_args.remote_as + if 'peer_ip' in parsed_args: + attrs['peer_ip'] = parsed_args.peer_ip + if 'password' in parsed_args: + attrs['password'] = parsed_args.password + + if 'project' in parsed_args and parsed_args.project is not None: + identity_client = client_manager.identity + project_id = nc_osc_utils.find_project( + identity_client, + parsed_args.project, + parsed_args.project_domain, + ).id + attrs['tenant_id'] = project_id + return attrs + + +class CreateBgpPeer(command.ShowOne): + _description = _("Create a BGP peer") + + def get_parser(self, prog_name): + parser = super(CreateBgpPeer, self).get_parser(prog_name) + parser.add_argument( + 'name', + metavar='', + help=_("Name of the BGP peer to create")) + parser.add_argument( + '--peer-ip', + metavar='', + required=True, + help=_("Peer IP address")) + parser.add_argument( + '--remote-as', + required=True, + metavar='', + help=_("Peer AS number. (Integer in [%(min_val)s, %(max_val)s] " + "is allowed)") % {'min_val': constants.MIN_AS_NUM, + 'max_val': constants.MAX_AS_NUM}) + parser.add_argument( + '--auth-type', + metavar='', + choices=['none', 'md5'], + type=nc_utils.convert_to_lowercase, + default='none', + help=_("Authentication algorithm. Supported algorithms: " + "none (default), md5")) + parser.add_argument( + '--password', + metavar='', + help=_("Authentication password")) + nc_osc_utils.add_project_owner_option_to_parser(parser) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + attrs = _get_attrs(self.app.client_manager, parsed_args) + obj = client.create_bgp_peer(**attrs) + display_columns, columns = nc_osc_utils._get_columns(obj) + data = utils.get_dict_properties(obj, columns) + return display_columns, data + + +class DeleteBgpPeer(command.Command): + _description = _("Delete a BGP peer") + + def get_parser(self, prog_name): + parser = super(DeleteBgpPeer, self).get_parser(prog_name) + parser.add_argument( + 'bgp_peer', + metavar="", + help=_("BGP peer to delete (name or ID)") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + id = client.find_bgp_peer(parsed_args.bgp_peer)['id'] + client.delete_bgp_peer(id) + + +class ListBgpPeer(command.Lister): + _description = _("List BGP peers") + + def take_action(self, parsed_args): + data = self.app.client_manager.network.bgp_peers(retrieve_all=True) + headers = ('ID', 'Name', 'Peer IP', 'Remote AS') + columns = ('id', 'name', 'peer_ip', 'remote_as') + return (headers, + (utils.get_dict_properties(s, columns,) for s in data)) + + +class SetBgpPeer(command.Command): + _description = _("Update a BGP peer") + resource = constants.BGP_PEER + + def get_parser(self, prog_name): + parser = super(SetBgpPeer, self).get_parser(prog_name) + parser.add_argument( + '--name', + help=_("Updated name of the BGP peer")) + parser.add_argument( + '--password', + metavar='', + help=_("Updated authentication password")) + parser.add_argument( + 'bgp_peer', + metavar="", + help=_("BGP peer to update (name or ID)") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + id = client.find_bgp_peer(parsed_args.bgp_peer)['id'] + attrs = _get_attrs(self.app.client_manager, parsed_args) + client.update_bgp_peer(id, **attrs) + + +class ShowBgpPeer(command.ShowOne): + _description = _("Show information for a BGP peer") + + def get_parser(self, prog_name): + parser = super(ShowBgpPeer, self).get_parser(prog_name) + parser.add_argument( + 'bgp_peer', + metavar="", + help=_("BGP peer to display (name or ID)") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + id = client.find_bgp_peer(parsed_args.bgp_peer, + ignore_missing=False).id + obj = client.get_bgp_peer(id) + display_columns, columns = nc_osc_utils._get_columns(obj) + data = utils.get_dict_properties(obj, columns) + return display_columns, data diff --git a/neutronclient/osc/v2/dynamic_routing/bgp_speaker.py b/neutronclient/osc/v2/dynamic_routing/bgp_speaker.py new file mode 100644 index 000000000..76d8340b9 --- /dev/null +++ b/neutronclient/osc/v2/dynamic_routing/bgp_speaker.py @@ -0,0 +1,305 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# 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 osc_lib.command import command +from osc_lib import utils + +from neutronclient._i18n import _ +from neutronclient.osc import utils as nc_osc_utils +from neutronclient.osc.v2.dynamic_routing import constants + + +def _get_attrs(client_manager, parsed_args): + attrs = {} + if parsed_args.name is not None: + attrs['name'] = str(parsed_args.name) + if 'local_as' in parsed_args: + attrs['local_as'] = parsed_args.local_as + if 'ip_version' in parsed_args: + attrs['ip_version'] = parsed_args.ip_version + if parsed_args.advertise_tenant_networks: + attrs['advertise_tenant_networks'] = True + if parsed_args.no_advertise_tenant_networks: + attrs['advertise_tenant_networks'] = False + if parsed_args.advertise_floating_ip_host_routes: + attrs['advertise_floating_ip_host_routes'] = True + if parsed_args.no_advertise_floating_ip_host_routes: + attrs['advertise_floating_ip_host_routes'] = False + + if 'project' in parsed_args and parsed_args.project is not None: + identity_client = client_manager.identity + project_id = nc_osc_utils.find_project( + identity_client, + parsed_args.project, + parsed_args.project_domain, + ).id + attrs['tenant_id'] = project_id + return attrs + + +def add_common_arguments(parser): + parser.add_argument( + '--advertise-floating-ip-host-routes', + action='store_true', + help=_("Enable the advertisement of floating IP host routes " + "by the BGP speaker. (default)")) + parser.add_argument( + '--no-advertise-floating-ip-host-routes', + action='store_true', + help=_("Disable the advertisement of floating IP host routes " + "by the BGP speaker.")) + parser.add_argument( + '--advertise-tenant-networks', + action='store_true', + help=_("Enable the advertisement of tenant network routes " + "by the BGP speaker. (default)")) + parser.add_argument( + '--no-advertise-tenant-networks', + action='store_true', + help=_("Disable the advertisement of tenant network routes " + "by the BGP speaker.")) + + +class AddNetworkToSpeaker(command.Command): + _description = _("Add a network to a BGP speaker") + + def get_parser(self, prog_name): + parser = super(AddNetworkToSpeaker, self).get_parser(prog_name) + parser.add_argument( + 'bgp_speaker', + metavar='', + help=_("BGP speaker (name or ID)")) + parser.add_argument( + 'network', + metavar='', + help=_("Network to add (name or ID)")) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + speaker_id = client.find_bgp_speaker(parsed_args.bgp_speaker, + ignore_missing=False).id + net_id = client.find_network(parsed_args.network, + ignore_missing=False).id + client.add_gateway_network_to_speaker(speaker_id, net_id) + + +class AddPeerToSpeaker(command.Command): + _description = _("Add a peer to a BGP speaker") + + def get_parser(self, prog_name): + parser = super(AddPeerToSpeaker, self).get_parser(prog_name) + parser.add_argument( + 'bgp_speaker', + metavar='', + help=_("BGP speaker (name or ID)")) + parser.add_argument( + 'bgp_peer', + metavar='', + help=_("BGP Peer to add (name or ID)")) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + speaker_id = client.find_bgp_speaker(parsed_args.bgp_speaker)['id'] + peer_id = client.find_bgp_peer(parsed_args.bgp_peer)['id'] + client.add_bgp_peer_to_speaker(speaker_id, peer_id) + + +class CreateBgpSpeaker(command.ShowOne): + _description = _("Create a BGP speaker") + + def get_parser(self, prog_name): + parser = super(CreateBgpSpeaker, self).get_parser(prog_name) + parser.add_argument( + 'name', + metavar='', + help=_("Name of the BGP speaker to create")) + parser.add_argument( + '--local-as', + metavar='', + required=True, + help=_("Local AS number. (Integer in [%(min_val)s, %(max_val)s] " + "is allowed.)") % {'min_val': constants.MIN_AS_NUM, + 'max_val': constants.MAX_AS_NUM}) + parser.add_argument( + '--ip-version', + type=int, choices=[4, 6], + default=4, + help=_("IP version for the BGP speaker (default is 4)")) + add_common_arguments(parser) + nc_osc_utils.add_project_owner_option_to_parser(parser) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + attrs = _get_attrs(self.app.client_manager, parsed_args) + obj = client.create_bgp_speaker(**attrs) + display_columns, columns = nc_osc_utils._get_columns(obj) + data = utils.get_dict_properties(obj, columns) + return display_columns, data + + +class DeleteBgpSpeaker(command.Command): + _description = _("Delete a BGP speaker") + + def get_parser(self, prog_name): + parser = super(DeleteBgpSpeaker, self).get_parser(prog_name) + parser.add_argument( + 'bgp_speaker', + metavar="", + help=_("BGP speaker to delete (name or ID)") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + id = client.find_bgp_speaker(parsed_args.bgp_speaker)['id'] + client.delete_bgp_speaker(id) + + +class ListBgpSpeaker(command.Lister): + _description = _("List BGP speakers") + + def get_parser(self, prog_name): + parser = super(ListBgpSpeaker, self).get_parser(prog_name) + parser.add_argument( + '--agent', + metavar='', + help=_("List BGP speakers hosted by an agent (ID only)")) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + if parsed_args.agent is not None: + data = client.get_bgp_speakers_hosted_by_dragent(parsed_args.agent) + else: + data = client.bgp_speakers(retrieve_all=True) + + headers = ('ID', 'Name', 'Local AS', 'IP Version') + columns = ('id', 'name', 'local_as', 'ip_version') + return (headers, (utils.get_dict_properties(s, columns) + for s in data)) + + +class ListRoutesAdvertisedBySpeaker(command.Lister): + _description = _("List routes advertised") + + def get_parser(self, prog_name): + parser = super(ListRoutesAdvertisedBySpeaker, + self).get_parser(prog_name) + parser.add_argument( + 'bgp_speaker', + metavar='', + help=_("BGP speaker (name or ID)")) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + speaker_id = client.find_bgp_speaker(parsed_args.bgp_speaker)['id'] + data = client.get_advertised_routes_of_speaker(speaker_id) + headers = ('Destination', 'Nexthop') + columns = ('destination', 'next_hop') + return (headers, (utils.get_dict_properties(s, columns) + for s in data['advertised_routes'])) + + +class RemoveNetworkFromSpeaker(command.Command): + _description = _("Remove a network from a BGP speaker") + + def get_parser(self, prog_name): + parser = super(RemoveNetworkFromSpeaker, self).get_parser(prog_name) + parser.add_argument( + 'bgp_speaker', + metavar='', + help=_("BGP speaker (name or ID)")) + parser.add_argument( + 'network', + metavar='', + help=_("Network to remove (name or ID)")) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + speaker_id = client.find_bgp_speaker(parsed_args.bgp_speaker)['id'] + net_id = client.find_network(parsed_args.network)['id'] + client.remove_gateway_network_from_speaker(speaker_id, net_id) + + +class RemovePeerFromSpeaker(command.Command): + _description = _("Remove a peer from a BGP speaker") + + def get_parser(self, prog_name): + parser = super(RemovePeerFromSpeaker, self).get_parser(prog_name) + parser.add_argument( + 'bgp_speaker', + metavar='', + help=_("BGP speaker (name or ID)")) + parser.add_argument( + 'bgp_peer', + metavar='', + help=_("BGP Peer to remove (name or ID)")) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + speaker_id = client.find_bgp_speaker(parsed_args.bgp_speaker)['id'] + peer_id = client.find_bgp_peer(parsed_args.bgp_peer)['id'] + client.remove_bgp_peer_from_speaker(speaker_id, peer_id) + + +class SetBgpSpeaker(command.Command): + _description = _("Set BGP speaker properties") + + resource = constants.BGP_SPEAKER + + def get_parser(self, prog_name): + parser = super(SetBgpSpeaker, self).get_parser(prog_name) + parser.add_argument( + 'bgp_speaker', + metavar="", + help=_("BGP speaker to update (name or ID)") + ) + parser.add_argument( + '--name', + help=_("New name for the BGP speaker")) + add_common_arguments(parser) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + id = client.find_bgp_speaker(parsed_args.bgp_speaker)['id'] + attrs = _get_attrs(self.app.client_manager, parsed_args) + client.update_bgp_speaker(id, **attrs) + + +class ShowBgpSpeaker(command.ShowOne): + _description = _("Show a BGP speaker") + + def get_parser(self, prog_name): + parser = super(ShowBgpSpeaker, self).get_parser(prog_name) + parser.add_argument( + 'bgp_speaker', + metavar="", + help=_("BGP speaker to display (name or ID)") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + id = client.find_bgp_speaker(parsed_args.bgp_speaker, + ignore_missing=False).id + obj = client.get_bgp_speaker(id) + display_columns, columns = nc_osc_utils._get_columns(obj) + data = utils.get_dict_properties(obj, columns) + return display_columns, data diff --git a/neutronclient/osc/v2/dynamic_routing/constants.py b/neutronclient/osc/v2/dynamic_routing/constants.py new file mode 100644 index 000000000..8885b42ed --- /dev/null +++ b/neutronclient/osc/v2/dynamic_routing/constants.py @@ -0,0 +1,18 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +BGP_SPEAKERS = 'bgp_speakers' +BGP_SPEAKER = 'bgp_speaker' +BGP_PEERS = 'bgp_peers' +BGP_PEER = 'bgp_peer' +MIN_AS_NUM = 1 +MAX_AS_NUM = 4294967295 diff --git a/neutronclient/osc/v2/fwaas/constants.py b/neutronclient/osc/v2/fwaas/constants.py new file mode 100644 index 000000000..a5a74af73 --- /dev/null +++ b/neutronclient/osc/v2/fwaas/constants.py @@ -0,0 +1,28 @@ +# Copyright 2016-2017 FUJITSU LIMITED +# All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +# TODO(slaweq): remove it once "logging" OSC plugin will be moved to the +# python-openstackclient repo + +FWG = 'firewall_group' +FWGS = 'firewall_groups' +FWP = 'firewall_policy' +FWPS = 'firewall_policies' +FWR = 'firewall_rule' +FWRS = 'firewall_rules' +CMD_FWG = 'fwaas_' + FWG +CMD_FWP = 'fwaas_' + FWP +CMD_FWR = 'fwaas_' + FWR diff --git a/neutronclient/osc/v2/trunk/__init__.py b/neutronclient/osc/v2/logging/__init__.py similarity index 100% rename from neutronclient/osc/v2/trunk/__init__.py rename to neutronclient/osc/v2/logging/__init__.py diff --git a/neutronclient/osc/v2/logging/network_log.py b/neutronclient/osc/v2/logging/network_log.py new file mode 100644 index 000000000..b3b8204c3 --- /dev/null +++ b/neutronclient/osc/v2/logging/network_log.py @@ -0,0 +1,304 @@ +# Copyright 2017-2018 FUJTISU LIMITED. +# All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +import copy + +from osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils +from osc_lib.utils import columns as column_util +from oslo_log import log as logging + +from neutronclient._i18n import _ +from neutronclient.common import utils as nc_utils +from neutronclient.osc import utils as osc_utils +from neutronclient.osc.v2.fwaas import constants as fwaas_const + +LOG = logging.getLogger(__name__) + +_attr_map = ( + ('id', 'ID', column_util.LIST_BOTH), + ('description', 'Description', column_util.LIST_LONG_ONLY), + ('enabled', 'Enabled', column_util.LIST_BOTH), + ('name', 'Name', column_util.LIST_BOTH), + ('target_id', 'Target', column_util.LIST_LONG_ONLY), + ('project_id', 'Project', column_util.LIST_LONG_ONLY), + ('resource_id', 'Resource', column_util.LIST_LONG_ONLY), + ('resource_type', 'Type', column_util.LIST_BOTH), + ('event', 'Event', column_util.LIST_LONG_ONLY), + ('summary', 'Summary', column_util.LIST_SHORT_ONLY), +) + +_attr_map_for_loggable = ( + ('type', 'Supported types', column_util.LIST_BOTH), +) + +NET_LOG = 'network_log' + + +def _get_common_parser(parser): + parser.add_argument( + '--description', + metavar='', + help=_('Description of the network log')) + enable_group = parser.add_mutually_exclusive_group() + enable_group.add_argument( + '--enable', + action='store_true', + help=_('Enable this log')) + enable_group.add_argument( + '--disable', + action='store_true', + help=_('Disable this log (default is enabled)')) + return parser + + +def _get_common_attrs(client_manager, parsed_args, is_create=True): + attrs = {} + client = client_manager.neutronclient + + if is_create: + if 'project' in parsed_args and parsed_args.project is not None: + attrs['project_id'] = osc_utils.find_project( + client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + resource_type = parsed_args.resource_type + attrs['resource_type'] = resource_type + if parsed_args.resource: + cmd_resource = None + if resource_type == fwaas_const.FWG: + cmd_resource = fwaas_const.CMD_FWG + attrs['resource_id'] = client.find_resource( + resource_type, + parsed_args.resource, + cmd_resource=cmd_resource)['id'] + + if parsed_args.target: + # NOTE(yushiro) Currently, we're supporting only port + attrs['target_id'] = client.find_resource( + 'port', parsed_args.target)['id'] + if parsed_args.event: + attrs['event'] = parsed_args.event + if parsed_args.enable: + attrs['enabled'] = True + if parsed_args.disable: + attrs['enabled'] = False + if parsed_args.name: + attrs['name'] = parsed_args.name + if parsed_args.description: + attrs['description'] = parsed_args.description + return attrs + + +class CreateNetworkLog(command.ShowOne): + _description = _("Create a new network log") + + def get_parser(self, prog_name): + parser = super(CreateNetworkLog, self).get_parser(prog_name) + _get_common_parser(parser) + osc_utils.add_project_owner_option_to_parser(parser) + parser.add_argument( + 'name', + metavar='', + help=_('Name for the network log')) + parser.add_argument( + '--event', + metavar='{ALL,ACCEPT,DROP}', + choices=['ALL', 'ACCEPT', 'DROP'], + type=nc_utils.convert_to_uppercase, + help=_('An event to store with log')) + # NOTE(yushiro) '--resource-type' is managed by following command: + # "openstack network loggable resources list". Therefore, this option + # shouldn't have "choices" like ['security_group', 'firewall_group'] + parser.add_argument( + '--resource-type', + metavar='', + required=True, + type=nc_utils.convert_to_lowercase, + help=_('Network log type(s). ' + 'You can see supported type(s) with following command:\n' + '$ openstack network loggable resources list')) + parser.add_argument( + '--resource', + metavar='', + help=_('Name or ID of resource (security group or firewall group) ' + 'that used for logging. You can control for logging target ' + 'combination with --target option.')) + parser.add_argument( + '--target', + metavar='', + help=_('Port (name or ID) for logging. You can control ' + 'for logging target combination with --resource option.')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + attrs = _get_common_attrs(self.app.client_manager, parsed_args) + obj = client.create_network_log({'log': attrs})['log'] + columns, display_columns = column_util.get_columns(obj, _attr_map) + data = utils.get_dict_properties(obj, columns) + return (display_columns, data) + + +class DeleteNetworkLog(command.Command): + _description = _("Delete network log(s)") + + def get_parser(self, prog_name): + parser = super(DeleteNetworkLog, self).get_parser(prog_name) + parser.add_argument( + 'network_log', + metavar='', + nargs='+', + help=_('Network log(s) to delete (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + result = 0 + for log_res in parsed_args.network_log: + try: + log_id = client.find_resource( + 'log', log_res, cmd_resource=NET_LOG)['id'] + client.delete_network_log(log_id) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete network log with " + "name or ID '%(network_log)s': %(e)s"), + {'network_log': log_res, 'e': e}) + + if result > 0: + total = len(parsed_args.network_log) + msg = (_("%(result)s of %(total)s network log(s) " + "failed to delete") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) + + +class ListLoggableResource(command.Lister): + _description = _("List supported loggable resources") + + def get_parser(self, prog_name): + parser = super(ListLoggableResource, self).get_parser(prog_name) + parser.add_argument( + '--long', + action='store_true', + help=_("List additional fields in output") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + obj = client.list_network_loggable_resources()['loggable_resources'] + headers, columns = column_util.get_column_definitions( + _attr_map_for_loggable, long_listing=parsed_args.long) + return (headers, (utils.get_dict_properties(s, columns) for s in obj)) + + +class ListNetworkLog(command.Lister): + _description = _("List network logs") + + def get_parser(self, prog_name): + parser = super(ListNetworkLog, self).get_parser(prog_name) + parser.add_argument( + '--long', + action='store_true', + help=_("List additional fields in output") + ) + # TODO(yushiro): We'll support filtering in the future. + return parser + + def _extend_list(self, data, parsed_args): + ext_data = copy.deepcopy(data) + for d in ext_data: + e_prefix = 'Event: ' + if d['event']: + event = e_prefix + d['event'].upper() + port = '(port) ' + d['target_id'] if d['target_id'] else '' + resource_type = d['resource_type'] + if d['resource_id']: + res = '(%s) %s' % (resource_type, d['resource_id']) + else: + res = '' + t_prefix = 'Logged: ' + if port and res: + t = '%s on %s' % (res, port) + else: + # Either of res and port is empty, so concatenation works fine + t = res + port + target = t_prefix + t if t else t_prefix + '(None specified)' + d['summary'] = ',\n'.join([event, target]) + return ext_data + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + obj = client.list_network_logs()['logs'] + obj_extend = self._extend_list(obj, parsed_args) + headers, columns = column_util.get_column_definitions( + _attr_map, long_listing=parsed_args.long) + return (headers, ( + utils.get_dict_properties(s, columns) for s in obj_extend)) + + +class SetNetworkLog(command.Command): + _description = _("Set network log properties") + + def get_parser(self, prog_name): + parser = super(SetNetworkLog, self).get_parser(prog_name) + _get_common_parser(parser) + parser.add_argument( + 'network_log', + metavar='', + help=_('Network log to set (name or ID)')) + parser.add_argument( + '--name', + metavar='', + help=_('Name of the network log')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + log_id = client.find_resource( + 'log', parsed_args.network_log, cmd_resource=NET_LOG)['id'] + attrs = _get_common_attrs(self.app.client_manager, parsed_args, + is_create=False) + try: + client.update_network_log(log_id, {'log': attrs}) + except Exception as e: + msg = (_("Failed to set network log '%(logging)s': %(e)s") + % {'logging': parsed_args.network_log, 'e': e}) + raise exceptions.CommandError(msg) + + +class ShowNetworkLog(command.ShowOne): + _description = _("Display network log details") + + def get_parser(self, prog_name): + parser = super(ShowNetworkLog, self).get_parser(prog_name) + parser.add_argument( + 'network_log', + metavar='', + help=_('Network log to show (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + log_id = client.find_resource( + 'log', parsed_args.network_log, cmd_resource=NET_LOG)['id'] + obj = client.show_network_log(log_id)['log'] + columns, display_columns = column_util.get_columns(obj, _attr_map) + data = utils.get_dict_properties(obj, columns) + return (display_columns, data) diff --git a/neutronclient/tests/functional/__init__.py b/neutronclient/osc/v2/sfc/__init__.py similarity index 100% rename from neutronclient/tests/functional/__init__.py rename to neutronclient/osc/v2/sfc/__init__.py diff --git a/neutronclient/osc/v2/sfc/sfc_flow_classifier.py b/neutronclient/osc/v2/sfc/sfc_flow_classifier.py new file mode 100755 index 000000000..775730fc2 --- /dev/null +++ b/neutronclient/osc/v2/sfc/sfc_flow_classifier.py @@ -0,0 +1,346 @@ +# Copyright (c) 2017 Huawei Technologies India Pvt.Limited. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import argparse +import logging + +from osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils +from osc_lib.utils import columns as column_util + +from neutronclient._i18n import _ +from neutronclient.common import exceptions as nc_exc + +LOG = logging.getLogger(__name__) + +resource = 'flow_classifier' + +_attr_map = ( + ('id', 'ID', column_util.LIST_BOTH), + ('name', 'Name', column_util.LIST_BOTH), + ('summary', 'Summary', column_util.LIST_SHORT_ONLY), + ('protocol', 'Protocol', column_util.LIST_LONG_ONLY), + ('ethertype', 'Ethertype', column_util.LIST_LONG_ONLY), + ('source_ip_prefix', 'Source IP', + column_util.LIST_LONG_ONLY), + ('destination_ip_prefix', 'Destination IP', + column_util.LIST_LONG_ONLY), + ('logical_source_port', 'Logical Source Port', + column_util.LIST_LONG_ONLY), + ('logical_destination_port', 'Logical Destination Port', + column_util.LIST_LONG_ONLY), + ('source_port_range_min', 'Source Port Range Min', + column_util.LIST_LONG_ONLY), + ('source_port_range_max', 'Source Port Range Max', + column_util.LIST_LONG_ONLY), + ('destination_port_range_min', 'Destination Port Range Min', + column_util.LIST_LONG_ONLY), + ('destination_port_range_max', 'Destination Port Range Max', + column_util.LIST_LONG_ONLY), + ('l7_parameters', 'L7 Parameters', column_util.LIST_LONG_ONLY), + ('description', 'Description', column_util.LIST_LONG_ONLY), + ('project_id', 'Project', column_util.LIST_LONG_ONLY), +) + +_attr_map_dict = { + 'id': 'ID', + 'name': 'Name', + 'description': 'Description', + 'summary': 'Summary', + 'protocol': 'Protocol', + 'ethertype': 'Ethertype', + 'source_ip_prefix': 'Source IP', + 'destination_ip_prefix': 'Destination IP', + 'logical_source_port': 'Logical Source Port', + 'logical_destination_port': 'Logical Destination Port', + 'source_port_range_min': 'Source Port Range Min', + 'source_port_range_max': 'Source Port Range Max', + 'destination_port_range_min': 'Destination Port Range Min', + 'destination_port_range_max': 'Destination Port Range Max', + 'l7_parameters': 'L7 Parameters', + 'tenant_id': 'Project', + 'project_id': 'Project', +} + + +class CreateSfcFlowClassifier(command.ShowOne): + _description = _("Create a flow classifier") + + def get_parser(self, prog_name): + parser = super(CreateSfcFlowClassifier, self).get_parser(prog_name) + parser.add_argument( + 'name', + metavar='', + help=_('Name of the flow classifier')) + parser.add_argument( + '--description', + metavar='', + help=_('Description for the flow classifier')) + parser.add_argument( + '--protocol', + metavar='', + help=_('IP protocol name. Protocol name should be as per ' + 'IANA standard.')) + parser.add_argument( + '--ethertype', + metavar='{IPv4,IPv6}', + default='IPv4', choices=['IPv4', 'IPv6'], + help=_('L2 ethertype, default is IPv4')) + parser.add_argument( + '--source-port', + metavar=':', + help=_('Source protocol port (allowed range [1,65535]. Must be ' + 'specified as a:b, where a=min-port and b=max-port) ' + 'in the allowed range.')) + parser.add_argument( + '--destination-port', + metavar=':', + help=_('Destination protocol port (allowed range [1,65535]. Must ' + 'be specified as a:b, where a=min-port and b=max-port) ' + 'in the allowed range.')) + parser.add_argument( + '--source-ip-prefix', + metavar='', + help=_('Source IP address in CIDR notation')) + parser.add_argument( + '--destination-ip-prefix', + metavar='', + help=_('Destination IP address in CIDR notation')) + parser.add_argument( + '--logical-source-port', + metavar='', + help=_('Neutron source port (name or ID)')) + parser.add_argument( + '--logical-destination-port', + metavar='', + help=_('Neutron destination port (name or ID)')) + parser.add_argument( + '--l7-parameters', + help=_('Dictionary of L7 parameters. Currently, no value is ' + 'supported for this option.')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + attrs = _get_common_attrs(self.app.client_manager, parsed_args) + obj = client.create_sfc_flow_classifier(**attrs) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id', 'summary']) + data = utils.get_dict_properties(obj, columns) + return display_columns, data + + +class DeleteSfcFlowClassifier(command.Command): + _description = _("Delete a given flow classifier") + + def get_parser(self, prog_name): + parser = super(DeleteSfcFlowClassifier, self).get_parser(prog_name) + parser.add_argument( + 'flow_classifier', + metavar='', + nargs='+', + help=_("Flow classifier(s) to delete (name or ID)") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + result = 0 + for fcl in parsed_args.flow_classifier: + try: + fc_id = client.find_sfc_flow_classifier( + fcl, ignore_missing=False)['id'] + client.delete_sfc_flow_classifier(fc_id) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete flow classifier with name " + "or ID '%(fc)s': %(e)s"), {'fc': fcl, 'e': e}) + if result > 0: + total = len(parsed_args.flow_classifier) + msg = (_("%(result)s of %(total)s flow classifier(s) " + "failed to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) + + +class ListSfcFlowClassifier(command.Lister): + _description = _("List flow classifiers") + + def get_parser(self, prog_name): + parser = super(ListSfcFlowClassifier, self).get_parser(prog_name) + parser.add_argument( + '--long', + action='store_true', + help=_("List additional fields in output") + ) + return parser + + def extend_list(self, data, parsed_args): + ext_data = [] + for d in data: + val = [] + protocol = d['protocol'].upper() if d['protocol'] else 'any' + val.append('protocol: ' + protocol) + val.append(self._get_protocol_port_details(d, 'source')) + val.append(self._get_protocol_port_details(d, 'destination')) + if 'logical_source_port' in d: + val.append('neutron_source_port: ' + + str(d['logical_source_port'])) + + if 'logical_destination_port' in d: + val.append('neutron_destination_port: ' + + str(d['logical_destination_port'])) + + if 'l7_parameters' in d: + l7_param = 'l7_parameters: {%s}' % ','.join(d['l7_parameters']) + val.append(l7_param) + d['summary'] = ',\n'.join(val) + ext_data.append(d) + return ext_data + + def _get_protocol_port_details(self, data, val): + type_ip_prefix = val + '_ip_prefix' + ip_prefix = data.get(type_ip_prefix) + if not ip_prefix: + ip_prefix = 'any' + min_port = data.get(val + '_port_range_min') + if min_port is None: + min_port = 'any' + max_port = data.get(val + '_port_range_max') + if max_port is None: + max_port = 'any' + return '%s[port]: %s[%s:%s]' % ( + val, ip_prefix, min_port, max_port) + + def take_action(self, parsed_args): + client = self.app.client_manager.network + obj = client.sfc_flow_classifiers() + obj_extend = self.extend_list(obj, parsed_args) + headers, columns = column_util.get_column_definitions( + _attr_map, long_listing=parsed_args.long) + return (headers, (utils.get_dict_properties( + s, columns) for s in obj_extend)) + + +class SetSfcFlowClassifier(command.Command): + _description = _("Set flow classifier properties") + + def get_parser(self, prog_name): + parser = super(SetSfcFlowClassifier, self).get_parser(prog_name) + parser.add_argument( + '--name', + metavar='', + help=_('Name of the flow classifier')) + parser.add_argument( + '--description', + metavar='', + help=_('Description for the flow classifier')) + parser.add_argument( + 'flow_classifier', + metavar='', + help=_("Flow classifier to modify (name or ID)") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + fc_id = client.find_sfc_flow_classifier(parsed_args.flow_classifier, + ignore_missing=False)['id'] + attrs = _get_common_attrs(self.app.client_manager, parsed_args, + is_create=False) + try: + client.update_sfc_flow_classifier(fc_id, **attrs) + except Exception as e: + msg = (_("Failed to update flow classifier '%(fc)s': %(e)s") + % {'fc': parsed_args.flow_classifier, 'e': e}) + raise exceptions.CommandError(msg) + + +class ShowSfcFlowClassifier(command.ShowOne): + _description = _("Display flow classifier details") + + def get_parser(self, prog_name): + parser = super(ShowSfcFlowClassifier, self).get_parser(prog_name) + parser.add_argument( + 'flow_classifier', + metavar='', + help=_("Flow classifier to display (name or ID)") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + fc_id = client.find_sfc_flow_classifier(parsed_args.flow_classifier, + ignore_missing=False)['id'] + obj = client.get_sfc_flow_classifier(fc_id) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id', 'summary']) + data = utils.get_dict_properties(obj, columns) + return display_columns, data + + +def _get_common_attrs(client_manager, parsed_args, is_create=True): + attrs = {} + if parsed_args.name is not None: + attrs['name'] = parsed_args.name + if parsed_args.description is not None: + attrs['description'] = parsed_args.description + if is_create: + _get_attrs(client_manager, attrs, parsed_args) + return attrs + + +def _get_attrs(client_manager, attrs, parsed_args): + if parsed_args.protocol is not None: + attrs['protocol'] = parsed_args.protocol + if parsed_args.ethertype: + attrs['ethertype'] = parsed_args.ethertype + if parsed_args.source_ip_prefix is not None: + attrs['source_ip_prefix'] = parsed_args.source_ip_prefix + if parsed_args.destination_ip_prefix is not None: + attrs['destination_ip_prefix'] = parsed_args.destination_ip_prefix + if parsed_args.logical_source_port is not None: + attrs['logical_source_port'] = client_manager.network.find_port( + parsed_args.logical_source_port, ignore_missing=False + )['id'] + if parsed_args.logical_destination_port is not None: + attrs['logical_destination_port'] = client_manager.network.find_port( + parsed_args.logical_destination_port, ignore_missing=False + )['id'] + if parsed_args.source_port is not None: + _fill_protocol_port_info(attrs, 'source', + parsed_args.source_port) + if parsed_args.destination_port is not None: + _fill_protocol_port_info(attrs, 'destination', + parsed_args.destination_port) + if parsed_args.l7_parameters is not None: + attrs['l7_parameters'] = parsed_args.l7_parameters + + +def _fill_protocol_port_info(attrs, port_type, port_val): + min_port, sep, max_port = port_val.partition(":") + if not min_port: + msg = ("Invalid port value '%s', expected format is " + "min-port:max-port or min-port.") + raise argparse.ArgumentTypeError(msg % port_val) + if not max_port: + max_port = min_port + try: + attrs[port_type + '_port_range_min'] = int(min_port) + attrs[port_type + '_port_range_max'] = int(max_port) + except ValueError: + message = (_("Protocol port value %s must be an integer " + "or integer:integer.") % port_val) + raise nc_exc.CommandError(message=message) diff --git a/neutronclient/osc/v2/sfc/sfc_port_chain.py b/neutronclient/osc/v2/sfc/sfc_port_chain.py new file mode 100755 index 000000000..d96007b85 --- /dev/null +++ b/neutronclient/osc/v2/sfc/sfc_port_chain.py @@ -0,0 +1,381 @@ +# Copyright (c) 2017 Huawei Technologies India Pvt.Limited. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import logging + +from osc_lib.cli import parseractions +from osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils +from osc_lib.utils import columns as column_util + +from neutronclient._i18n import _ + +LOG = logging.getLogger(__name__) + +resource = 'port_chain' + +_attr_map = ( + ('id', 'ID', column_util.LIST_BOTH), + ('name', 'Name', column_util.LIST_BOTH), + ('port_pair_groups', 'Port Pair Groups', column_util.LIST_BOTH), + ('flow_classifiers', 'Flow Classifiers', + column_util.LIST_BOTH), + ('chain_parameters', 'Chain Parameters', + column_util.LIST_BOTH), + ('description', 'Description', column_util.LIST_LONG_ONLY), + ('project_id', 'Project', column_util.LIST_LONG_ONLY), +) + +_attr_map_dict = { + 'id': 'ID', + 'name': 'Name', + 'port_pair_groups': 'Port Pair Groups', + 'flow_classifiers': 'Flow Classifiers', + 'chain_parameters': 'Chain Parameters', + 'description': 'Description', + 'tenant_id': 'Project', + 'project_id': 'Project', +} + + +class CreateSfcPortChain(command.ShowOne): + _description = _("Create a port chain") + + def get_parser(self, prog_name): + parser = super(CreateSfcPortChain, self).get_parser(prog_name) + parser.add_argument( + 'name', + metavar='', + help=_('Name of the port chain')) + parser.add_argument( + '--description', + metavar='', + help=_('Description for the port chain')) + parser.add_argument( + '--flow-classifier', + default=[], + metavar='', + dest='flow_classifiers', + action='append', + help=_('Add flow classifier (name or ID). ' + 'This option can be repeated.')) + parser.add_argument( + '--chain-parameters', + metavar='correlation=,symmetric=', + action=parseractions.MultiKeyValueAction, + optional_keys=['correlation', 'symmetric'], + help=_('Dictionary of chain parameters. Supports ' + 'correlation=(mpls|nsh) (default is mpls) ' + 'and symmetric=(true|false).')) + parser.add_argument( + '--port-pair-group', + metavar='', + dest='port_pair_groups', + required=True, + action='append', + help=_('Add port pair group (name or ID). ' + 'This option can be repeated.')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + attrs = _get_common_attrs(self.app.client_manager, parsed_args) + obj = client.create_sfc_port_chain(**attrs) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id']) + data = utils.get_dict_properties(obj, columns) + return display_columns, data + + +class DeleteSfcPortChain(command.Command): + _description = _("Delete a given port chain") + + def get_parser(self, prog_name): + parser = super(DeleteSfcPortChain, self).get_parser(prog_name) + parser.add_argument( + 'port_chain', + metavar="", + nargs='+', + help=_("Port chain(s) to delete (name or ID)") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + result = 0 + for pc in parsed_args.port_chain: + try: + pc_id = client.find_sfc_port_chain( + pc, ignore_missing=False)['id'] + client.delete_sfc_port_chain(pc_id) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete port chain with name " + "or ID '%(pc)s': %(e)s"), {'pc': pc, 'e': e}) + if result > 0: + total = len(parsed_args.port_chain) + msg = (_("%(result)s of %(total)s port chain(s) " + "failed to delete.") % {'result': result, + 'total': total}) + raise exceptions.CommandError(msg) + + +class ListSfcPortChain(command.Lister): + _description = _("List port chains") + + def get_parser(self, prog_name): + parser = super(ListSfcPortChain, self).get_parser(prog_name) + parser.add_argument( + '--long', + action='store_true', + default=False, + help=_("List additional fields in output") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + data = client.sfc_port_chains() + headers, columns = column_util.get_column_definitions( + _attr_map, long_listing=parsed_args.long) + return (headers, + (utils.get_dict_properties(s, columns) + for s in data)) + + +class SetSfcPortChain(command.Command): + _description = _("Set port chain properties") + + def get_parser(self, prog_name): + parser = super(SetSfcPortChain, self).get_parser(prog_name) + parser.add_argument( + '--name', + metavar='', + help=_('Name of the port chain')) + parser.add_argument( + '--description', + metavar='', + help=_('Description for the port chain')) + parser.add_argument( + '--flow-classifier', + metavar='', + dest='flow_classifiers', + action='append', + help=_('Add flow classifier (name or ID). ' + 'This option can be repeated.')) + parser.add_argument( + '--no-flow-classifier', + action='store_true', + help=_('Remove associated flow classifiers from the port chain')) + parser.add_argument( + '--port-pair-group', + metavar='', + dest='port_pair_groups', + action='append', + help=_('Add port pair group (name or ID). ' + 'Current port pair groups order is kept, the added port ' + 'pair group will be placed at the end of the port chain. ' + 'This option can be repeated.')) + parser.add_argument( + '--no-port-pair-group', + action='store_true', + help=_('Remove associated port pair groups from the port chain. ' + 'At least one --port-pair-group must be specified ' + 'together.')) + parser.add_argument( + 'port_chain', + metavar='', + help=_("Port chain to modify (name or ID)")) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + pc_id = client.find_sfc_port_chain(parsed_args.port_chain, + ignore_missing=False)['id'] + attrs = _get_common_attrs(self.app.client_manager, parsed_args, + is_create=False) + if parsed_args.no_flow_classifier: + attrs['flow_classifiers'] = [] + if parsed_args.flow_classifiers: + if parsed_args.no_flow_classifier: + fc_list = [] + else: + fc_list = client.find_sfc_port_chain( + parsed_args.port_chain, + ignore_missing=False + )['flow_classifiers'] + for fc in parsed_args.flow_classifiers: + fc_id = client.find_sfc_flow_classifier( + fc, + ignore_missing=False)['id'] + if fc_id not in fc_list: + fc_list.append(fc_id) + attrs['flow_classifiers'] = fc_list + if (parsed_args.no_port_pair_group and not + parsed_args.port_pair_groups): + message = _('At least one --port-pair-group must be specified.') + raise exceptions.CommandError(message) + if parsed_args.no_port_pair_group and parsed_args.port_pair_groups: + ppg_list = [] + for ppg in parsed_args.port_pair_groups: + ppg_id = client.find_sfc_port_pair_group( + ppg, ignore_missing=False)['id'] + if ppg_id not in ppg_list: + ppg_list.append(ppg_id) + attrs['port_pair_groups'] = ppg_list + if (parsed_args.port_pair_groups and + not parsed_args.no_port_pair_group): + ppg_list = client.find_sfc_port_chain( + parsed_args.port_chain, + ignore_missing=False + )['port_pair_groups'] + for ppg in parsed_args.port_pair_groups: + ppg_id = client.find_sfc_port_pair_group( + ppg, ignore_missing=False)['id'] + if ppg_id not in ppg_list: + ppg_list.append(ppg_id) + attrs['port_pair_groups'] = ppg_list + try: + client.update_sfc_port_chain(pc_id, **attrs) + except Exception as e: + msg = (_("Failed to update port chain '%(pc)s': %(e)s") + % {'pc': parsed_args.port_chain, 'e': e}) + raise exceptions.CommandError(msg) + + +class ShowSfcPortChain(command.ShowOne): + _description = _("Display port chain details") + + def get_parser(self, prog_name): + parser = super(ShowSfcPortChain, self).get_parser(prog_name) + parser.add_argument( + 'port_chain', + metavar="", + help=_("Port chain to display (name or ID)") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + pc_id = client.find_sfc_port_chain(parsed_args.port_chain, + ignore_missing=False)['id'] + obj = client.get_sfc_port_chain(pc_id) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id']) + data = utils.get_dict_properties(obj, columns) + return display_columns, data + + +class UnsetSfcPortChain(command.Command): + _description = _("Unset port chain properties") + + def get_parser(self, prog_name): + parser = super(UnsetSfcPortChain, self).get_parser(prog_name) + parser.add_argument( + 'port_chain', + metavar='', + help=_("Port chain to unset (name or ID)")) + port_chain = parser.add_mutually_exclusive_group() + port_chain.add_argument( + '--flow-classifier', + action='append', + metavar='', + dest='flow_classifiers', + help=_('Remove flow classifier(s) from the port chain ' + '(name or ID). This option can be repeated.')) + port_chain.add_argument( + '--all-flow-classifier', + action='store_true', + help=_('Remove all flow classifiers from the port chain')) + parser.add_argument( + '--port-pair-group', + metavar='', + dest='port_pair_groups', + action='append', + help=_('Remove port pair group(s) from the port chain ' + '(name or ID). This option can be repeated.')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + pc_id = client.find_sfc_port_chain(parsed_args.port_chain, + ignore_missing=False)['id'] + attrs = {} + if parsed_args.flow_classifiers: + fc_list = client.find_sfc_port_chain( + parsed_args.port_chain, ignore_missing=False + )['flow_classifiers'] + for fc in parsed_args.flow_classifiers: + fc_id = client.find_sfc_flow_classifier( + fc, + ignore_missing=False)['id'] + if fc_id in fc_list: + fc_list.remove(fc_id) + attrs['flow_classifiers'] = fc_list + if parsed_args.all_flow_classifier: + attrs['flow_classifiers'] = [] + if parsed_args.port_pair_groups: + ppg_list = client.find_sfc_port_chain( + parsed_args.port_chain, + ignore_missing=False)['port_pair_groups'] + for ppg in parsed_args.port_pair_groups: + ppg_id = client.find_sfc_port_pair_group( + ppg, + ignore_missing=False)['id'] + if ppg_id in ppg_list: + ppg_list.remove(ppg_id) + if ppg_list == []: + message = _('At least one port pair group must be' + ' specified.') + raise exceptions.CommandError(message) + attrs['port_pair_groups'] = ppg_list + try: + client.update_sfc_port_chain(pc_id, **attrs) + except Exception as e: + msg = (_("Failed to unset port chain '%(pc)s': %(e)s") + % {'pc': parsed_args.port_chain, 'e': e}) + raise exceptions.CommandError(msg) + + +def _get_common_attrs(client_manager, parsed_args, is_create=True): + attrs = {} + client = client_manager.network + if parsed_args.name is not None: + attrs['name'] = parsed_args.name + if parsed_args.description is not None: + attrs['description'] = parsed_args.description + if parsed_args.port_pair_groups: + attrs['port_pair_groups'] = [client.find_sfc_port_pair_group( + ppg, ignore_missing=False)['id'] + for ppg in parsed_args.port_pair_groups] + if parsed_args.flow_classifiers: + attrs['flow_classifiers'] = [client.find_sfc_flow_classifier( + fc, ignore_missing=False)['id'] + for fc in parsed_args.flow_classifiers] + if is_create is True: + _get_attrs(attrs, parsed_args) + return attrs + + +def _get_attrs(attrs, parsed_args): + if parsed_args.chain_parameters is not None: + chain_params = {} + for chain_param in parsed_args.chain_parameters: + if 'correlation' in chain_param: + chain_params['correlation'] = chain_param['correlation'] + if 'symmetric' in chain_param: + chain_params['symmetric'] = chain_param['symmetric'] + attrs['chain_parameters'] = chain_params diff --git a/neutronclient/osc/v2/sfc/sfc_port_pair.py b/neutronclient/osc/v2/sfc/sfc_port_pair.py new file mode 100755 index 000000000..11e8e7631 --- /dev/null +++ b/neutronclient/osc/v2/sfc/sfc_port_pair.py @@ -0,0 +1,251 @@ +# Copyright (c) 2017 Huawei Technologies India Pvt.Limited. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import logging + +from osc_lib.cli import parseractions +from osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils +from osc_lib.utils import columns as column_util + +from neutronclient._i18n import _ + +LOG = logging.getLogger(__name__) + +resource = 'port_pair' + +_attr_map = ( + ('id', 'ID', column_util.LIST_BOTH), + ('name', 'Name', column_util.LIST_BOTH), + ('ingress', 'Ingress Logical Port', column_util.LIST_BOTH), + ('egress', 'Egress Logical Port', column_util.LIST_BOTH), + ('service_function_parameters', 'Service Function Parameters', + column_util.LIST_LONG_ONLY), + ('description', 'Description', column_util.LIST_LONG_ONLY), + ('project_id', 'Project', column_util.LIST_LONG_ONLY), +) + +_attr_map_dict = { + 'id': 'ID', + 'name': 'Name', + 'description': 'Description', + 'ingress': 'Ingress Logical Port', + 'egress': 'Egress Logical Port', + 'service_function_parameters': 'Service Function Parameters', + 'tenant_id': 'Project', + 'project_id': 'Project', +} + + +class CreateSfcPortPair(command.ShowOne): + _description = _("Create a port pair") + + def get_parser(self, prog_name): + parser = super(CreateSfcPortPair, self).get_parser(prog_name) + parser.add_argument( + 'name', + metavar='', + help=_('Name of the port pair')) + parser.add_argument( + '--description', + metavar='', + help=_('Description for the port pair')) + parser.add_argument( + '--service-function-parameters', + metavar='correlation=,weight=', + action=parseractions.MultiKeyValueAction, + optional_keys=['correlation', 'weight'], + help=_('Dictionary of service function parameters. ' + 'Currently, correlation=(None|mpls|nsh) and weight ' + 'are supported. Weight is an integer that influences ' + 'the selection of a port pair within a port pair group ' + 'for a flow. The higher the weight, the more flows will ' + 'hash to the port pair. The default weight is 1.')) + parser.add_argument( + '--ingress', + metavar='', + required=True, + help=_('Ingress neutron port (name or ID)')) + parser.add_argument( + '--egress', + metavar='', + required=True, + help=_('Egress neutron port (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + attrs = _get_common_attrs(self.app.client_manager, parsed_args) + obj = client.create_sfc_port_pair(**attrs) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id']) + data = utils.get_dict_properties(obj, columns) + return display_columns, data + + +class DeleteSfcPortPair(command.Command): + _description = _("Delete a given port pair") + + def get_parser(self, prog_name): + parser = super(DeleteSfcPortPair, self).get_parser(prog_name) + parser.add_argument( + 'port_pair', + metavar="", + nargs='+', + help=_("Port pair(s) to delete (name or ID)") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + result = 0 + for pp in parsed_args.port_pair: + try: + port_pair_id = client.find_sfc_port_pair( + pp, ignore_missing=False)['id'] + client.delete_sfc_port_pair(port_pair_id) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete port pair with name " + "or ID '%(port_pair)s': %(e)s"), + {'port_pair': pp, 'e': e}) + if result > 0: + total = len(parsed_args.port_pair) + msg = (_("%(result)s of %(total)s port pair(s) " + "failed to delete.") % {'result': result, + 'total': total}) + raise exceptions.CommandError(msg) + + +class ListSfcPortPair(command.Lister): + _description = _("List port pairs") + + def get_parser(self, prog_name): + parser = super(ListSfcPortPair, self).get_parser(prog_name) + parser.add_argument( + '--long', + action='store_true', + help=_("List additional fields in output") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + data = client.sfc_port_pairs() + headers, columns = column_util.get_column_definitions( + _attr_map, long_listing=parsed_args.long) + return (headers, + (utils.get_dict_properties( + s, columns, + ) for s in data)) + + +class SetSfcPortPair(command.Command): + _description = _("Set port pair properties") + + def get_parser(self, prog_name): + parser = super(SetSfcPortPair, self).get_parser(prog_name) + parser.add_argument( + '--name', + metavar='', + help=_('Name of the port pair')) + parser.add_argument( + '--description', + metavar='', + help=_('Description for the port pair')) + parser.add_argument( + 'port_pair', + metavar='', + help=_("Port pair to modify (name or ID)") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + port_pair_id = client.find_sfc_port_pair( + parsed_args.port_pair, ignore_missing=False + )['id'] + attrs = _get_common_attrs(self.app.client_manager, parsed_args, + is_create=False) + try: + client.update_sfc_port_pair(port_pair_id, **attrs) + except Exception as e: + msg = (_("Failed to update port pair '%(port_pair)s': %(e)s") + % {'port_pair': parsed_args.port_pair, 'e': e}) + raise exceptions.CommandError(msg) + + +class ShowSfcPortPair(command.ShowOne): + _description = _("Display port pair details") + + def get_parser(self, prog_name): + parser = super(ShowSfcPortPair, self).get_parser(prog_name) + parser.add_argument( + 'port_pair', + metavar='', + help=_("Port pair to display (name or ID)") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + port_pair_id = client.find_sfc_port_pair( + parsed_args.port_pair, ignore_missing=False + )['id'] + obj = client.get_sfc_port_pair(port_pair_id) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id']) + data = utils.get_dict_properties(obj, columns) + return display_columns, data + + +def _get_common_attrs(client_manager, parsed_args, is_create=True): + attrs = {} + if parsed_args.name is not None: + attrs['name'] = parsed_args.name + if parsed_args.description is not None: + attrs['description'] = parsed_args.description + if is_create: + _get_attrs(client_manager, attrs, parsed_args) + return attrs + + +def _get_attrs(client_manager, attrs, parsed_args): + client = client_manager.network + if parsed_args.ingress is not None: + attrs['ingress'] = client.find_port( + parsed_args.ingress, ignore_missing=False + )['id'] + if parsed_args.egress is not None: + attrs['egress'] = client.find_port( + parsed_args.egress, ignore_missing=False + )['id'] + if parsed_args.service_function_parameters is not None: + attrs['service_function_parameters'] = _get_service_function_params( + parsed_args.service_function_parameters) + + +def _get_service_function_params(sf_params): + attrs = {} + for sf_param in sf_params: + if 'correlation' in sf_param: + if sf_param['correlation'] == 'None': + attrs['correlation'] = None + else: + attrs['correlation'] = sf_param['correlation'] + if 'weight' in sf_param: + attrs['weight'] = sf_param['weight'] + return attrs diff --git a/neutronclient/osc/v2/sfc/sfc_port_pair_group.py b/neutronclient/osc/v2/sfc/sfc_port_pair_group.py new file mode 100755 index 000000000..ec8859b42 --- /dev/null +++ b/neutronclient/osc/v2/sfc/sfc_port_pair_group.py @@ -0,0 +1,326 @@ +# Copyright (c) 2017 Huawei Technologies India Pvt.Limited. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import logging + +from osc_lib.cli import parseractions +from osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils +from osc_lib.utils import columns as column_util + +from neutronclient._i18n import _ + +LOG = logging.getLogger(__name__) + +resource = 'port_pair_group' + +_attr_map = ( + ('id', 'ID', column_util.LIST_BOTH), + ('name', 'Name', column_util.LIST_BOTH), + ('port_pairs', 'Port Pair', column_util.LIST_BOTH), + ('port_pair_group_parameters', 'Port Pair Group Parameters', + column_util.LIST_BOTH), + ('description', 'Description', column_util.LIST_LONG_ONLY), + ('project_id', 'Project', column_util.LIST_LONG_ONLY), + ('is_tap_enabled', 'Tap Enabled', column_util.LIST_BOTH) +) + +_attr_map_dict = { + 'id': 'ID', + 'name': 'Name', + 'description': 'Description', + 'port_pairs': 'Port Pair', + 'port_pair_group_parameters': 'Port Pair Group Parameters', + 'is_tap_enabled': 'Tap Enabled', + 'tenant_id': 'Project', + 'project_id': 'Project', +} + + +class CreateSfcPortPairGroup(command.ShowOne): + _description = _("Create a port pair group") + + def get_parser(self, prog_name): + parser = super(CreateSfcPortPairGroup, self).get_parser(prog_name) + parser.add_argument( + 'name', + metavar='', + help=_('Name of the port pair group')) + parser.add_argument( + '--description', + metavar='', + help=_('Description for the port pair group')) + parser.add_argument( + '--port-pair', + metavar='', + dest='port_pairs', + default=[], + action='append', + help=_('Port pair (name or ID). ' + 'This option can be repeated.')) + tap_enable = parser.add_mutually_exclusive_group() + tap_enable.add_argument( + '--enable-tap', + action='store_true', + help=_('Port pairs of this port pair group are deployed as ' + 'passive tap service function') + ) + tap_enable.add_argument( + '--disable-tap', + action='store_true', + help=_('Port pairs of this port pair group are deployed as l3 ' + 'service function (default)') + ) + parser.add_argument( + '--port-pair-group-parameters', + metavar='lb-fields=', + action=parseractions.KeyValueAction, + help=_('Dictionary of port pair group parameters. ' + 'Currently only one parameter lb-fields is supported. ' + ' is a & separated list of load-balancing ' + 'fields.')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + attrs = _get_common_attrs(self.app.client_manager, parsed_args) + obj = client.create_sfc_port_pair_group(**attrs) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id']) + data = utils.get_dict_properties(obj, columns) + return display_columns, data + + +class DeleteSfcPortPairGroup(command.Command): + _description = _("Delete a given port pair group") + + def get_parser(self, prog_name): + parser = super(DeleteSfcPortPairGroup, self).get_parser(prog_name) + parser.add_argument( + 'port_pair_group', + metavar='', + nargs='+', + help=_("Port pair group(s) to delete (name or ID)") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + result = 0 + for ppg in parsed_args.port_pair_group: + try: + ppg_id = client.find_sfc_port_pair_group( + ppg, ignore_missing=False)['id'] + client.delete_sfc_port_pair_group(ppg_id) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete port pair group with name " + "or ID '%(ppg)s': %(e)s"), {'ppg': ppg, 'e': e}) + if result > 0: + total = len(parsed_args.port_pair_group) + msg = (_("%(result)s of %(total)s port pair group(s) " + "failed to delete.") % {'result': result, + 'total': total}) + raise exceptions.CommandError(msg) + + +class ListSfcPortPairGroup(command.Lister): + _description = _("List port pair group") + + def get_parser(self, prog_name): + parser = super(ListSfcPortPairGroup, self).get_parser(prog_name) + parser.add_argument( + '--long', + action='store_true', + default=False, + help=_("List additional fields in output") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + data = client.sfc_port_pair_groups() + headers, columns = column_util.get_column_definitions( + _attr_map, long_listing=parsed_args.long) + return (headers, + (utils.get_dict_properties( + s, columns, + ) for s in data)) + + +class SetSfcPortPairGroup(command.Command): + _description = _("Set port pair group properties") + + def get_parser(self, prog_name): + parser = super(SetSfcPortPairGroup, self).get_parser(prog_name) + parser.add_argument( + 'port_pair_group', + metavar='', + help=_("Port pair group to modify (name or ID)")) + parser.add_argument( + '--name', + metavar='', + help=_('Name of the port pair group')) + parser.add_argument( + '--description', + metavar='', + help=_('Description for the port pair group')) + parser.add_argument( + '--port-pair', + metavar='', + dest='port_pairs', + default=[], + action='append', + help=_('Port pair (name or ID). ' + 'This option can be repeated.')) + parser.add_argument( + '--no-port-pair', + action='store_true', + help=_('Remove all port pair from port pair group')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + ppg_id = client.find_sfc_port_pair_group( + parsed_args.port_pair_group)['id'] + attrs = _get_common_attrs(self.app.client_manager, parsed_args, + is_create=False) + if parsed_args.no_port_pair: + attrs['port_pairs'] = [] + if parsed_args.port_pairs: + added = [client.find_sfc_port_pair(pp, + ignore_missing=False)['id'] + for pp in parsed_args.port_pairs] + if parsed_args.no_port_pair: + existing = [] + else: + existing = client.find_sfc_port_pair_group( + parsed_args.port_pair_group, + ignore_missing=False)['port_pairs'] + attrs['port_pairs'] = sorted(list(set(existing) | set(added))) + try: + client.update_sfc_port_pair_group(ppg_id, **attrs) + except Exception as e: + msg = (_("Failed to update port pair group '%(ppg)s': %(e)s") + % {'ppg': parsed_args.port_pair_group, 'e': e}) + raise exceptions.CommandError(msg) + + +class ShowSfcPortPairGroup(command.ShowOne): + _description = _("Display port pair group details") + + def get_parser(self, prog_name): + parser = super(ShowSfcPortPairGroup, self).get_parser(prog_name) + parser.add_argument( + 'port_pair_group', + metavar='', + help=_("Port pair group to display (name or ID)") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + ppg_id = client.find_sfc_port_pair_group( + parsed_args.port_pair_group, ignore_missing=False)['id'] + obj = client.get_sfc_port_pair_group(ppg_id) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id']) + data = utils.get_dict_properties(obj, columns) + return display_columns, data + + +class UnsetSfcPortPairGroup(command.Command): + _description = _("Unset port pairs from port pair group") + + def get_parser(self, prog_name): + parser = super(UnsetSfcPortPairGroup, self).get_parser(prog_name) + parser.add_argument( + 'port_pair_group', + metavar='', + help=_("Port pair group to unset (name or ID)")) + port_pair_group = parser.add_mutually_exclusive_group() + port_pair_group.add_argument( + '--port-pair', + action='append', + metavar='', + dest='port_pairs', + help=_('Remove port pair(s) from the port pair group ' + '(name or ID). This option can be repeated.')) + port_pair_group.add_argument( + '--all-port-pair', + action='store_true', + help=_('Remove all port pairs from the port pair group')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + ppg_id = client.find_sfc_port_pair_group( + parsed_args.port_pair_group, ignore_missing=False)['id'] + attrs = {} + if parsed_args.port_pairs: + existing = client.find_sfc_port_pair_group( + parsed_args.port_pair_group, + ignore_missing=False)['port_pairs'] + removed = [client.find_sfc_port_pair(pp, + ignore_missing=False)['id'] + for pp in parsed_args.port_pairs] + attrs['port_pairs'] = list(set(existing) - set(removed)) + if parsed_args.all_port_pair: + attrs['port_pairs'] = [] + try: + client.update_sfc_port_pair_group(ppg_id, **attrs) + except Exception as e: + msg = (_("Failed to unset port pair group '%(ppg)s': %(e)s") + % {'ppg': parsed_args.port_pair_group, 'e': e}) + raise exceptions.CommandError(msg) + + +def _get_ppg_param(attrs, ppg): + attrs['port_pair_group_parameters'] = {} + for key, value in ppg.items(): + if key == 'lb-fields': + attrs['port_pair_group_parameters']['lb_fields'] = ([ + field for field in value.split('&') if field]) + else: + attrs['port_pair_group_parameters'][key] = value + return attrs['port_pair_group_parameters'] + + +def _get_common_attrs(client_manager, parsed_args, is_create=True): + client = client_manager.network + attrs = {} + if parsed_args.name is not None: + attrs['name'] = parsed_args.name + if parsed_args.description is not None: + attrs['description'] = parsed_args.description + if parsed_args.port_pairs: + attrs['port_pairs'] = [client.find_sfc_port_pair( + pp, ignore_missing=False)['id'] + for pp in parsed_args.port_pairs] + + if is_create: + _get_attrs(attrs, parsed_args) + return attrs + + +def _get_attrs(attrs, parsed_args): + if parsed_args.port_pair_group_parameters is not None: + attrs['port_pair_group_parameters'] = ( + _get_ppg_param(attrs, parsed_args.port_pair_group_parameters)) + if parsed_args.enable_tap: + attrs['tap_enabled'] = True + if parsed_args.disable_tap: + attrs['tap_enabled'] = False diff --git a/neutronclient/osc/v2/sfc/sfc_service_graph.py b/neutronclient/osc/v2/sfc/sfc_service_graph.py new file mode 100644 index 000000000..2edf2ccd0 --- /dev/null +++ b/neutronclient/osc/v2/sfc/sfc_service_graph.py @@ -0,0 +1,272 @@ +# Copyright 2017 Intel Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# 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 osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils +from osc_lib.utils import columns as column_util + +from neutronclient._i18n import _ + +LOG = logging.getLogger(__name__) + +resource = 'service_graph' + +_attr_map = ( + ('id', 'ID', column_util.LIST_BOTH), + ('name', 'Name', column_util.LIST_BOTH), + ('port_chains', 'Branching Points', column_util.LIST_BOTH), + ('description', 'Description', column_util.LIST_LONG_ONLY), + ('project_id', 'Project', column_util.LIST_LONG_ONLY), +) + +_attr_map_dict = { + 'id': 'ID', + 'name': 'Name', + 'description': 'Description', + 'port_chains': 'Branching Points', + 'tenant_id': 'Project', + 'project_id': 'Project', +} + + +class CreateSfcServiceGraph(command.ShowOne): + """Create a service graph.""" + def get_parser(self, prog_name): + parser = super(CreateSfcServiceGraph, self).get_parser(prog_name) + parser.add_argument( + 'name', + metavar='', + help=_('Name of the service graph.')) + parser.add_argument( + '--description', + help=_('Description for the service graph.')) + parser.add_argument( + '--branching-point', + metavar='SRC_CHAIN:DST_CHAIN_1,DST_CHAIN_2,DST_CHAIN_N', + dest='branching_points', + action='append', + default=[], required=True, + help=_('Service graph branching point: the key is the source ' + 'Port Chain while the value is a list of destination ' + 'Port Chains. This option can be repeated.')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + attrs = _get_common_attrs(self.app.client_manager, parsed_args) + try: + obj = client.create_sfc_service_graph(**attrs) + display_columns, columns = \ + utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id']) + data = utils.get_dict_properties(obj, columns) + return display_columns, data + except Exception as e: + msg = (_("Failed to create service graph using '%(pcs)s': %(e)s") + % {'pcs': parsed_args.branching_points, 'e': e}) + raise exceptions.CommandError(msg) + + +class SetSfcServiceGraph(command.Command): + _description = _("Set service graph properties") + + def get_parser(self, prog_name): + parser = super(SetSfcServiceGraph, self).get_parser(prog_name) + parser.add_argument( + '--name', + metavar='', + help=_('Name of the service graph')) + parser.add_argument( + '--description', + metavar='', + help=_('Description for the service graph')) + parser.add_argument( + 'service_graph', + metavar='', + help=_("Service graph to modify (name or ID)") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + service_graph_id = client.find_sfc_service_graph( + parsed_args.service_graph, ignore_missing=False)['id'] + attrs = _get_common_attrs(self.app.client_manager, parsed_args, + is_create=False) + try: + client.update_sfc_service_graph(service_graph_id, **attrs) + except Exception as e: + msg = (_("Failed to update service graph " + "'%(service_graph)s': %(e)s") + % {'service_graph': parsed_args.service_graph, 'e': e}) + raise exceptions.CommandError(msg) + + +class DeleteSfcServiceGraph(command.Command): + """Delete a given service graph.""" + + def get_parser(self, prog_name): + parser = super(DeleteSfcServiceGraph, self).get_parser(prog_name) + parser.add_argument( + 'service_graph', + metavar="", + nargs='+', + help=_("ID or name of the service graph(s) to delete.") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + result = 0 + for sg in parsed_args.service_graph: + try: + sg_id = client.find_sfc_service_graph( + sg, ignore_missing=False)['id'] + client.delete_sfc_service_graph(sg_id) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete service graph with name " + "or ID '%(sg)s': %(e)s"), + {'sg': sg, 'e': e}) + if result > 0: + total = len(parsed_args.service_graph) + msg = (_("%(result)s of %(total)s service graph(s) " + "failed to delete.") % {'result': result, + 'total': total}) + raise exceptions.CommandError(msg) + + +class ListSfcServiceGraph(command.Lister): + _description = _("List service graphs") + + def get_parser(self, prog_name): + parser = super(ListSfcServiceGraph, self).get_parser(prog_name) + parser.add_argument( + '--long', + action='store_true', + default=False, + help=_("List additional fields in output") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + data = client.sfc_service_graphs() + headers, columns = column_util.get_column_definitions( + _attr_map, long_listing=parsed_args.long) + return (headers, + (utils.get_dict_properties(s, columns) + for s in data)) + + +class ShowSfcServiceGraph(command.ShowOne): + """Show information of a given service graph.""" + + def get_parser(self, prog_name): + parser = super(ShowSfcServiceGraph, self).get_parser(prog_name) + parser.add_argument( + 'service_graph', + metavar="", + help=_("ID or name of the service graph to display.") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + sg_id = client.find_sfc_service_graph(parsed_args.service_graph, + ignore_missing=False)['id'] + obj = client.get_sfc_service_graph(sg_id) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id']) + data = utils.get_dict_properties(obj, columns) + return display_columns, data + + +def _get_common_attrs(client_manager, parsed_args, is_create=True): + attrs = {} + if parsed_args.name is not None: + attrs['name'] = str(parsed_args.name) + if parsed_args.description is not None: + attrs['description'] = str(parsed_args.description) + if is_create: + _get_attrs_for_create(client_manager, attrs, parsed_args) + return attrs + + +def _validate_destination_chains(comma_split, attrs, client, sc_): + for e in comma_split: + if e != "": + dc_ = client.find_sfc_port_chain(e, ignore_missing=False)['id'] + attrs['port_chains'][sc_].append(dc_) + if _check_cycle(attrs['port_chains'], sc_, dc_): + raise exceptions.CommandError( + "Error: Service graph contains a cycle") + else: + raise exceptions.CommandError( + "Error: you must specify at least one " + "destination chain for each source chain") + return attrs + + +def _check_cycle(graph, new_src, new_dest): + for src in graph: + if src == new_dest: + if _visit(graph, src, new_dest, new_src): + return True + return False + + +def _visit(graph, src, new_dest, new_src): + if src in graph: + found_cycle = False + for dest in graph[src]: + if new_src == dest or found_cycle: + return True + else: + found_cycle = _visit(graph, dest, new_dest, new_src) + return False + + +def _get_attrs_for_create(client_manager, attrs, parsed_args): + client = client_manager.network + if parsed_args.branching_points: + attrs['port_chains'] = {} + src_chain = None + for c in parsed_args.branching_points: + if ':' not in c: + raise exceptions.CommandError( + "Error: You must specify at least one " + "destination chain for each source chain.") + colon_split = c.split(':') + src_chain = colon_split.pop(0) + sc_ = client.find_sfc_port_chain(src_chain, + ignore_missing=False)['id'] + for i in colon_split: + comma_split = i.split(',') + unique = set(comma_split) + if len(unique) != len(comma_split): + raise exceptions.CommandError( + "Error: Duplicate " + "destination chains from " + "source chain {}".format(src_chain)) + if sc_ in attrs['port_chains']: + raise exceptions.CommandError( + "Error: Source chain {} is in " + "use already ".format(src_chain)) + attrs['port_chains'][sc_] = [] + _validate_destination_chains( + comma_split, attrs, client, sc_) diff --git a/neutronclient/tests/functional/adv-svcs/__init__.py b/neutronclient/osc/v2/subnet_onboard/__init__.py similarity index 100% rename from neutronclient/tests/functional/adv-svcs/__init__.py rename to neutronclient/osc/v2/subnet_onboard/__init__.py diff --git a/neutronclient/osc/v2/subnet_onboard/subnet_onboard.py b/neutronclient/osc/v2/subnet_onboard/subnet_onboard.py new file mode 100644 index 000000000..10c6ab4c2 --- /dev/null +++ b/neutronclient/osc/v2/subnet_onboard/subnet_onboard.py @@ -0,0 +1,59 @@ +# Copyright (c) 2019 SUSE Linux Products GmbH +# 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. +# + +"""Subnet onboard action implementation""" +import logging + +from osc_lib.command import command +from osc_lib import exceptions + +from neutronclient._i18n import _ + +LOG = logging.getLogger(__name__) + + +class NetworkOnboardSubnets(command.Command): + """Onboard network subnets into a subnet pool""" + + def get_parser(self, prog_name): + parser = super(NetworkOnboardSubnets, self).get_parser(prog_name) + parser.add_argument( + 'network', + metavar="", + help=_("Onboard all subnets associated with this network") + ) + parser.add_argument( + 'subnetpool', + metavar="", + help=_("Target subnet pool for onboarding subnets") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.neutronclient + subnetpool_id = _get_id(client, parsed_args.subnetpool, 'subnetpool') + network_id = _get_id(client, parsed_args.network, 'network') + body = {'network_id': network_id} + try: + client.onboard_network_subnets(subnetpool_id, body) + except Exception as e: + msg = (_("Failed to onboard subnets for network '%(n)s': %(e)s") + % {'n': parsed_args.network, 'e': e}) + raise exceptions.CommandError(msg) + + +def _get_id(client, id_or_name, resource): + return client.find_resource(resource, str(id_or_name))['id'] diff --git a/neutronclient/osc/v2/trunk/network_trunk.py b/neutronclient/osc/v2/trunk/network_trunk.py deleted file mode 100644 index e3eed827a..000000000 --- a/neutronclient/osc/v2/trunk/network_trunk.py +++ /dev/null @@ -1,380 +0,0 @@ -# Copyright 2016 ZTE Corporation. -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -"""Network trunk and subports action implementations""" -import logging - -from osc_lib.cli import parseractions -from osc_lib.command import command -from osc_lib import exceptions -from osc_lib import utils - -# TODO(abhiraut): Switch to neutronclients identity utils -from openstackclient.identity import common as identity_common - -from neutronclient._i18n import _ - -LOG = logging.getLogger(__name__) - -TRUNK = 'trunk' -TRUNKS = 'trunks' -SUB_PORTS = 'sub_ports' - - -class CreateNetworkTrunk(command.ShowOne): - """Create a network trunk for a given project""" - - def get_parser(self, prog_name): - parser = super(CreateNetworkTrunk, self).get_parser(prog_name) - parser.add_argument( - 'name', - metavar='', - help=_("Name of the trunk to create") - ) - parser.add_argument( - '--parent-port', - metavar='', - required=True, - help=_("Parent port belonging to this trunk (name or ID)") - ) - parser.add_argument( - '--subport', - metavar='', - action=parseractions.MultiKeyValueAction, dest='add_subports', - help=_("Subport to add. Subport is of form " - "\'port=,segmentation-type=,segmentation-ID=\' " - "(--subport) option can be repeated") - ) - admin_group = parser.add_mutually_exclusive_group() - admin_group.add_argument( - '--enable', - action='store_true', - default=True, - help=_("Enable trunk (default)") - ) - admin_group.add_argument( - '--disable', - action='store_true', - help=_("Disable trunk") - ) - parser.add_argument( - '--project', - metavar='', - help=_("Owner's project (name or ID)") - ) - identity_common.add_project_domain_option_to_parser(parser) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - attrs = _get_attrs_for_trunk(self.app.client_manager, - parsed_args) - body = {TRUNK: attrs} - obj = client.create_trunk(body) - columns = _get_columns(obj[TRUNK]) - data = utils.get_dict_properties(obj[TRUNK], columns, - formatters=_formatters) - return columns, data - - -class DeleteNetworkTrunk(command.Command): - """Delete a given network trunk""" - - def get_parser(self, prog_name): - parser = super(DeleteNetworkTrunk, self).get_parser(prog_name) - parser.add_argument( - 'trunk', - metavar="", - nargs="+", - help=_("Trunk(s) to delete (name or ID)") - ) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - result = 0 - for trunk in parsed_args.trunk: - try: - trunk_id = _get_id(client, trunk, TRUNK) - client.delete_trunk(trunk_id) - except Exception as e: - result += 1 - LOG.error(_("Failed to delete trunk with name " - "or ID '%(trunk)s': %(e)s") - % {'trunk': trunk, 'e': e}) - if result > 0: - total = len(parsed_args.trunk) - msg = (_("%(result)s of %(total)s trunks failed " - "to delete.") % {'result': result, 'total': total}) - raise exceptions.CommandError(msg) - - -class ListNetworkTrunk(command.Lister): - """List all network trunks""" - - def get_parser(self, prog_name): - parser = super(ListNetworkTrunk, self).get_parser(prog_name) - parser.add_argument( - '--long', - action='store_true', - default=False, - help=_("List additional fields in output") - ) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - data = client.list_trunks() - headers = ( - 'ID', - 'Name', - 'Parent Port' - ) - columns = ( - 'id', - 'name', - 'port_id' - ) - if parsed_args.long: - headers += ( - 'Status', - 'State', - ) - columns += ( - 'status', - 'admin_state_up', - ) - return (headers, - (utils.get_dict_properties( - s, columns, - formatters=_formatters, - ) for s in data[TRUNKS])) - - -class SetNetworkTrunk(command.Command): - """Set network trunk properties""" - - def get_parser(self, prog_name): - parser = super(SetNetworkTrunk, self).get_parser(prog_name) - parser.add_argument( - 'trunk', - metavar="", - help=_("Trunk to modify (name or ID)") - ) - parser.add_argument( - '--name', - metavar="", - help=_("Set trunk name") - ) - parser.add_argument( - '--subport', - metavar='', - action=parseractions.MultiKeyValueAction, dest='set_subports', - help=_("Subport to add. Subport is of form " - "\'port=,segmentation-type=,segmentation-ID=\'" - "(--subport) option can be repeated") - ) - admin_group = parser.add_mutually_exclusive_group() - admin_group.add_argument( - '--enable', - action='store_true', - help=_("Enable trunk") - ) - admin_group.add_argument( - '--disable', - action='store_true', - help=_("Disable trunk") - ) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - trunk_id = _get_id(client, parsed_args.trunk, TRUNK) - attrs = _get_attrs_for_trunk(self.app.client_manager, parsed_args) - body = {TRUNK: attrs} - try: - client.update_trunk(trunk_id, body) - except Exception as e: - msg = (_("Failed to set trunk '%(t)s': %(e)s") - % {'t': parsed_args.trunk, 'e': e}) - raise exceptions.CommandError(msg) - if parsed_args.set_subports: - subport_attrs = _get_attrs_for_subports(self.app.client_manager, - parsed_args) - try: - client.trunk_add_subports(trunk_id, subport_attrs) - except Exception as e: - msg = (_("Failed to add subports to trunk '%(t)s': %(e)s") - % {'t': parsed_args.trunk, 'e': e}) - raise exceptions.CommandError(msg) - - -class ShowNetworkTrunk(command.ShowOne): - """Show information of a given network trunk""" - def get_parser(self, prog_name): - parser = super(ShowNetworkTrunk, self).get_parser(prog_name) - parser.add_argument( - 'trunk', - metavar="", - help=_("Trunk to display (name or ID)") - ) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - trunk_id = _get_id(client, parsed_args.trunk, TRUNK) - obj = client.show_trunk(trunk_id) - columns = _get_columns(obj[TRUNK]) - data = utils.get_dict_properties(obj[TRUNK], columns, - formatters=_formatters) - return columns, data - - -class ListNetworkSubport(command.Lister): - """List all subports for a given network trunk""" - - def get_parser(self, prog_name): - parser = super(ListNetworkSubport, self).get_parser(prog_name) - parser.add_argument( - '--trunk', - required=True, - metavar="", - help=_("List subports belonging to this trunk (name or ID)") - ) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - trunk_id = _get_id(client, parsed_args.trunk, TRUNK) - data = client.trunk_get_subports(trunk_id) - headers = ('Port', 'Segmentation Type', 'Segmentation ID') - columns = ('port_id', 'segmentation_type', 'segmentation_id') - return (headers, - (utils.get_dict_properties( - s, columns, - ) for s in data[SUB_PORTS])) - - -class UnsetNetworkTrunk(command.Command): - """Unset subports from a given network trunk""" - - def get_parser(self, prog_name): - parser = super(UnsetNetworkTrunk, self).get_parser(prog_name) - parser.add_argument( - 'trunk', - metavar="", - help=_("Unset subports from this trunk (name or ID)") - ) - parser.add_argument( - '--subport', - metavar="", - required=True, - action='append', dest='unset_subports', - help=_("Subport to delete (name or ID of the port) " - "(--subport) option can be repeated") - ) - return parser - - def take_action(self, parsed_args): - client = self.app.client_manager.neutronclient - attrs = _get_attrs_for_subports(self.app.client_manager, parsed_args) - trunk_id = _get_id(client, parsed_args.trunk, TRUNK) - client.trunk_remove_subports(trunk_id, attrs) - - -def _format_admin_state(item): - return 'UP' if item else 'DOWN' - - -_formatters = { - 'admin_state_up': _format_admin_state, - 'sub_ports': utils.format_list_of_dicts, -} - - -def _get_columns(item): - return tuple(sorted(list(item.keys()))) - - -def _get_attrs_for_trunk(client_manager, parsed_args): - attrs = {} - if parsed_args.name is not None: - attrs['name'] = str(parsed_args.name) - if parsed_args.enable: - attrs['admin_state_up'] = True - if parsed_args.disable: - attrs['admin_state_up'] = False - if 'parent_port' in parsed_args and parsed_args.parent_port is not None: - port_id = _get_id(client_manager.neutronclient, - parsed_args.parent_port, 'port') - attrs['port_id'] = port_id - if 'add_subports' in parsed_args and parsed_args.add_subports is not None: - attrs[SUB_PORTS] = _format_subports(client_manager, - parsed_args.add_subports) - - # "trunk set" command doesn't support setting project. - if 'project' in parsed_args and parsed_args.project is not None: - identity_client = client_manager.identity - project_id = identity_common.find_project( - identity_client, - parsed_args.project, - parsed_args.project_domain, - ).id - attrs['tenant_id'] = project_id - - return attrs - - -def _format_subports(client_manager, subports): - attrs = [] - for subport in subports: - subport_attrs = {} - if subport.get('port'): - port_id = _get_id(client_manager.neutronclient, - subport['port'], 'port') - subport_attrs['port_id'] = port_id - if subport.get('segmentation-id'): - try: - subport_attrs['segmentation_id'] = int( - subport['segmentation-id']) - except ValueError: - msg = (_("Segmentation-id '%s' is not an integer") % - subport['segmentation-id']) - raise exceptions.CommandError(msg) - if subport.get('segmentation-type'): - subport_attrs['segmentation_type'] = subport['segmentation-type'] - attrs.append(subport_attrs) - return attrs - - -def _get_attrs_for_subports(client_manager, parsed_args): - attrs = {} - if 'set_subports' in parsed_args and parsed_args.set_subports is not None: - attrs[SUB_PORTS] = _format_subports(client_manager, - parsed_args.set_subports) - if ('unset_subports' in parsed_args and - parsed_args.unset_subports is not None): - subports_list = [] - for subport in parsed_args.unset_subports: - port_id = _get_id(client_manager.neutronclient, - subport, 'port') - subports_list.append({'port_id': port_id}) - attrs[SUB_PORTS] = subports_list - return attrs - - -def _get_id(client, id_or_name, resource): - return client.find_resource(resource, str(id_or_name))['id'] diff --git a/neutronclient/osc/v2/utils.py b/neutronclient/osc/v2/utils.py new file mode 100644 index 000000000..b03e29c3d --- /dev/null +++ b/neutronclient/osc/v2/utils.py @@ -0,0 +1,24 @@ +# Copyright 2016 NEC Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +"""This module is intended to contain methods specific +to Networking v2 API and its extensions. +""" + +from cliff import columns as cliff_columns + + +class AdminStateColumn(cliff_columns.FormattableColumn): + def human_readable(self): + return 'UP' if self._value else 'DOWN' diff --git a/neutronclient/osc/v2/vpnaas/endpoint_group.py b/neutronclient/osc/v2/vpnaas/endpoint_group.py new file mode 100644 index 000000000..e37c2cef4 --- /dev/null +++ b/neutronclient/osc/v2/vpnaas/endpoint_group.py @@ -0,0 +1,222 @@ +# Copyright 2017 FUJITSU LIMITED +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +from osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils +from osc_lib.utils import columns as column_util +from oslo_log import log as logging + +from neutronclient._i18n import _ +from neutronclient.osc import utils as osc_utils + + +LOG = logging.getLogger(__name__) + +_attr_map = ( + ('id', 'ID', column_util.LIST_BOTH), + ('name', 'Name', column_util.LIST_BOTH), + ('type', 'Type', column_util.LIST_BOTH), + ('endpoints', 'Endpoints', column_util.LIST_BOTH), + ('description', 'Description', column_util.LIST_LONG_ONLY), + ('project_id', 'Project', column_util.LIST_LONG_ONLY), +) + +_attr_map_dict = { + 'id': 'ID', + 'name': 'Name', + 'type': 'Type', + 'endpoints': 'Endpoints', + 'description': 'Description', + 'tenant_id': 'Project', + 'project_id': 'Project', +} + + +def _get_common_parser(parser): + parser.add_argument( + '--description', + metavar='', + help=_('Description for the endpoint group')) + return parser + + +def _get_common_attrs(client_manager, parsed_args, is_create=True): + attrs = {} + if is_create: + if parsed_args.project is not None: + attrs['tenant_id'] = osc_utils.find_project( + client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + if parsed_args.description: + attrs['description'] = parsed_args.description + return attrs + + +class CreateEndpointGroup(command.ShowOne): + _description = _("Create an endpoint group") + + def get_parser(self, prog_name): + parser = super(CreateEndpointGroup, self).get_parser(prog_name) + _get_common_parser(parser) + parser.add_argument( + 'name', + metavar='', + help=_('Name for the endpoint group')) + parser.add_argument( + '--type', + required=True, + help=_('Type of endpoints in group (e.g. subnet, cidr)')) + parser.add_argument( + '--value', + action='append', + dest='endpoints', + required=True, + help=_('Endpoint(s) for the group. Must all be of the same type. ' + '(--value) option can be repeated')) + osc_utils.add_project_owner_option_to_parser(parser) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + attrs = _get_common_attrs(self.app.client_manager, parsed_args) + if parsed_args.name: + attrs['name'] = str(parsed_args.name) + attrs['type'] = parsed_args.type + if parsed_args.type == 'subnet': + _subnet_ids = [client.find_subnet( + endpoint, + ignore_missing=False)['id'] + for endpoint in parsed_args.endpoints] + attrs['endpoints'] = _subnet_ids + else: + attrs['endpoints'] = parsed_args.endpoints + obj = client.create_vpn_endpoint_group(**attrs) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id']) + data = utils.get_dict_properties(obj, columns) + return display_columns, data + + +class DeleteEndpointGroup(command.Command): + _description = _("Delete endpoint group(s)") + + def get_parser(self, prog_name): + parser = super(DeleteEndpointGroup, self).get_parser(prog_name) + parser.add_argument( + 'endpoint_group', + metavar='', + nargs='+', + help=_('Endpoint group(s) to delete (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + result = 0 + for endpoint in parsed_args.endpoint_group: + try: + endpoint_id = client.find_vpn_endpoint_group( + endpoint, ignore_missing=False)['id'] + client.delete_vpn_endpoint_group(endpoint_id) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete endpoint group with " + "name or ID '%(endpoint_group)s': %(e)s"), + {'endpoint_group': endpoint, 'e': e}) + + if result > 0: + total = len(parsed_args.endpoint_group) + msg = (_("%(result)s of %(total)s endpoint group failed " + "to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) + + +class ListEndpointGroup(command.Lister): + _description = _("List endpoint groups that belong to a given project") + + def get_parser(self, prog_name): + parser = super(ListEndpointGroup, self).get_parser(prog_name) + parser.add_argument( + '--long', + action='store_true', + default=False, + help=_("List additional fields in output") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + obj = client.vpn_endpoint_groups() + headers, columns = column_util.get_column_definitions( + _attr_map, long_listing=parsed_args.long) + return (headers, (utils.get_dict_properties(s, columns) for s in obj)) + + +class SetEndpointGroup(command.Command): + _description = _("Set endpoint group properties") + + def get_parser(self, prog_name): + parser = super(SetEndpointGroup, self).get_parser(prog_name) + _get_common_parser(parser) + parser.add_argument( + '--name', + metavar='', + help=_('Set a name for the endpoint group')) + parser.add_argument( + 'endpoint_group', + metavar='', + help=_('Endpoint group to set (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + attrs = _get_common_attrs(self.app.client_manager, + parsed_args, is_create=False) + if parsed_args.name: + attrs['name'] = str(parsed_args.name) + endpoint_id = client.find_vpn_endpoint_group( + parsed_args.endpoint_group, ignore_missing=False)['id'] + try: + client.update_vpn_endpoint_group(endpoint_id, **attrs) + except Exception as e: + msg = (_("Failed to set endpoint group " + "%(endpoint_group)s: %(e)s") + % {'endpoint_group': parsed_args.endpoint_group, 'e': e}) + raise exceptions.CommandError(msg) + + +class ShowEndpointGroup(command.ShowOne): + _description = _("Display endpoint group details") + + def get_parser(self, prog_name): + parser = super(ShowEndpointGroup, self).get_parser(prog_name) + parser.add_argument( + 'endpoint_group', + metavar='', + help=_('Endpoint group to display (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + endpoint_id = client.find_vpn_endpoint_group( + parsed_args.endpoint_group, ignore_missing=False)['id'] + obj = client.get_vpn_endpoint_group(endpoint_id) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id']) + data = utils.get_dict_properties(obj, columns) + return (display_columns, data) diff --git a/neutronclient/osc/v2/vpnaas/ikepolicy.py b/neutronclient/osc/v2/vpnaas/ikepolicy.py new file mode 100644 index 000000000..0a8aecb08 --- /dev/null +++ b/neutronclient/osc/v2/vpnaas/ikepolicy.py @@ -0,0 +1,318 @@ +# Copyright 2017 FUJITSU LIMITED +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +from osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils +from osc_lib.utils import columns as column_util +from oslo_log import log as logging + +from neutronclient._i18n import _ +from neutronclient.common import utils as nc_utils +from neutronclient.osc import utils as osc_utils +from neutronclient.osc.v2.vpnaas import utils as vpn_utils + + +LOG = logging.getLogger(__name__) + +_attr_map = ( + ('id', 'ID', column_util.LIST_BOTH), + ('name', 'Name', column_util.LIST_BOTH), + ('auth_algorithm', 'Authentication Algorithm', column_util.LIST_BOTH), + ('encryption_algorithm', 'Encryption Algorithm', column_util.LIST_BOTH), + ('ike_version', 'IKE Version', column_util.LIST_BOTH), + ('pfs', 'Perfect Forward Secrecy (PFS)', column_util.LIST_BOTH), + ('description', 'Description', column_util.LIST_LONG_ONLY), + ('phase1_negotiation_mode', 'Phase1 Negotiation Mode', + column_util.LIST_LONG_ONLY), + ('project_id', 'Project', column_util.LIST_LONG_ONLY), + ('lifetime', 'Lifetime', column_util.LIST_LONG_ONLY), +) + +_attr_map_dict = { + 'id': 'ID', + 'name': 'Name', + 'auth_algorithm': 'Authentication Algorithm', + 'encryption_algorithm': 'Encryption Algorithm', + 'ike_version': 'IKE Version', + 'pfs': 'Perfect Forward Secrecy (PFS)', + 'phase1_negotiation_mode': 'Phase1 Negotiation Mode', + 'lifetime': 'Lifetime', + 'description': 'Description', + 'tenant_id': 'Project', + 'project_id': 'Project', +} + +_auth_algorithms = [ + 'sha1', + 'sha256', + 'sha384', + 'sha512', + 'aes-xcbc', + 'aes-cmac', +] + +_encryption_algorithms = [ + '3des', + 'aes-128', + 'aes-192', + 'aes-256', + 'aes-128-ccm-8', + 'aes-192-ccm-8', + 'aes-256-ccm-8', + 'aes-128-ccm-12', + 'aes-192-ccm-12', + 'aes-256-ccm-12', + 'aes-128-ccm-16', + 'aes-192-ccm-16', + 'aes-256-ccm-16', + 'aes-128-gcm-8', + 'aes-192-gcm-8', + 'aes-256-gcm-8', + 'aes-128-gcm-12', + 'aes-192-gcm-12', + 'aes-256-gcm-12', + 'aes-128-gcm-16', + 'aes-192-gcm-16', + 'aes-256-gcm-16', + 'aes-128-ctr', + 'aes-192-ctr', + 'aes-256-ctr', +] + +_pfs_groups = [ + 'group2', + 'group5', + 'group14', + 'group15', + 'group16', + 'group17', + 'group18', + 'group19', + 'group20', + 'group21', + 'group22', + 'group23', + 'group24', + 'group25', + 'group26', + 'group27', + 'group28', + 'group29', + 'group30', + 'group31', +] + + +def _convert_to_lowercase(string): + return string.lower() + + +def _get_common_parser(parser): + parser.add_argument( + '--description', + metavar='', + help=_('Description of the IKE policy')) + parser.add_argument( + '--auth-algorithm', + choices=_auth_algorithms, + type=_convert_to_lowercase, + help=_('Authentication algorithm')) + parser.add_argument( + '--encryption-algorithm', + choices=_encryption_algorithms, + type=_convert_to_lowercase, + help=_('Encryption algorithm')) + parser.add_argument( + '--phase1-negotiation-mode', + choices=['main', 'aggressive'], + type=_convert_to_lowercase, + help=_('IKE Phase1 negotiation mode')) + parser.add_argument( + '--ike-version', + choices=['v1', 'v2'], + type=_convert_to_lowercase, + help=_('IKE version for the policy')) + parser.add_argument( + '--pfs', + choices=_pfs_groups, + type=_convert_to_lowercase, + help=_('Perfect Forward Secrecy')) + parser.add_argument( + '--lifetime', + metavar="units=UNITS,value=VALUE", + type=nc_utils.str2dict_type(optional_keys=['units', 'value']), + help=vpn_utils.lifetime_help("IKE")) + return parser + + +def _get_common_attrs(client_manager, parsed_args, is_create=True): + attrs = {} + if is_create: + if 'project' in parsed_args and parsed_args.project is not None: + attrs['project_id'] = osc_utils.find_project( + client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + if parsed_args.description: + attrs['description'] = parsed_args.description + if parsed_args.auth_algorithm: + attrs['auth_algorithm'] = parsed_args.auth_algorithm + if parsed_args.encryption_algorithm: + attrs['encryption_algorithm'] = parsed_args.encryption_algorithm + if parsed_args.phase1_negotiation_mode: + attrs['phase1_negotiation_mode'] = parsed_args.phase1_negotiation_mode + if parsed_args.ike_version: + attrs['ike_version'] = parsed_args.ike_version + if parsed_args.pfs: + attrs['pfs'] = parsed_args.pfs + if parsed_args.lifetime: + vpn_utils.validate_lifetime_dict(parsed_args.lifetime) + attrs['lifetime'] = parsed_args.lifetime + return attrs + + +class CreateIKEPolicy(command.ShowOne): + _description = _("Create an IKE policy") + + def get_parser(self, prog_name): + parser = super(CreateIKEPolicy, self).get_parser(prog_name) + _get_common_parser(parser) + parser.add_argument( + 'name', + metavar='', + help=_('Name of the IKE policy')) + osc_utils.add_project_owner_option_to_parser(parser) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + attrs = _get_common_attrs(self.app.client_manager, parsed_args) + if parsed_args.name: + attrs['name'] = str(parsed_args.name) + obj = client.create_vpn_ike_policy(**attrs) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id', 'units', 'value']) + data = utils.get_dict_properties(obj, columns) + return display_columns, data + + +class DeleteIKEPolicy(command.Command): + _description = _("Delete IKE policy (policies)") + + def get_parser(self, prog_name): + parser = super(DeleteIKEPolicy, self).get_parser(prog_name) + parser.add_argument( + 'ikepolicy', + metavar='', + nargs='+', + help=_('IKE policy to delete (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + result = 0 + for ike in parsed_args.ikepolicy: + try: + ike_id = client.find_vpn_ike_policy(ike, + ignore_missing=False)['id'] + client.delete_vpn_ike_policy(ike_id) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete IKE policy with " + "name or ID '%(ikepolicy)s': %(e)s"), + {'ikepolicy': ike, 'e': e}) + + if result > 0: + total = len(parsed_args.ikepolicy) + msg = (_("%(result)s of %(total)s IKE policy failed " + "to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) + + +class ListIKEPolicy(command.Lister): + _description = _("List IKE policies that belong to a given project") + + def get_parser(self, prog_name): + parser = super(ListIKEPolicy, self).get_parser(prog_name) + parser.add_argument( + '--long', + action='store_true', + help=_("List additional fields in output") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + obj = client.vpn_ike_policies() + headers, columns = column_util.get_column_definitions( + _attr_map, long_listing=parsed_args.long) + return (headers, (utils.get_dict_properties(s, columns) for s in obj)) + + +class SetIKEPolicy(command.Command): + _description = _("Set IKE policy properties") + + def get_parser(self, prog_name): + parser = super(SetIKEPolicy, self).get_parser(prog_name) + _get_common_parser(parser) + parser.add_argument( + '--name', + metavar='', + help=_('Name of the IKE policy')) + parser.add_argument( + 'ikepolicy', + metavar='', + help=_('IKE policy to set (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + attrs = _get_common_attrs(self.app.client_manager, + parsed_args, is_create=False) + if parsed_args.name: + attrs['name'] = parsed_args.name + ike_id = client.find_vpn_ike_policy(parsed_args.ikepolicy, + ignore_missing=False)['id'] + try: + client.update_vpn_ike_policy(ike_id, **attrs) + except Exception as e: + msg = (_("Failed to set IKE policy '%(ike)s': %(e)s") + % {'ike': parsed_args.ikepolicy, 'e': e}) + raise exceptions.CommandError(msg) + + +class ShowIKEPolicy(command.ShowOne): + _description = _("Display IKE policy details") + + def get_parser(self, prog_name): + parser = super(ShowIKEPolicy, self).get_parser(prog_name) + parser.add_argument( + 'ikepolicy', + metavar='', + help=_('IKE policy to display (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + ike_id = client.find_vpn_ike_policy(parsed_args.ikepolicy, + ignore_missing=False)['id'] + obj = client.get_vpn_ike_policy(ike_id) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id', 'units', 'value']) + data = utils.get_dict_properties(obj, columns) + return (display_columns, data) diff --git a/neutronclient/osc/v2/vpnaas/ipsec_site_connection.py b/neutronclient/osc/v2/vpnaas/ipsec_site_connection.py new file mode 100644 index 000000000..1497793b0 --- /dev/null +++ b/neutronclient/osc/v2/vpnaas/ipsec_site_connection.py @@ -0,0 +1,385 @@ +# Copyright 2017 FUJITSU LIMITED +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +from osc_lib.cli import format_columns +from osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils +from osc_lib.utils import columns as column_util +from oslo_log import log as logging + +from neutronclient._i18n import _ +from neutronclient.common import utils as nc_utils +from neutronclient.osc import utils as osc_utils +from neutronclient.osc.v2.vpnaas import utils as vpn_utils + + +LOG = logging.getLogger(__name__) + + +_formatters = { + 'peer_cidrs': format_columns.ListColumn +} + + +_attr_map = ( + ('id', 'ID', column_util.LIST_BOTH), + ('name', 'Name', column_util.LIST_BOTH), + ('peer_address', 'Peer Address', column_util.LIST_BOTH), + ('auth_mode', 'Authentication Algorithm', column_util.LIST_BOTH), + ('status', 'Status', column_util.LIST_BOTH), + ('project_id', 'Project', column_util.LIST_LONG_ONLY), + ('peer_cidrs', 'Peer CIDRs', column_util.LIST_LONG_ONLY), + ('vpnservice_id', 'VPN Service', column_util.LIST_LONG_ONLY), + ('ipsecpolicy_id', 'IPSec Policy', column_util.LIST_LONG_ONLY), + ('ikepolicy_id', 'IKE Policy', column_util.LIST_LONG_ONLY), + ('mtu', 'MTU', column_util.LIST_LONG_ONLY), + ('initiator', 'Initiator', column_util.LIST_LONG_ONLY), + ('is_admin_state_up', 'State', column_util.LIST_LONG_ONLY), + ('description', 'Description', column_util.LIST_LONG_ONLY), + ('psk', 'Pre-shared Key', column_util.LIST_LONG_ONLY), + ('route_mode', 'Route Mode', column_util.LIST_LONG_ONLY), + ('local_id', 'Local ID', column_util.LIST_LONG_ONLY), + ('peer_id', 'Peer ID', column_util.LIST_LONG_ONLY), + ('local_ep_group_id', 'Local Endpoint Group ID', + column_util.LIST_LONG_ONLY), + ('peer_ep_group_id', 'Peer Endpoint Group ID', column_util.LIST_LONG_ONLY), + ('dpd', 'DPD', column_util.LIST_LONG_ONLY), +) + +_attr_map_dict = { + 'id': 'ID', + 'name': 'Name', + 'peer_address': 'Peer Address', + 'auth_mode': 'Authentication Algorithm', + 'status': 'Status', + 'peer_cidrs': 'Peer CIDRs', + 'vpnservice_id': 'VPN Service', + 'ipsecpolicy_id': 'IPSec Policy', + 'ikepolicy_id': 'IKE Policy', + 'mtu': 'MTU', + 'initiator': 'Initiator', + 'is_admin_state_up': 'State', + 'psk': 'Pre-shared Key', + 'route_mode': 'Route Mode', + 'local_id': 'Local ID', + 'peer_id': 'Peer ID', + 'local_ep_group_id': 'Local Endpoint Group ID', + 'peer_ep_group_id': 'Peer Endpoint Group ID', + 'description': 'Description', + 'project_id': 'Project', + 'dpd': 'DPD', +} + + +def _convert_to_lowercase(string): + return string.lower() + + +def _get_common_parser(parser, is_create=True): + parser.add_argument( + '--description', + metavar='', + help=_('Description for the connection')) + parser.add_argument( + '--dpd', + metavar="action=ACTION,interval=INTERVAL,timeout=TIMEOUT", + type=nc_utils.str2dict_type( + optional_keys=['action', 'interval', 'timeout']), + help=vpn_utils.dpd_help("IPsec connection")) + parser.add_argument( + '--mtu', + help=_('MTU size for the connection')) + parser.add_argument( + '--initiator', + choices=['bi-directional', 'response-only'], + type=_convert_to_lowercase, + help=_('Initiator state')) + peer_group = parser.add_mutually_exclusive_group() + peer_group.add_argument( + '--peer-cidr', + dest='peer_cidrs', + help=_('Remote subnet(s) in CIDR format. ' + 'Cannot be specified when using endpoint groups. Only ' + 'applicable, if subnet provided for VPN service.') + ) + peer_group.add_argument( + '--local-endpoint-group', + help=_('Local endpoint group (name or ID) with subnet(s) ' + 'for IPsec connection') + ) + parser.add_argument( + '--peer-endpoint-group', + help=_('Peer endpoint group (name or ID) with CIDR(s) for ' + 'IPSec connection')) + admin_group = parser.add_mutually_exclusive_group() + admin_group.add_argument( + '--enable', + action='store_true', + help=_("Enable IPSec site connection") + ) + admin_group.add_argument( + '--disable', + action='store_true', + help=_("Disable IPSec site connection") + ) + parser.add_argument( + '--local-id', + help=_('An ID to be used instead of the external IP ' + 'address for a virtual router')) + return parser + + +def _get_common_attrs(client_manager, parsed_args, is_create=True): + attrs = {} + if is_create: + if 'project' in parsed_args and parsed_args.project is not None: + attrs['project_id'] = osc_utils.find_project( + client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + if parsed_args.description: + attrs['description'] = str(parsed_args.description) + if parsed_args.mtu: + attrs['mtu'] = parsed_args.mtu + if parsed_args.enable: + attrs['admin_state_up'] = True + if parsed_args.disable: + attrs['admin_state_up'] = False + if parsed_args.initiator: + attrs['initiator'] = parsed_args.initiator + if parsed_args.dpd: + vpn_utils.validate_dpd_dict(parsed_args.dpd) + attrs['dpd'] = parsed_args.dpd + if parsed_args.local_endpoint_group: + _local_epg = client_manager.network.find_vpn_endpoint_group( + parsed_args.local_endpoint_group)['id'] + attrs['local_ep_group_id'] = _local_epg + if parsed_args.peer_endpoint_group: + _peer_epg = client_manager.network.find_vpn_endpoint_group( + parsed_args.peer_endpoint_group)['id'] + attrs['peer_ep_group_id'] = _peer_epg + if parsed_args.peer_cidrs: + attrs['peer_cidrs'] = parsed_args.peer_cidrs + if parsed_args.local_id: + attrs['local_id'] = parsed_args.local_id + return attrs + + +class CreateIPsecSiteConnection(command.ShowOne): + _description = _("Create an IPsec site connection") + + def get_parser(self, prog_name): + parser = super(CreateIPsecSiteConnection, self).get_parser(prog_name) + _get_common_parser(parser) + parser.add_argument( + '--peer-id', + required=True, + help=_('Peer router identity for authentication. Can be ' + 'IPv4/IPv6 address, e-mail address, key id, or FQDN')) + parser.add_argument( + '--peer-address', + required=True, + help=_('Peer gateway public IPv4/IPv6 address or FQDN')) + parser.add_argument( + '--psk', + required=True, + help=_('Pre-shared key string.')) + parser.add_argument( + '--vpnservice', + metavar='VPNSERVICE', + required=True, + help=_('VPN service instance associated with this ' + 'connection (name or ID)')) + parser.add_argument( + '--ikepolicy', + metavar='IKEPOLICY', + required=True, + help=_('IKE policy associated with this connection (name or ID)')) + parser.add_argument( + '--ipsecpolicy', + metavar='IPSECPOLICY', + required=True, + help=_('IPsec policy associated with this connection ' + '(name or ID)')) + parser.add_argument( + 'name', + metavar='', + help=_('Set friendly name for the connection')) + osc_utils.add_project_owner_option_to_parser(parser) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + attrs = _get_common_attrs(self.app.client_manager, parsed_args) + if parsed_args.vpnservice: + _vpnservice_id = client.find_vpn_service( + parsed_args.vpnservice, ignore_missing=False)['id'] + attrs['vpnservice_id'] = _vpnservice_id + if parsed_args.ikepolicy: + _ikepolicy_id = client.find_vpn_ike_policy( + parsed_args.ikepolicy, ignore_missing=False)['id'] + attrs['ikepolicy_id'] = _ikepolicy_id + if parsed_args.ipsecpolicy: + _ipsecpolicy_id = client.find_vpn_ipsec_policy( + parsed_args.ipsecpolicy, ignore_missing=False)['id'] + attrs['ipsecpolicy_id'] = _ipsecpolicy_id + if parsed_args.peer_id: + attrs['peer_id'] = parsed_args.peer_id + if parsed_args.peer_address: + attrs['peer_address'] = parsed_args.peer_address + if parsed_args.psk: + attrs['psk'] = parsed_args.psk + if parsed_args.name: + attrs['name'] = parsed_args.name + if (bool(parsed_args.local_endpoint_group) != + bool(parsed_args.peer_endpoint_group)): + message = _("You must specify both local and peer endpoint " + "groups") + raise exceptions.CommandError(message) + if not parsed_args.peer_cidrs and not parsed_args.local_endpoint_group: + message = _("You must specify endpoint groups or peer CIDR(s)") + raise exceptions.CommandError(message) + obj = client.create_vpn_ipsec_site_connection(**attrs) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id', 'action', + 'timeout', 'interval']) + data = utils.get_dict_properties(obj, columns, formatters=_formatters) + return display_columns, data + + +class DeleteIPsecSiteConnection(command.Command): + _description = _("Delete IPsec site connection(s)") + + def get_parser(self, prog_name): + parser = super(DeleteIPsecSiteConnection, self).get_parser(prog_name) + parser.add_argument( + 'ipsec_site_connection', + metavar='', + nargs='+', + help=_('IPsec site connection to delete (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + result = 0 + for ipsec_conn in parsed_args.ipsec_site_connection: + try: + ipsec_con_id = client.find_vpn_ipsec_site_connection( + ipsec_conn, ignore_missing=False)['id'] + client.delete_vpn_ipsec_site_connection(ipsec_con_id) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete IPsec site connection with " + "name or ID '%(ipsec_site_conn)s': %(e)s"), + {'ipsec_site_conn': ipsec_conn, 'e': e}) + + if result > 0: + total = len(parsed_args.ipsec_site_connection) + msg = (_("%(result)s of %(total)s IPsec site connection failed " + "to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) + + +class ListIPsecSiteConnection(command.Lister): + _description = _("List IPsec site connections " + "that belong to a given project") + + def get_parser(self, prog_name): + parser = super(ListIPsecSiteConnection, self).get_parser(prog_name) + parser.add_argument( + '--long', + action='store_true', + default=False, + help=_("List additional fields in output") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + obj = client.vpn_ipsec_site_connections() + headers, columns = column_util.get_column_definitions( + _attr_map, long_listing=parsed_args.long) + return (headers, (utils.get_dict_properties( + s, columns, formatters=_formatters) for s in obj)) + + +class SetIPsecSiteConnection(command.Command): + _description = _("Set IPsec site connection properties") + + def get_parser(self, prog_name): + parser = super(SetIPsecSiteConnection, self).get_parser(prog_name) + _get_common_parser(parser) + parser.add_argument( + '--peer-id', + help=_('Peer router identity for authentication. Can be ' + 'IPv4/IPv6 address, e-mail address, key id, or FQDN')) + parser.add_argument( + '--peer-address', + help=_('Peer gateway public IPv4/IPv6 address or FQDN')) + parser.add_argument( + '--name', + metavar='', + help=_('Set friendly name for the connection')) + parser.add_argument( + 'ipsec_site_connection', + metavar='', + help=_('IPsec site connection to set (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + attrs = _get_common_attrs(self.app.client_manager, + parsed_args, is_create=False) + if parsed_args.peer_id: + attrs['peer_id'] = parsed_args.peer_id + if parsed_args.peer_address: + attrs['peer_address'] = parsed_args.peer_address + if parsed_args.name: + attrs['name'] = parsed_args.name + ipsec_conn_id = client.find_vpn_ipsec_site_connection( + parsed_args.ipsec_site_connection, ignore_missing=False)['id'] + try: + client.update_vpn_ipsec_site_connection(ipsec_conn_id, **attrs) + except Exception as e: + msg = (_("Failed to set IPsec site " + "connection '%(ipsec_conn)s': %(e)s") + % {'ipsec_conn': parsed_args.ipsec_site_connection, 'e': e}) + raise exceptions.CommandError(msg) + + +class ShowIPsecSiteConnection(command.ShowOne): + _description = _("Show information of a given IPsec site connection") + + def get_parser(self, prog_name): + parser = super(ShowIPsecSiteConnection, self).get_parser(prog_name) + parser.add_argument( + 'ipsec_site_connection', + metavar='', + help=_('IPsec site connection to display (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + ipsec_site_id = client.find_vpn_ipsec_site_connection( + parsed_args.ipsec_site_connection, ignore_missing=False)['id'] + obj = client.get_vpn_ipsec_site_connection(ipsec_site_id) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id', 'action', + 'timeout', 'interval']) + data = utils.get_dict_properties(obj, columns, formatters=_formatters) + return (display_columns, data) diff --git a/neutronclient/osc/v2/vpnaas/ipsecpolicy.py b/neutronclient/osc/v2/vpnaas/ipsecpolicy.py new file mode 100644 index 000000000..c6e42bd06 --- /dev/null +++ b/neutronclient/osc/v2/vpnaas/ipsecpolicy.py @@ -0,0 +1,318 @@ +# Copyright 2017 FUJITSU LIMITED +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +from osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils +from osc_lib.utils import columns as column_util +from oslo_log import log as logging + +from neutronclient._i18n import _ +from neutronclient.common import utils as nc_utils +from neutronclient.osc import utils as osc_utils +from neutronclient.osc.v2.vpnaas import utils as vpn_utils + + +LOG = logging.getLogger(__name__) + +_attr_map = ( + ('id', 'ID', column_util.LIST_BOTH), + ('name', 'Name', column_util.LIST_BOTH), + ('auth_algorithm', 'Authentication Algorithm', column_util.LIST_BOTH), + ('encapsulation_mode', 'Encapsulation Mode', column_util.LIST_BOTH), + ('transform_protocol', 'Transform Protocol', column_util.LIST_BOTH), + ('encryption_algorithm', 'Encryption Algorithm', column_util.LIST_BOTH), + ('pfs', 'Perfect Forward Secrecy (PFS)', column_util.LIST_LONG_ONLY), + ('description', 'Description', column_util.LIST_LONG_ONLY), + ('project_id', 'Project', column_util.LIST_LONG_ONLY), + ('lifetime', 'Lifetime', column_util.LIST_LONG_ONLY), +) + +_attr_map_dict = { + 'id': 'ID', + 'name': 'Name', + 'auth_algorithm': 'Authentication Algorithm', + 'encapsulation_mode': 'Encapsulation Mode', + 'transform_protocol': 'Transform Protocol', + 'encryption_algorithm': 'Encryption Algorithm', + 'pfs': 'Perfect Forward Secrecy (PFS)', + 'lifetime': 'Lifetime', + 'description': 'Description', + 'project_id': 'Project', +} + +_auth_algorithms = [ + 'sha1', + 'sha256', + 'sha384', + 'sha512', + 'aes-xcbc', + 'aes-cmac', +] + +_encryption_algorithms = [ + '3des', + 'aes-128', + 'aes-192', + 'aes-256', + 'aes-128-ccm-8', + 'aes-192-ccm-8', + 'aes-256-ccm-8', + 'aes-128-ccm-12', + 'aes-192-ccm-12', + 'aes-256-ccm-12', + 'aes-128-ccm-16', + 'aes-192-ccm-16', + 'aes-256-ccm-16', + 'aes-128-gcm-8', + 'aes-192-gcm-8', + 'aes-256-gcm-8', + 'aes-128-gcm-12', + 'aes-192-gcm-12', + 'aes-256-gcm-12', + 'aes-128-gcm-16', + 'aes-192-gcm-16', + 'aes-256-gcm-16', + 'aes-128-ctr', + 'aes-192-ctr', + 'aes-256-ctr', +] + +_pfs_groups = [ + 'group2', + 'group5', + 'group14', + 'group15', + 'group16', + 'group17', + 'group18', + 'group19', + 'group20', + 'group21', + 'group22', + 'group23', + 'group24', + 'group25', + 'group26', + 'group27', + 'group28', + 'group29', + 'group30', + 'group31', +] + + +def _convert_to_lowercase(string): + return string.lower() + + +def _get_common_parser(parser): + parser.add_argument( + '--description', + metavar='', + help=_('Description of the IPsec policy')) + parser.add_argument( + '--auth-algorithm', + choices=_auth_algorithms, + type=_convert_to_lowercase, + help=_('Authentication algorithm for IPsec policy')) + parser.add_argument( + '--encapsulation-mode', + choices=['tunnel', 'transport'], + type=_convert_to_lowercase, + help=_('Encapsulation mode for IPsec policy')) + parser.add_argument( + '--encryption-algorithm', + choices=_encryption_algorithms, + type=_convert_to_lowercase, + help=_('Encryption algorithm for IPsec policy')) + parser.add_argument( + '--lifetime', + metavar="units=UNITS,value=VALUE", + type=nc_utils.str2dict_type(optional_keys=['units', 'value']), + help=vpn_utils.lifetime_help("IPsec")) + parser.add_argument( + '--pfs', + choices=_pfs_groups, + type=_convert_to_lowercase, + help=_('Perfect Forward Secrecy for IPsec policy')) + parser.add_argument( + '--transform-protocol', + type=_convert_to_lowercase, + choices=['esp', 'ah', 'ah-esp'], + help=_('Transform protocol for IPsec policy')) + + +def _get_common_attrs(client_manager, parsed_args, is_create=True): + attrs = {} + if is_create: + if 'project' in parsed_args and parsed_args.project is not None: + attrs['project_id'] = osc_utils.find_project( + client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + if parsed_args.description: + attrs['description'] = str(parsed_args.description) + if parsed_args.auth_algorithm: + attrs['auth_algorithm'] = parsed_args.auth_algorithm + if parsed_args.encapsulation_mode: + attrs['encapsulation_mode'] = parsed_args.encapsulation_mode + if parsed_args.transform_protocol: + attrs['transform_protocol'] = parsed_args.transform_protocol + if parsed_args.encryption_algorithm: + attrs['encryption_algorithm'] = parsed_args.encryption_algorithm + if parsed_args.pfs: + attrs['pfs'] = parsed_args.pfs + if parsed_args.lifetime: + vpn_utils.validate_lifetime_dict(parsed_args.lifetime) + attrs['lifetime'] = parsed_args.lifetime + return attrs + + +class CreateIPsecPolicy(command.ShowOne): + _description = _("Create an IPsec policy") + + def get_parser(self, prog_name): + parser = super(CreateIPsecPolicy, self).get_parser(prog_name) + _get_common_parser(parser) + parser.add_argument( + 'name', + metavar='', + help=_('Name of the IPsec policy')) + osc_utils.add_project_owner_option_to_parser(parser) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + attrs = _get_common_attrs(self.app.client_manager, parsed_args) + if parsed_args.name: + attrs['name'] = str(parsed_args.name) + obj = client.create_vpn_ipsec_policy(**attrs) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id', + 'phase1_negotiation_mode', 'units', 'value']) + data = utils.get_dict_properties(obj, columns) + return display_columns, data + + +class DeleteIPsecPolicy(command.Command): + _description = _("Delete IPsec policy(policies)") + + def get_parser(self, prog_name): + parser = super(DeleteIPsecPolicy, self).get_parser(prog_name) + parser.add_argument( + 'ipsecpolicy', + metavar='', + nargs='+', + help=_('ipsec policy to delete (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + result = 0 + for ipsec in parsed_args.ipsecpolicy: + try: + ipsec_id = client.find_vpn_ipsec_policy( + ipsec, ignore_missing=False)['id'] + client.delete_vpn_ipsec_policy(ipsec_id) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete IPsec policy with " + "name or ID '%(ipsecpolicy)s': %(e)s"), + {'ipsecpolicy': ipsec, 'e': e}) + + if result > 0: + total = len(parsed_args.ipsecpolicy) + msg = (_("%(result)s of %(total)s IPsec policy failed " + "to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) + + +class ListIPsecPolicy(command.Lister): + _description = _("List IPsec policies that belong to a given project") + + def get_parser(self, prog_name): + parser = super(ListIPsecPolicy, self).get_parser(prog_name) + parser.add_argument( + '--long', + action='store_true', + default=False, + help=_("List additional fields in output") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + obj = client.vpn_ipsec_policies() + headers, columns = column_util.get_column_definitions( + _attr_map, long_listing=parsed_args.long) + return (headers, (utils.get_dict_properties(s, columns) for s in obj)) + + +class SetIPsecPolicy(command.Command): + _description = _("Set IPsec policy properties") + + def get_parser(self, prog_name): + parser = super(SetIPsecPolicy, self).get_parser(prog_name) + _get_common_parser(parser) + parser.add_argument( + '--name', + metavar='', + help=_('Name of the IPsec policy')) + parser.add_argument( + 'ipsecpolicy', + metavar='', + help=_('IPsec policy to set (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + attrs = _get_common_attrs(self.app.client_manager, + parsed_args, is_create=False) + if parsed_args.name: + attrs['name'] = str(parsed_args.name) + ipsec_id = client.find_vpn_ipsec_policy( + parsed_args.ipsecpolicy, ignore_missing=False)['id'] + try: + client.update_vpn_ipsec_policy(ipsec_id, **attrs) + except Exception as e: + msg = (_("Failed to set IPsec policy '%(ipsec)s': %(e)s") + % {'ipsec': parsed_args.ipsecpolicy, 'e': e}) + raise exceptions.CommandError(msg) + + +class ShowIPsecPolicy(command.ShowOne): + _description = _("Display IPsec policy details") + + def get_parser(self, prog_name): + parser = super(ShowIPsecPolicy, self).get_parser(prog_name) + parser.add_argument( + 'ipsecpolicy', + metavar='', + help=_('IPsec policy to display (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + ipsec_id = client.find_vpn_ipsec_policy( + parsed_args.ipsecpolicy, ignore_missing=False)['id'] + obj = client.get_vpn_ipsec_policy(ipsec_id) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id', + 'phase1_negotiation_mode', 'units', 'value']) + data = utils.get_dict_properties(obj, columns) + return (display_columns, data) diff --git a/neutronclient/osc/v2/vpnaas/utils.py b/neutronclient/osc/v2/vpnaas/utils.py new file mode 100644 index 000000000..2de5cc35e --- /dev/null +++ b/neutronclient/osc/v2/vpnaas/utils.py @@ -0,0 +1,112 @@ +# Copyright 2017 FUJITSU LIMITED +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + + +"""VPN Utilities and helper functions.""" + + +from neutronclient._i18n import _ +from neutronclient.common import exceptions + +DPD_SUPPORTED_ACTIONS = ['hold', 'clear', 'restart', + 'restart-by-peer', 'disabled'] +DPD_SUPPORTED_KEYS = ['action', 'interval', 'timeout'] + +lifetime_keys = ['units', 'value'] +lifetime_units = ['seconds'] + + +def validate_dpd_dict(dpd_dict): + for key, value in dpd_dict.items(): + if key not in DPD_SUPPORTED_KEYS: + message = _( + "DPD Dictionary KeyError: " + "Reason-Invalid DPD key : " + "'%(key)s' not in %(supported_key)s") % { + 'key': key, 'supported_key': DPD_SUPPORTED_KEYS} + raise exceptions.CommandError(message) + if key == 'action' and value not in DPD_SUPPORTED_ACTIONS: + message = _( + "DPD Dictionary ValueError: " + "Reason-Invalid DPD action : " + "'%(key_value)s' not in %(supported_action)s") % { + 'key_value': value, + 'supported_action': DPD_SUPPORTED_ACTIONS} + raise exceptions.CommandError(message) + if key in ('interval', 'timeout'): + try: + if int(value) <= 0: + raise ValueError() + except ValueError: + message = _( + "DPD Dictionary ValueError: " + "Reason-Invalid positive integer value: " + "'%(key)s' = %(value)s") % { + 'key': key, 'value': value} + raise exceptions.CommandError(message) + else: + dpd_dict[key] = int(value) + return + + +def validate_lifetime_dict(lifetime_dict): + + for key, value in lifetime_dict.items(): + if key not in lifetime_keys: + message = _( + "Lifetime Dictionary KeyError: " + "Reason-Invalid unit key : " + "'%(key)s' not in %(supported_key)s") % { + 'key': key, 'supported_key': lifetime_keys} + raise exceptions.CommandError(message) + if key == 'units' and value not in lifetime_units: + message = _( + "Lifetime Dictionary ValueError: " + "Reason-Invalid units : " + "'%(key_value)s' not in %(supported_units)s") % { + 'key_value': key, 'supported_units': lifetime_units} + raise exceptions.CommandError(message) + if key == 'value': + try: + if int(value) < 60: + raise ValueError() + except ValueError: + message = _( + "Lifetime Dictionary ValueError: " + "Reason-Invalid value should be at least 60:" + "'%(key_value)s' = %(value)s") % { + 'key_value': key, 'value': value} + raise exceptions.CommandError(message) + else: + lifetime_dict['value'] = int(value) + return + + +def lifetime_help(policy): + lifetime = _("%s lifetime attributes. " + "'units'-seconds, default:seconds. " + "'value'-non negative integer, default:3600.") % policy + return lifetime + + +def dpd_help(policy): + dpd = _(" %s Dead Peer Detection attributes." + " 'action'-hold,clear,disabled,restart,restart-by-peer." + " 'interval' and 'timeout' are non negative integers. " + " 'interval' should be less than 'timeout' value. " + " 'action', default:hold 'interval', default:30, " + " 'timeout', default:120.") % policy.capitalize() + return dpd diff --git a/neutronclient/osc/v2/vpnaas/vpnservice.py b/neutronclient/osc/v2/vpnaas/vpnservice.py new file mode 100644 index 000000000..9e1e489ca --- /dev/null +++ b/neutronclient/osc/v2/vpnaas/vpnservice.py @@ -0,0 +1,252 @@ +# Copyright 2017 FUJITSU LIMITED +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +from osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils +from osc_lib.utils import columns as column_util +from oslo_log import log as logging + +from neutronclient._i18n import _ +from neutronclient.osc import utils as osc_utils + + +LOG = logging.getLogger(__name__) + +_attr_map = ( + ('id', 'ID', column_util.LIST_BOTH), + ('name', 'Name', column_util.LIST_BOTH), + ('router_id', 'Router', column_util.LIST_BOTH), + ('subnet_id', 'Subnet', column_util.LIST_BOTH), + ('flavor_id', 'Flavor', column_util.LIST_BOTH), + ('is_admin_state_up', 'State', column_util.LIST_BOTH), + ('status', 'Status', column_util.LIST_BOTH), + ('description', 'Description', column_util.LIST_LONG_ONLY), + ('project_id', 'Project', column_util.LIST_LONG_ONLY), + ('external_v4_ip', 'Ext v4 IP', column_util.LIST_LONG_ONLY), + ('external_v6_ip', 'Ext v6 IP', column_util.LIST_LONG_ONLY), +) + +_attr_map_dict = { + 'id': 'ID', + 'name': 'Name', + 'router_id': 'Router', + 'subnet_id': 'Subnet', + 'flavor_id': 'Flavor', + 'is_admin_state_up': 'State', + 'status': 'Status', + 'description': 'Description', + 'project_id': 'Project', + 'external_v4_ip': 'Ext v4 IP', + 'external_v6_ip': 'Ext v6 IP', +} + + +def _get_common_parser(parser): + parser.add_argument( + '--description', + metavar='', + help=_('Description for the VPN service')) + parser.add_argument( + '--subnet', + metavar='', + help=_('Local private subnet (name or ID)')) + parser.add_argument( + '--flavor', + metavar='', + help=_('Flavor for the VPN service (name or ID)')) + admin_group = parser.add_mutually_exclusive_group() + admin_group.add_argument( + '--enable', + action='store_true', + help=_("Enable VPN service") + ) + admin_group.add_argument( + '--disable', + action='store_true', + help=_("Disable VPN service") + ) + return parser + + +def _get_common_attrs(client_manager, parsed_args, is_create=True): + attrs = {} + if is_create: + if 'project' in parsed_args and parsed_args.project is not None: + attrs['project_id'] = osc_utils.find_project( + client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + if parsed_args.description: + attrs['description'] = str(parsed_args.description) + if parsed_args.subnet: + _subnet_id = client_manager.network.find_subnet( + parsed_args.subnet).id + attrs['subnet_id'] = _subnet_id + if parsed_args.flavor: + _flavor_id = client_manager.network.find_flavor( + parsed_args.flavor, + ignore_missing=False + ).id + attrs['flavor_id'] = _flavor_id + if parsed_args.enable: + attrs['admin_state_up'] = True + if parsed_args.disable: + attrs['admin_state_up'] = False + return attrs + + +class CreateVPNService(command.ShowOne): + _description = _("Create an VPN service") + + def get_parser(self, prog_name): + parser = super(CreateVPNService, self).get_parser(prog_name) + _get_common_parser(parser) + parser.add_argument( + 'name', + metavar='', + help=_('Name for the VPN service')) + parser.add_argument( + '--router', + metavar='ROUTER', + required=True, + help=_('Router for the VPN service (name or ID)')) + osc_utils.add_project_owner_option_to_parser(parser) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + attrs = _get_common_attrs(self.app.client_manager, parsed_args) + if parsed_args.name: + attrs['name'] = str(parsed_args.name) + if parsed_args.router: + _router_id = client.find_router(parsed_args.router, + ignore_missing=False).id + attrs['router_id'] = _router_id + obj = client.create_vpn_service(**attrs) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id']) + data = utils.get_dict_properties(obj, columns) + return display_columns, data + + +class DeleteVPNService(command.Command): + _description = _("Delete VPN service(s)") + + def get_parser(self, prog_name): + parser = super(DeleteVPNService, self).get_parser(prog_name) + parser.add_argument( + 'vpnservice', + metavar='', + nargs='+', + help=_('VPN service to delete (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + result = 0 + for vpn in parsed_args.vpnservice: + try: + vpn_id = client.find_vpn_service(vpn, + ignore_missing=False)['id'] + client.delete_vpn_service(vpn_id) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete VPN service with " + "name or ID '%(vpnservice)s': %(e)s"), + {'vpnservice': vpn, 'e': e}) + + if result > 0: + total = len(parsed_args.vpnservice) + msg = (_("%(result)s of %(total)s vpn service failed " + "to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) + + +class ListVPNService(command.Lister): + _description = _("List VPN services that belong to a given project") + + def get_parser(self, prog_name): + parser = super(ListVPNService, self).get_parser(prog_name) + parser.add_argument( + '--long', + action='store_true', + default=False, + help=_("List additional fields in output") + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + obj = client.vpn_services() + headers, columns = column_util.get_column_definitions( + _attr_map, long_listing=parsed_args.long) + return (headers, (utils.get_dict_properties(s, columns) for s in obj)) + + +class SetVPNSercice(command.Command): + _description = _("Set VPN service properties") + + def get_parser(self, prog_name): + parser = super(SetVPNSercice, self).get_parser(prog_name) + _get_common_parser(parser) + parser.add_argument( + '--name', + metavar='', + help=_('Name for the VPN service')) + parser.add_argument( + 'vpnservice', + metavar='', + help=_('VPN service to modify (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + attrs = _get_common_attrs(self.app.client_manager, + parsed_args, is_create=False) + if parsed_args.name: + attrs['name'] = str(parsed_args.name) + vpn_id = client.find_vpn_service(parsed_args.vpnservice, + ignore_missing=False)['id'] + try: + client.update_vpn_service(vpn_id, **attrs) + except Exception as e: + msg = (_("Failed to set vpn service '%(vpn)s': %(e)s") + % {'vpn': parsed_args.vpnservice, 'e': e}) + raise exceptions.CommandError(msg) + + +class ShowVPNService(command.ShowOne): + _description = _("Display VPN service details") + + def get_parser(self, prog_name): + parser = super(ShowVPNService, self).get_parser(prog_name) + parser.add_argument( + 'vpnservice', + metavar='', + help=_('VPN service to display (name or ID)')) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + vpn_id = client.find_vpn_service(parsed_args.vpnservice, + ignore_missing=False)['id'] + obj = client.get_vpn_service(vpn_id) + display_columns, columns = utils.get_osc_show_columns_for_sdk_resource( + obj, _attr_map_dict, ['location', 'tenant_id']) + data = utils.get_dict_properties(obj, columns) + return (display_columns, data) diff --git a/neutronclient/shell.py b/neutronclient/shell.py deleted file mode 100644 index 5ddae52b2..000000000 --- a/neutronclient/shell.py +++ /dev/null @@ -1,1003 +0,0 @@ -# Copyright 2012 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. -# - -""" -Command-line interface to the Neutron APIs -""" - -from __future__ import print_function - -import argparse -import inspect -import itertools -import logging -import os -import sys - -from keystoneauth1 import session -import os_client_config -from oslo_utils import encodeutils - -from cliff import app -from cliff import command -from cliff import commandmanager - -from neutronclient._i18n import _ -from neutronclient.common import clientmanager -from neutronclient.common import exceptions as exc -from neutronclient.common import extension as client_extension -from neutronclient.common import utils -from neutronclient.neutron.v2_0 import address_scope -from neutronclient.neutron.v2_0 import agent -from neutronclient.neutron.v2_0 import agentscheduler -from neutronclient.neutron.v2_0 import auto_allocated_topology -from neutronclient.neutron.v2_0 import availability_zone -from neutronclient.neutron.v2_0.bgp import dragentscheduler as bgp_drsched -from neutronclient.neutron.v2_0.bgp import peer as bgp_peer -from neutronclient.neutron.v2_0.bgp import speaker as bgp_speaker -from neutronclient.neutron.v2_0 import extension -from neutronclient.neutron.v2_0.flavor import flavor -from neutronclient.neutron.v2_0.flavor import flavor_profile -from neutronclient.neutron.v2_0 import floatingip -from neutronclient.neutron.v2_0.fw import firewall -from neutronclient.neutron.v2_0.fw import firewallpolicy -from neutronclient.neutron.v2_0.fw import firewallrule -from neutronclient.neutron.v2_0.lb import healthmonitor as lb_healthmonitor -from neutronclient.neutron.v2_0.lb import member as lb_member -from neutronclient.neutron.v2_0.lb import pool as lb_pool -from neutronclient.neutron.v2_0.lb.v2 import healthmonitor as lbaas_healthmon -from neutronclient.neutron.v2_0.lb.v2 import l7policy as lbaas_l7policy -from neutronclient.neutron.v2_0.lb.v2 import l7rule as lbaas_l7rule -from neutronclient.neutron.v2_0.lb.v2 import listener as lbaas_listener -from neutronclient.neutron.v2_0.lb.v2 import loadbalancer as lbaas_loadbalancer -from neutronclient.neutron.v2_0.lb.v2 import member as lbaas_member -from neutronclient.neutron.v2_0.lb.v2 import pool as lbaas_pool -from neutronclient.neutron.v2_0.lb import vip as lb_vip -from neutronclient.neutron.v2_0 import metering -from neutronclient.neutron.v2_0 import network -from neutronclient.neutron.v2_0 import network_ip_availability -from neutronclient.neutron.v2_0 import port -from neutronclient.neutron.v2_0 import purge -from neutronclient.neutron.v2_0.qos import bandwidth_limit_rule -from neutronclient.neutron.v2_0.qos import dscp_marking_rule -from neutronclient.neutron.v2_0.qos import policy as qos_policy -from neutronclient.neutron.v2_0.qos import rule as qos_rule -from neutronclient.neutron.v2_0 import quota -from neutronclient.neutron.v2_0 import rbac -from neutronclient.neutron.v2_0 import router -from neutronclient.neutron.v2_0 import securitygroup -from neutronclient.neutron.v2_0 import servicetype -from neutronclient.neutron.v2_0 import subnet -from neutronclient.neutron.v2_0 import subnetpool -from neutronclient.neutron.v2_0 import tag -from neutronclient.neutron.v2_0.vpn import endpoint_group -from neutronclient.neutron.v2_0.vpn import ikepolicy -from neutronclient.neutron.v2_0.vpn import ipsec_site_connection -from neutronclient.neutron.v2_0.vpn import ipsecpolicy -from neutronclient.neutron.v2_0.vpn import vpnservice -from neutronclient.version import __version__ - - -VERSION = '2.0' -NEUTRON_API_VERSION = '2.0' - - -def run_command(cmd, cmd_parser, sub_argv): - _argv = sub_argv - index = -1 - values_specs = [] - if '--' in sub_argv: - index = sub_argv.index('--') - _argv = sub_argv[:index] - values_specs = sub_argv[index:] - known_args, _values_specs = cmd_parser.parse_known_args(_argv) - if(isinstance(cmd, subnet.CreateSubnet) and not known_args.cidr): - cidr = get_first_valid_cidr(_values_specs) - if cidr: - known_args.cidr = cidr - _values_specs.remove(cidr) - cmd.values_specs = (index == -1 and _values_specs or values_specs) - return cmd.run(known_args) - - -def get_first_valid_cidr(value_specs): - # Bug 1442771, argparse does not allow optional positional parameter - # to be separated from previous positional parameter. - # When cidr was separated from network, the value will not be able - # to be parsed into known_args, but saved to _values_specs instead. - for value in value_specs: - if utils.is_valid_cidr(value): - return value - - -def env(*_vars, **kwargs): - """Search for the first defined of possibly many env vars. - - Returns the first environment variable defined in vars, or - returns the default defined in kwargs. - - """ - for v in _vars: - value = os.environ.get(v, None) - if value: - return value - return kwargs.get('default', '') - - -def check_non_negative_int(value): - try: - value = int(value) - except ValueError: - raise argparse.ArgumentTypeError(_("invalid int value: %r") % value) - if value < 0: - raise argparse.ArgumentTypeError(_("input value %d is negative") % - value) - return value - - -class BashCompletionCommand(command.Command): - """Prints all of the commands and options for bash-completion.""" - - def take_action(self, parsed_args): - pass - - -COMMAND_V2 = { - 'bash-completion': BashCompletionCommand, - 'net-list': network.ListNetwork, - 'net-external-list': network.ListExternalNetwork, - 'net-show': network.ShowNetwork, - 'net-create': network.CreateNetwork, - 'net-delete': network.DeleteNetwork, - 'net-update': network.UpdateNetwork, - 'subnet-list': subnet.ListSubnet, - 'subnet-show': subnet.ShowSubnet, - 'subnet-create': subnet.CreateSubnet, - 'subnet-delete': subnet.DeleteSubnet, - 'subnet-update': subnet.UpdateSubnet, - 'subnetpool-list': subnetpool.ListSubnetPool, - 'subnetpool-show': subnetpool.ShowSubnetPool, - 'subnetpool-create': subnetpool.CreateSubnetPool, - 'subnetpool-delete': subnetpool.DeleteSubnetPool, - 'subnetpool-update': subnetpool.UpdateSubnetPool, - 'port-list': port.ListPort, - 'port-show': port.ShowPort, - 'port-create': port.CreatePort, - 'port-delete': port.DeletePort, - 'port-update': port.UpdatePort, - 'purge': purge.Purge, - 'quota-list': quota.ListQuota, - 'quota-show': quota.ShowQuota, - 'quota-default-show': quota.ShowQuotaDefault, - 'quota-delete': quota.DeleteQuota, - 'quota-update': quota.UpdateQuota, - 'ext-list': extension.ListExt, - 'ext-show': extension.ShowExt, - 'router-list': router.ListRouter, - 'router-port-list': port.ListRouterPort, - 'router-show': router.ShowRouter, - 'router-create': router.CreateRouter, - 'router-delete': router.DeleteRouter, - 'router-update': router.UpdateRouter, - 'router-interface-add': router.AddInterfaceRouter, - 'router-interface-delete': router.RemoveInterfaceRouter, - 'router-gateway-set': router.SetGatewayRouter, - 'router-gateway-clear': router.RemoveGatewayRouter, - 'floatingip-list': floatingip.ListFloatingIP, - 'floatingip-show': floatingip.ShowFloatingIP, - 'floatingip-create': floatingip.CreateFloatingIP, - 'floatingip-delete': floatingip.DeleteFloatingIP, - 'floatingip-associate': floatingip.AssociateFloatingIP, - 'floatingip-disassociate': floatingip.DisassociateFloatingIP, - 'security-group-list': securitygroup.ListSecurityGroup, - 'security-group-show': securitygroup.ShowSecurityGroup, - 'security-group-create': securitygroup.CreateSecurityGroup, - 'security-group-delete': securitygroup.DeleteSecurityGroup, - 'security-group-update': securitygroup.UpdateSecurityGroup, - 'security-group-rule-list': securitygroup.ListSecurityGroupRule, - 'security-group-rule-show': securitygroup.ShowSecurityGroupRule, - 'security-group-rule-create': securitygroup.CreateSecurityGroupRule, - 'security-group-rule-delete': securitygroup.DeleteSecurityGroupRule, - 'lbaas-loadbalancer-list': lbaas_loadbalancer.ListLoadBalancer, - 'lbaas-loadbalancer-show': lbaas_loadbalancer.ShowLoadBalancer, - 'lbaas-loadbalancer-create': lbaas_loadbalancer.CreateLoadBalancer, - 'lbaas-loadbalancer-update': lbaas_loadbalancer.UpdateLoadBalancer, - 'lbaas-loadbalancer-delete': lbaas_loadbalancer.DeleteLoadBalancer, - 'lbaas-loadbalancer-stats': lbaas_loadbalancer.RetrieveLoadBalancerStats, - 'lbaas-loadbalancer-status': lbaas_loadbalancer.RetrieveLoadBalancerStatus, - 'lbaas-listener-list': lbaas_listener.ListListener, - 'lbaas-listener-show': lbaas_listener.ShowListener, - 'lbaas-listener-create': lbaas_listener.CreateListener, - 'lbaas-listener-update': lbaas_listener.UpdateListener, - 'lbaas-listener-delete': lbaas_listener.DeleteListener, - 'lbaas-l7policy-list': lbaas_l7policy.ListL7Policy, - 'lbaas-l7policy-show': lbaas_l7policy.ShowL7Policy, - 'lbaas-l7policy-create': lbaas_l7policy.CreateL7Policy, - 'lbaas-l7policy-update': lbaas_l7policy.UpdateL7Policy, - 'lbaas-l7policy-delete': lbaas_l7policy.DeleteL7Policy, - 'lbaas-l7rule-list': lbaas_l7rule.ListL7Rule, - 'lbaas-l7rule-show': lbaas_l7rule.ShowL7Rule, - 'lbaas-l7rule-create': lbaas_l7rule.CreateL7Rule, - 'lbaas-l7rule-update': lbaas_l7rule.UpdateL7Rule, - 'lbaas-l7rule-delete': lbaas_l7rule.DeleteL7Rule, - 'lbaas-pool-list': lbaas_pool.ListPool, - 'lbaas-pool-show': lbaas_pool.ShowPool, - 'lbaas-pool-create': lbaas_pool.CreatePool, - 'lbaas-pool-update': lbaas_pool.UpdatePool, - 'lbaas-pool-delete': lbaas_pool.DeletePool, - 'lbaas-healthmonitor-list': lbaas_healthmon.ListHealthMonitor, - 'lbaas-healthmonitor-show': lbaas_healthmon.ShowHealthMonitor, - 'lbaas-healthmonitor-create': lbaas_healthmon.CreateHealthMonitor, - 'lbaas-healthmonitor-update': lbaas_healthmon.UpdateHealthMonitor, - 'lbaas-healthmonitor-delete': lbaas_healthmon.DeleteHealthMonitor, - 'lbaas-member-list': lbaas_member.ListMember, - 'lbaas-member-show': lbaas_member.ShowMember, - 'lbaas-member-create': lbaas_member.CreateMember, - 'lbaas-member-update': lbaas_member.UpdateMember, - 'lbaas-member-delete': lbaas_member.DeleteMember, - 'lb-vip-list': lb_vip.ListVip, - 'lb-vip-show': lb_vip.ShowVip, - 'lb-vip-create': lb_vip.CreateVip, - 'lb-vip-update': lb_vip.UpdateVip, - 'lb-vip-delete': lb_vip.DeleteVip, - 'lb-pool-list': lb_pool.ListPool, - 'lb-pool-show': lb_pool.ShowPool, - 'lb-pool-create': lb_pool.CreatePool, - 'lb-pool-update': lb_pool.UpdatePool, - 'lb-pool-delete': lb_pool.DeletePool, - 'lb-pool-stats': lb_pool.RetrievePoolStats, - 'lb-member-list': lb_member.ListMember, - 'lb-member-show': lb_member.ShowMember, - 'lb-member-create': lb_member.CreateMember, - 'lb-member-update': lb_member.UpdateMember, - 'lb-member-delete': lb_member.DeleteMember, - 'lb-healthmonitor-list': lb_healthmonitor.ListHealthMonitor, - 'lb-healthmonitor-show': lb_healthmonitor.ShowHealthMonitor, - 'lb-healthmonitor-create': lb_healthmonitor.CreateHealthMonitor, - 'lb-healthmonitor-update': lb_healthmonitor.UpdateHealthMonitor, - 'lb-healthmonitor-delete': lb_healthmonitor.DeleteHealthMonitor, - 'lb-healthmonitor-associate': lb_healthmonitor.AssociateHealthMonitor, - 'lb-healthmonitor-disassociate': ( - lb_healthmonitor.DisassociateHealthMonitor - ), - 'agent-list': agent.ListAgent, - 'agent-show': agent.ShowAgent, - 'agent-delete': agent.DeleteAgent, - 'agent-update': agent.UpdateAgent, - 'dhcp-agent-network-add': agentscheduler.AddNetworkToDhcpAgent, - 'dhcp-agent-network-remove': agentscheduler.RemoveNetworkFromDhcpAgent, - 'net-list-on-dhcp-agent': agentscheduler.ListNetworksOnDhcpAgent, - 'dhcp-agent-list-hosting-net': agentscheduler.ListDhcpAgentsHostingNetwork, - 'l3-agent-router-add': agentscheduler.AddRouterToL3Agent, - 'l3-agent-router-remove': agentscheduler.RemoveRouterFromL3Agent, - 'router-list-on-l3-agent': agentscheduler.ListRoutersOnL3Agent, - 'l3-agent-list-hosting-router': agentscheduler.ListL3AgentsHostingRouter, - 'lb-pool-list-on-agent': agentscheduler.ListPoolsOnLbaasAgent, - 'lb-agent-hosting-pool': agentscheduler.GetLbaasAgentHostingPool, - 'lbaas-loadbalancer-list-on-agent': - agentscheduler.ListLoadBalancersOnLbaasAgent, - 'lbaas-agent-hosting-loadbalancer': - agentscheduler.GetLbaasAgentHostingLoadBalancer, - 'service-provider-list': servicetype.ListServiceProvider, - 'firewall-rule-list': firewallrule.ListFirewallRule, - 'firewall-rule-show': firewallrule.ShowFirewallRule, - 'firewall-rule-create': firewallrule.CreateFirewallRule, - 'firewall-rule-update': firewallrule.UpdateFirewallRule, - 'firewall-rule-delete': firewallrule.DeleteFirewallRule, - 'firewall-policy-list': firewallpolicy.ListFirewallPolicy, - 'firewall-policy-show': firewallpolicy.ShowFirewallPolicy, - 'firewall-policy-create': firewallpolicy.CreateFirewallPolicy, - 'firewall-policy-update': firewallpolicy.UpdateFirewallPolicy, - 'firewall-policy-delete': firewallpolicy.DeleteFirewallPolicy, - 'firewall-policy-insert-rule': firewallpolicy.FirewallPolicyInsertRule, - 'firewall-policy-remove-rule': firewallpolicy.FirewallPolicyRemoveRule, - 'firewall-list': firewall.ListFirewall, - 'firewall-show': firewall.ShowFirewall, - 'firewall-create': firewall.CreateFirewall, - 'firewall-update': firewall.UpdateFirewall, - 'firewall-delete': firewall.DeleteFirewall, - 'ipsec-site-connection-list': ( - ipsec_site_connection.ListIPsecSiteConnection - ), - 'ipsec-site-connection-show': ( - ipsec_site_connection.ShowIPsecSiteConnection - ), - 'ipsec-site-connection-create': ( - ipsec_site_connection.CreateIPsecSiteConnection - ), - 'ipsec-site-connection-update': ( - ipsec_site_connection.UpdateIPsecSiteConnection - ), - 'ipsec-site-connection-delete': ( - ipsec_site_connection.DeleteIPsecSiteConnection - ), - 'vpn-endpoint-group-list': endpoint_group.ListEndpointGroup, - 'vpn-endpoint-group-show': endpoint_group.ShowEndpointGroup, - 'vpn-endpoint-group-create': endpoint_group.CreateEndpointGroup, - 'vpn-endpoint-group-update': endpoint_group.UpdateEndpointGroup, - 'vpn-endpoint-group-delete': endpoint_group.DeleteEndpointGroup, - 'vpn-service-list': vpnservice.ListVPNService, - 'vpn-service-show': vpnservice.ShowVPNService, - 'vpn-service-create': vpnservice.CreateVPNService, - 'vpn-service-update': vpnservice.UpdateVPNService, - 'vpn-service-delete': vpnservice.DeleteVPNService, - 'vpn-ipsecpolicy-list': ipsecpolicy.ListIPsecPolicy, - 'vpn-ipsecpolicy-show': ipsecpolicy.ShowIPsecPolicy, - 'vpn-ipsecpolicy-create': ipsecpolicy.CreateIPsecPolicy, - 'vpn-ipsecpolicy-update': ipsecpolicy.UpdateIPsecPolicy, - 'vpn-ipsecpolicy-delete': ipsecpolicy.DeleteIPsecPolicy, - 'vpn-ikepolicy-list': ikepolicy.ListIKEPolicy, - 'vpn-ikepolicy-show': ikepolicy.ShowIKEPolicy, - 'vpn-ikepolicy-create': ikepolicy.CreateIKEPolicy, - 'vpn-ikepolicy-update': ikepolicy.UpdateIKEPolicy, - 'vpn-ikepolicy-delete': ikepolicy.DeleteIKEPolicy, - 'meter-label-create': metering.CreateMeteringLabel, - 'meter-label-list': metering.ListMeteringLabel, - 'meter-label-show': metering.ShowMeteringLabel, - 'meter-label-delete': metering.DeleteMeteringLabel, - 'meter-label-rule-create': metering.CreateMeteringLabelRule, - 'meter-label-rule-list': metering.ListMeteringLabelRule, - 'meter-label-rule-show': metering.ShowMeteringLabelRule, - 'meter-label-rule-delete': metering.DeleteMeteringLabelRule, - 'rbac-create': rbac.CreateRBACPolicy, - 'rbac-update': rbac.UpdateRBACPolicy, - 'rbac-list': rbac.ListRBACPolicy, - 'rbac-show': rbac.ShowRBACPolicy, - 'rbac-delete': rbac.DeleteRBACPolicy, - 'address-scope-list': address_scope.ListAddressScope, - 'address-scope-show': address_scope.ShowAddressScope, - 'address-scope-create': address_scope.CreateAddressScope, - 'address-scope-delete': address_scope.DeleteAddressScope, - 'address-scope-update': address_scope.UpdateAddressScope, - 'qos-policy-list': qos_policy.ListQoSPolicy, - 'qos-policy-show': qos_policy.ShowQoSPolicy, - 'qos-policy-create': qos_policy.CreateQoSPolicy, - 'qos-policy-update': qos_policy.UpdateQoSPolicy, - 'qos-policy-delete': qos_policy.DeleteQoSPolicy, - 'qos-bandwidth-limit-rule-create': ( - bandwidth_limit_rule.CreateQoSBandwidthLimitRule - ), - 'qos-bandwidth-limit-rule-show': ( - bandwidth_limit_rule.ShowQoSBandwidthLimitRule - ), - 'qos-bandwidth-limit-rule-list': ( - bandwidth_limit_rule.ListQoSBandwidthLimitRules - ), - 'qos-bandwidth-limit-rule-update': ( - bandwidth_limit_rule.UpdateQoSBandwidthLimitRule - ), - 'qos-bandwidth-limit-rule-delete': ( - bandwidth_limit_rule.DeleteQoSBandwidthLimitRule - ), - 'qos-dscp-marking-rule-create': ( - dscp_marking_rule.CreateQoSDscpMarkingRule - ), - 'qos-dscp-marking-rule-show': ( - dscp_marking_rule.ShowQoSDscpMarkingRule - ), - 'qos-dscp-marking-rule-list': ( - dscp_marking_rule.ListQoSDscpMarkingRules - ), - 'qos-dscp-marking-rule-update': ( - dscp_marking_rule.UpdateQoSDscpMarkingRule - ), - 'qos-dscp-marking-rule-delete': ( - dscp_marking_rule.DeleteQoSDscpMarkingRule - ), - 'qos-available-rule-types': qos_rule.ListQoSRuleTypes, - 'flavor-list': flavor.ListFlavor, - 'flavor-show': flavor.ShowFlavor, - 'flavor-create': flavor.CreateFlavor, - 'flavor-delete': flavor.DeleteFlavor, - 'flavor-update': flavor.UpdateFlavor, - 'flavor-associate': flavor.AssociateFlavor, - 'flavor-disassociate': flavor.DisassociateFlavor, - 'flavor-profile-list': flavor_profile.ListFlavorProfile, - 'flavor-profile-show': flavor_profile.ShowFlavorProfile, - 'flavor-profile-create': flavor_profile.CreateFlavorProfile, - 'flavor-profile-delete': flavor_profile.DeleteFlavorProfile, - 'flavor-profile-update': flavor_profile.UpdateFlavorProfile, - 'availability-zone-list': availability_zone.ListAvailabilityZone, - 'auto-allocated-topology-show': ( - auto_allocated_topology.ShowAutoAllocatedTopology), - 'bgp-dragent-speaker-add': ( - bgp_drsched.AddBGPSpeakerToDRAgent - ), - 'bgp-dragent-speaker-remove': ( - bgp_drsched.RemoveBGPSpeakerFromDRAgent - ), - 'bgp-speaker-list-on-dragent': ( - bgp_drsched.ListBGPSpeakersOnDRAgent - ), - 'bgp-dragent-list-hosting-speaker': ( - bgp_drsched.ListDRAgentsHostingBGPSpeaker - ), - 'bgp-speaker-list': bgp_speaker.ListSpeakers, - 'bgp-speaker-advertiseroute-list': ( - bgp_speaker.ListRoutesAdvertisedBySpeaker - ), - 'bgp-speaker-show': bgp_speaker.ShowSpeaker, - 'bgp-speaker-create': bgp_speaker.CreateSpeaker, - 'bgp-speaker-update': bgp_speaker.UpdateSpeaker, - 'bgp-speaker-delete': bgp_speaker.DeleteSpeaker, - 'bgp-speaker-peer-add': bgp_speaker.AddPeerToSpeaker, - 'bgp-speaker-peer-remove': bgp_speaker.RemovePeerFromSpeaker, - 'bgp-speaker-network-add': bgp_speaker.AddNetworkToSpeaker, - 'bgp-speaker-network-remove': bgp_speaker.RemoveNetworkFromSpeaker, - 'bgp-peer-list': bgp_peer.ListPeers, - 'bgp-peer-show': bgp_peer.ShowPeer, - 'bgp-peer-create': bgp_peer.CreatePeer, - 'bgp-peer-update': bgp_peer.UpdatePeer, - 'bgp-peer-delete': bgp_peer.DeletePeer, - 'net-ip-availability-list': network_ip_availability.ListIpAvailability, - 'net-ip-availability-show': network_ip_availability.ShowIpAvailability, - 'tag-add': tag.AddTag, - 'tag-replace': tag.ReplaceTag, - 'tag-remove': tag.RemoveTag, -} - -COMMANDS = {'2.0': COMMAND_V2} - - -class HelpAction(argparse.Action): - """Print help message including sub-commands - - Provide a custom action so the -h and --help options - to the main app will print a list of the commands. - - The commands are determined by checking the CommandManager - instance, passed in as the "default" value for the action. - """ - def __call__(self, parser, namespace, values, option_string=None): - outputs = [] - max_len = 0 - app = self.default - parser.print_help(app.stdout) - app.stdout.write(_('\nCommands for API v%s:\n') % app.api_version) - command_manager = app.command_manager - for name, ep in sorted(command_manager): - factory = ep.load() - cmd = factory(self, None) - one_liner = cmd.get_description().split('\n')[0] - outputs.append((name, one_liner)) - max_len = max(len(name), max_len) - for (name, one_liner) in outputs: - app.stdout.write(' %s %s\n' % (name.ljust(max_len), one_liner)) - sys.exit(0) - - -class NeutronShell(app.App): - - # verbose logging levels - WARNING_LEVEL = 0 - INFO_LEVEL = 1 - DEBUG_LEVEL = 2 - CONSOLE_MESSAGE_FORMAT = '%(message)s' - DEBUG_MESSAGE_FORMAT = '%(levelname)s: %(name)s %(message)s' - log = logging.getLogger(__name__) - - def __init__(self, apiversion): - super(NeutronShell, self).__init__( - description=__doc__.strip(), - version=VERSION, - command_manager=commandmanager.CommandManager('neutron.cli'), ) - self.commands = COMMANDS - for k, v in self.commands[apiversion].items(): - self.command_manager.add_command(k, v) - - self._register_extensions(VERSION) - - # Pop the 'complete' to correct the outputs of 'neutron help'. - self.command_manager.commands.pop('complete') - - # This is instantiated in initialize_app() only when using - # password flow auth - self.auth_client = None - self.api_version = apiversion - - def build_option_parser(self, description, version): - """Return an argparse option parser for this application. - - Subclasses may override this method to extend - the parser with more global options. - - :param description: full description of the application - :paramtype description: str - :param version: version number for the application - :paramtype version: str - """ - parser = argparse.ArgumentParser( - description=description, - add_help=False, ) - parser.add_argument( - '--version', - action='version', - version=__version__, ) - parser.add_argument( - '-v', '--verbose', '--debug', - action='count', - dest='verbose_level', - default=self.DEFAULT_VERBOSE_LEVEL, - help=_('Increase verbosity of output and show tracebacks on' - ' errors. You can repeat this option.')) - parser.add_argument( - '-q', '--quiet', - action='store_const', - dest='verbose_level', - const=0, - help=_('Suppress output except warnings and errors.')) - parser.add_argument( - '-h', '--help', - action=HelpAction, - nargs=0, - default=self, # tricky - help=_("Show this help message and exit.")) - parser.add_argument( - '-r', '--retries', - metavar="NUM", - type=check_non_negative_int, - default=0, - help=_("How many times the request to the Neutron server should " - "be retried if it fails.")) - # FIXME(bklei): this method should come from keystoneauth1 - self._append_global_identity_args(parser) - - return parser - - def _append_global_identity_args(self, parser): - # FIXME(bklei): these are global identity (Keystone) arguments which - # should be consistent and shared by all service clients. Therefore, - # they should be provided by keystoneauth1. We will need to - # refactor this code once this functionality is available in - # keystoneauth1. - # - # Note: At that time we'll need to decide if we can just abandon - # the deprecated args (--service-type and --endpoint-type). - - parser.add_argument( - '--os-service-type', metavar='', - default=env('OS_NETWORK_SERVICE_TYPE', default='network'), - help=_('Defaults to env[OS_NETWORK_SERVICE_TYPE] or network.')) - - parser.add_argument( - '--os-endpoint-type', metavar='', - default=env('OS_ENDPOINT_TYPE', default='public'), - help=_('Defaults to env[OS_ENDPOINT_TYPE] or public.')) - - # FIXME(bklei): --service-type is deprecated but kept in for - # backward compatibility. - parser.add_argument( - '--service-type', metavar='', - default=env('OS_NETWORK_SERVICE_TYPE', default='network'), - help=_('DEPRECATED! Use --os-service-type.')) - - # FIXME(bklei): --endpoint-type is deprecated but kept in for - # backward compatibility. - parser.add_argument( - '--endpoint-type', metavar='', - default=env('OS_ENDPOINT_TYPE', default='public'), - help=_('DEPRECATED! Use --os-endpoint-type.')) - - parser.add_argument( - '--os-auth-strategy', metavar='', - default=env('OS_AUTH_STRATEGY', default='keystone'), - help=_('DEPRECATED! Only keystone is supported.')) - - parser.add_argument( - '--os_auth_strategy', - help=argparse.SUPPRESS) - - parser.add_argument( - '--os-cloud', metavar='', - default=env('OS_CLOUD', default=None), - help=_('Defaults to env[OS_CLOUD].')) - - parser.add_argument( - '--os-auth-url', metavar='', - default=env('OS_AUTH_URL'), - help=_('Authentication URL, defaults to env[OS_AUTH_URL].')) - parser.add_argument( - '--os_auth_url', - help=argparse.SUPPRESS) - - project_name_group = parser.add_mutually_exclusive_group() - project_name_group.add_argument( - '--os-tenant-name', metavar='', - default=env('OS_TENANT_NAME'), - help=_('Authentication tenant name, defaults to ' - 'env[OS_TENANT_NAME].')) - project_name_group.add_argument( - '--os-project-name', - metavar='', - default=utils.env('OS_PROJECT_NAME'), - help=_('Another way to specify tenant name. ' - 'This option is mutually exclusive with ' - ' --os-tenant-name. ' - 'Defaults to env[OS_PROJECT_NAME].')) - - parser.add_argument( - '--os_tenant_name', - help=argparse.SUPPRESS) - - project_id_group = parser.add_mutually_exclusive_group() - project_id_group.add_argument( - '--os-tenant-id', metavar='', - default=env('OS_TENANT_ID'), - help=_('Authentication tenant ID, defaults to ' - 'env[OS_TENANT_ID].')) - project_id_group.add_argument( - '--os-project-id', - metavar='', - default=utils.env('OS_PROJECT_ID'), - help=_('Another way to specify tenant ID. ' - 'This option is mutually exclusive with ' - ' --os-tenant-id. ' - 'Defaults to env[OS_PROJECT_ID].')) - - parser.add_argument( - '--os-username', metavar='', - default=utils.env('OS_USERNAME'), - help=_('Authentication username, defaults to env[OS_USERNAME].')) - parser.add_argument( - '--os_username', - help=argparse.SUPPRESS) - - parser.add_argument( - '--os-user-id', metavar='', - default=env('OS_USER_ID'), - help=_('Authentication user ID (Env: OS_USER_ID)')) - - parser.add_argument( - '--os_user_id', - help=argparse.SUPPRESS) - - parser.add_argument( - '--os-user-domain-id', - metavar='', - default=utils.env('OS_USER_DOMAIN_ID'), - help=_('OpenStack user domain ID. ' - 'Defaults to env[OS_USER_DOMAIN_ID].')) - - parser.add_argument( - '--os_user_domain_id', - help=argparse.SUPPRESS) - - parser.add_argument( - '--os-user-domain-name', - metavar='', - default=utils.env('OS_USER_DOMAIN_NAME'), - help=_('OpenStack user domain name. ' - 'Defaults to env[OS_USER_DOMAIN_NAME].')) - - parser.add_argument( - '--os_user_domain_name', - help=argparse.SUPPRESS) - - parser.add_argument( - '--os_project_id', - help=argparse.SUPPRESS) - - parser.add_argument( - '--os_project_name', - help=argparse.SUPPRESS) - - parser.add_argument( - '--os-project-domain-id', - metavar='', - default=utils.env('OS_PROJECT_DOMAIN_ID'), - help=_('Defaults to env[OS_PROJECT_DOMAIN_ID].')) - - parser.add_argument( - '--os-project-domain-name', - metavar='', - default=utils.env('OS_PROJECT_DOMAIN_NAME'), - help=_('Defaults to env[OS_PROJECT_DOMAIN_NAME].')) - - parser.add_argument( - '--os-cert', - metavar='', - default=utils.env('OS_CERT'), - help=_("Path of certificate file to use in SSL " - "connection. This file can optionally be " - "prepended with the private key. Defaults " - "to env[OS_CERT].")) - - parser.add_argument( - '--os-cacert', - metavar='', - default=env('OS_CACERT', default=None), - help=_("Specify a CA bundle file to use in " - "verifying a TLS (https) server certificate. " - "Defaults to env[OS_CACERT].")) - - parser.add_argument( - '--os-key', - metavar='', - default=utils.env('OS_KEY'), - help=_("Path of client key to use in SSL " - "connection. This option is not necessary " - "if your key is prepended to your certificate " - "file. Defaults to env[OS_KEY].")) - - parser.add_argument( - '--os-password', metavar='', - default=utils.env('OS_PASSWORD'), - help=_('Authentication password, defaults to env[OS_PASSWORD].')) - parser.add_argument( - '--os_password', - help=argparse.SUPPRESS) - - parser.add_argument( - '--os-region-name', metavar='', - default=env('OS_REGION_NAME'), - help=_('Authentication region name, defaults to ' - 'env[OS_REGION_NAME].')) - parser.add_argument( - '--os_region_name', - help=argparse.SUPPRESS) - - parser.add_argument( - '--os-token', metavar='', - default=env('OS_TOKEN'), - help=_('Authentication token, defaults to env[OS_TOKEN].')) - parser.add_argument( - '--os_token', - help=argparse.SUPPRESS) - - parser.add_argument( - '--http-timeout', metavar='', - default=env('OS_NETWORK_TIMEOUT', default=None), type=float, - help=_('Timeout in seconds to wait for an HTTP response. Defaults ' - 'to env[OS_NETWORK_TIMEOUT] or None if not specified.')) - - parser.add_argument( - '--os-url', metavar='', - default=env('OS_URL'), - help=_('Defaults to env[OS_URL].')) - parser.add_argument( - '--os_url', - help=argparse.SUPPRESS) - - parser.add_argument( - '--insecure', - action='store_true', - default=env('NEUTRONCLIENT_INSECURE', default=False), - help=_("Explicitly allow neutronclient to perform \"insecure\" " - "SSL (https) requests. The server's certificate will " - "not be verified against any certificate authorities. " - "This option should be used with caution.")) - - def _bash_completion(self): - """Prints all of the commands and options for bash-completion.""" - commands = set() - options = set() - for option, _action in self.parser._option_string_actions.items(): - options.add(option) - for _name, _command in self.command_manager: - commands.add(_name) - cmd_factory = _command.load() - cmd = cmd_factory(self, None) - cmd_parser = cmd.get_parser('') - for option, _action in cmd_parser._option_string_actions.items(): - options.add(option) - print(' '.join(commands | options)) - - def _register_extensions(self, version): - for name, module in itertools.chain( - client_extension._discover_via_entry_points()): - self._extend_shell_commands(name, module, version) - - def _extend_shell_commands(self, name, module, version): - classes = inspect.getmembers(module, inspect.isclass) - for cls_name, cls in classes: - if (issubclass(cls, client_extension.NeutronClientExtension) and - hasattr(cls, 'shell_command')): - cmd = cls.shell_command - if hasattr(cls, 'versions'): - if version not in cls.versions: - continue - try: - name_prefix = "[%s]" % name - cls.__doc__ = ("%s %s" % (name_prefix, cls.__doc__) if - cls.__doc__ else name_prefix) - self.command_manager.add_command(cmd, cls) - self.commands[version][cmd] = cls - except TypeError: - pass - - def run(self, argv): - """Equivalent to the main program for the application. - - :param argv: input arguments and options - :paramtype argv: list of str - """ - try: - index = 0 - command_pos = -1 - help_pos = -1 - help_command_pos = -1 - for arg in argv: - if arg == 'bash-completion' and help_command_pos == -1: - self._bash_completion() - return 0 - if arg in self.commands[self.api_version]: - if command_pos == -1: - command_pos = index - elif arg in ('-h', '--help'): - if help_pos == -1: - help_pos = index - elif arg == 'help': - if help_command_pos == -1: - help_command_pos = index - index = index + 1 - if command_pos > -1 and help_pos > command_pos: - argv = ['help', argv[command_pos]] - if help_command_pos > -1 and command_pos == -1: - argv[help_command_pos] = '--help' - self.options, remainder = self.parser.parse_known_args(argv) - self.configure_logging() - self.interactive_mode = not remainder - self.initialize_app(remainder) - except Exception as err: - if self.options.verbose_level >= self.DEBUG_LEVEL: - self.log.exception(err) - raise - else: - self.log.error(err) - return 1 - if self.interactive_mode: - _argv = [sys.argv[0]] - sys.argv = _argv - return self.interact() - return self.run_subcommand(remainder) - - def run_subcommand(self, argv): - subcommand = self.command_manager.find_command(argv) - cmd_factory, cmd_name, sub_argv = subcommand - cmd = cmd_factory(self, self.options) - try: - self.prepare_to_run_command(cmd) - full_name = (cmd_name - if self.interactive_mode - else ' '.join([self.NAME, cmd_name]) - ) - cmd_parser = cmd.get_parser(full_name) - return run_command(cmd, cmd_parser, sub_argv) - except SystemExit: - print(_("Try 'neutron help %s' for more information.") % - cmd_name, file=sys.stderr) - raise - except Exception as e: - if self.options.verbose_level >= self.DEBUG_LEVEL: - self.log.exception("%s", e) - raise - self.log.error("%s", e) - return 1 - - def authenticate_user(self): - """Confirm user authentication - - Make sure the user has provided all of the authentication - info we need. - """ - cloud_config = os_client_config.OpenStackConfig().get_one_cloud( - cloud=self.options.os_cloud, argparse=self.options, - network_api_version=self.api_version, - verify=not self.options.insecure) - verify, cert = cloud_config.get_requests_verify_args() - - # TODO(singhj): Remove dependancy on HTTPClient - # for the case of token-endpoint authentication - - # When using token-endpoint authentication legacy - # HTTPClient will be used, otherwise SessionClient - # will be used. - if self.options.os_token and self.options.os_url: - auth = None - auth_session = None - else: - auth = cloud_config.get_auth() - - auth_session = session.Session( - auth=auth, verify=verify, cert=cert, - timeout=self.options.http_timeout) - - interface = self.options.os_endpoint_type or self.endpoint_type - if interface.endswith('URL'): - interface = interface[:-3] - self.client_manager = clientmanager.ClientManager( - retries=self.options.retries, - raise_errors=False, - session=auth_session, - url=self.options.os_url, - token=self.options.os_token, - region_name=cloud_config.get_region_name(), - api_version=cloud_config.get_api_version('network'), - service_type=cloud_config.get_service_type('network'), - service_name=cloud_config.get_service_name('network'), - endpoint_type=interface, - auth=auth, - insecure=not verify, - log_credentials=True) - return - - def initialize_app(self, argv): - """Global app init bits: - - * set up API versions - * validate authentication info - """ - - super(NeutronShell, self).initialize_app(argv) - - self.api_version = {'network': self.api_version} - - # If the user is not asking for help, make sure they - # have given us auth. - cmd_name = None - if argv: - cmd_info = self.command_manager.find_command(argv) - cmd_factory, cmd_name, sub_argv = cmd_info - if self.interactive_mode or cmd_name != 'help': - self.authenticate_user() - - def configure_logging(self): - """Create logging handlers for any log output.""" - root_logger = logging.getLogger('') - - # Set up logging to a file - root_logger.setLevel(logging.DEBUG) - - # Send higher-level messages to the console via stderr - console = logging.StreamHandler(self.stderr) - console_level = {self.WARNING_LEVEL: logging.WARNING, - self.INFO_LEVEL: logging.INFO, - self.DEBUG_LEVEL: logging.DEBUG, - }.get(self.options.verbose_level, logging.DEBUG) - # The default log level is INFO, in this situation, set the - # log level of the console to WARNING, to avoid displaying - # useless messages. This equals using "--quiet" - if console_level == logging.INFO: - console.setLevel(logging.WARNING) - else: - console.setLevel(console_level) - if logging.DEBUG == console_level: - formatter = logging.Formatter(self.DEBUG_MESSAGE_FORMAT) - else: - formatter = logging.Formatter(self.CONSOLE_MESSAGE_FORMAT) - logging.getLogger('iso8601.iso8601').setLevel(logging.WARNING) - logging.getLogger('urllib3.connectionpool').setLevel(logging.WARNING) - console.setFormatter(formatter) - root_logger.addHandler(console) - return - - -def main(argv=sys.argv[1:]): - try: - return NeutronShell(NEUTRON_API_VERSION).run( - list(map(encodeutils.safe_decode, argv))) - except KeyboardInterrupt: - print(_("... terminating neutron client"), file=sys.stderr) - return 130 - except exc.NeutronClientException: - return 1 - except Exception as e: - print(e) - return 1 - - -if __name__ == "__main__": - sys.exit(main(sys.argv[1:])) diff --git a/neutronclient/tests/functional/adv-svcs/test_readonly_neutron_vpn.py b/neutronclient/tests/functional/adv-svcs/test_readonly_neutron_vpn.py deleted file mode 100644 index 291e59ad7..000000000 --- a/neutronclient/tests/functional/adv-svcs/test_readonly_neutron_vpn.py +++ /dev/null @@ -1,53 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from neutronclient.tests.functional import base - - -class SimpleReadOnlyNeutronVpnClientTest(base.ClientTestBase): - - """Tests for vpn based client commands that are read only - - This is a first pass at a simple read only python-neutronclient test. - This only exercises vpn based client commands that are read only. - This should test commands: - * as a regular user - * as a admin user - * with and without optional parameters - * initially just check return codes, and later test command outputs - """ - - def test_neutron_vpn_ikepolicy_list(self): - ikepolicy = self.parser.listing(self.neutron('vpn-ikepolicy-list')) - self.assertTableStruct(ikepolicy, ['id', 'name', - 'auth_algorithm', - 'encryption_algorithm', - 'ike_version', 'pfs']) - - def test_neutron_vpn_ipsecpolicy_list(self): - ipsecpolicy = self.parser.listing(self.neutron('vpn-ipsecpolicy-list')) - self.assertTableStruct(ipsecpolicy, ['id', 'name', - 'auth_algorithm', - 'encryption_algorithm', - 'pfs']) - - def test_neutron_vpn_service_list(self): - vpn_list = self.parser.listing(self.neutron('vpn-service-list')) - self.assertTableStruct(vpn_list, ['id', 'name', - 'router_id', 'status']) - - def test_neutron_ipsec_site_connection_list(self): - ipsec_site = self.parser.listing(self.neutron - ('ipsec-site-connection-list')) - self.assertTableStruct(ipsec_site, ['id', 'name', - 'peer_address', - 'auth_mode', 'status']) diff --git a/neutronclient/tests/functional/base.py b/neutronclient/tests/functional/base.py deleted file mode 100644 index 16bbe2b07..000000000 --- a/neutronclient/tests/functional/base.py +++ /dev/null @@ -1,65 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import os - -import os_client_config -from tempest.lib.cli import base - - -def credentials(cloud='devstack-admin'): - """Retrieves credentials to run functional tests - - Credentials are either read via os-client-config from the environment - or from a config file ('clouds.yaml'). Environment variables override - those from the config file. - - devstack produces a clouds.yaml with two named clouds - one named - 'devstack' which has user privs and one named 'devstack-admin' which - has admin privs. This function will default to getting the devstack-admin - cloud as that is the current expected behavior. - """ - return get_cloud_config(cloud=cloud).get_auth_args() - - -def get_cloud_config(cloud='devstack-admin'): - return os_client_config.OpenStackConfig().get_one_cloud(cloud=cloud) - - -class ClientTestBase(base.ClientTestBase): - """This is a first pass at a simple read only python-neutronclient test. - - This only exercises client commands that are read only. - This should test commands: - * as a regular user - * as an admin user - * with and without optional parameters - * initially just check return codes, and later test command outputs - - """ - - def _get_clients(self): - self.creds = credentials() - cli_dir = os.environ.get( - 'OS_NEUTRONCLIENT_EXEC_DIR', - os.path.join(os.path.abspath('.'), '.tox/functional/bin')) - - return base.CLIClient( - username=self.creds['username'], - password=self.creds['password'], - tenant_name=self.creds['project_name'], - uri=self.creds['auth_url'], - cli_dir=cli_dir) - - def neutron(self, *args, **kwargs): - return self.clients.neutron(*args, - **kwargs) diff --git a/neutronclient/tests/functional/core/test_cli_formatter.py b/neutronclient/tests/functional/core/test_cli_formatter.py deleted file mode 100644 index cd133dee1..000000000 --- a/neutronclient/tests/functional/core/test_cli_formatter.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright 2016 NEC Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from oslo_serialization import jsonutils -from oslo_utils import uuidutils -import yaml - -from neutronclient.tests.functional import base - - -class TestCLIFormatter(base.ClientTestBase): - - def setUp(self): - super(TestCLIFormatter, self).setUp() - self.net_name = 'net-%s' % uuidutils.generate_uuid() - self.addCleanup(self.neutron, 'net-delete %s' % self.net_name) - - def _create_net(self, fmt, col_attrs): - params = ['-c %s' % attr for attr in col_attrs] - params.append('-f %s' % fmt) - params.append(self.net_name) - param_string = ' '.join(params) - return self.neutron('net-create', params=param_string) - - def test_net_create_with_json_formatter(self): - result = self._create_net('json', ['name', 'admin_state_up']) - self.assertDictEqual({'name': self.net_name, - 'admin_state_up': True}, - jsonutils.loads(result)) - - def test_net_create_with_yaml_formatter(self): - result = self._create_net('yaml', ['name', 'admin_state_up']) - self.assertDictEqual({'name': self.net_name, - 'admin_state_up': True}, - yaml.load(result)) - - def test_net_create_with_value_formatter(self): - # NOTE(amotoki): In 'value' formatter, there is no guarantee - # in the order of attribute, so we use one attribute in this test. - result = self._create_net('value', ['name']) - self.assertEqual(self.net_name, result.strip()) - - def test_net_create_with_shell_formatter(self): - result = self._create_net('shell', ['name', 'admin_state_up']) - result_lines = set(result.strip().split('\n')) - self.assertSetEqual(set(['name="%s"' % self.net_name, - 'admin_state_up="True"']), - result_lines) diff --git a/neutronclient/tests/functional/core/test_clientlib.py b/neutronclient/tests/functional/core/test_clientlib.py deleted file mode 100644 index 0fd968369..000000000 --- a/neutronclient/tests/functional/core/test_clientlib.py +++ /dev/null @@ -1,121 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import uuid - -from keystoneauth1 import plugin as ksa_plugin -from keystoneauth1 import session -from tempest.lib import base -import testtools - -from neutronclient.common import exceptions -from neutronclient.tests.functional import base as func_base -from neutronclient.v2_0 import client as v2_client - -# This module tests client library functionalities with -# Keystone client. Neutron client supports two types of -# HTTP clients (HTTPClient and SessionClient), -# so it is better to test both clients. - - -class LibraryTestBase(base.BaseTestCase): - - def setUp(self): - super(LibraryTestBase, self).setUp() - self.client = self._get_client() - - -class Libv2HTTPClientTestBase(LibraryTestBase): - - def _setup_creds(self): - creds = func_base.credentials() - cloud_config = func_base.get_cloud_config() - - # We're getting a session so we can find the v2 url via KSA - keystone_auth = cloud_config.get_auth() - (verify, cert) = cloud_config.get_requests_verify_args() - - ks_session = session.Session( - auth=keystone_auth, verify=verify, cert=cert) - - # for the old HTTPClient, we use keystone v2 API, regardless of - # whether v3 also exists or is configured - v2_auth_url = keystone_auth.get_endpoint( - ks_session, interface=ksa_plugin.AUTH_INTERFACE, version=(2, 0)) - return v2_auth_url, creds - - -class Libv2HTTPClientTenantTestBase(Libv2HTTPClientTestBase): - - def _get_client(self): - v2_auth_url, creds = self._setup_creds() - return v2_client.Client(username=creds['username'], - password=creds['password'], - tenant_name=creds['project_name'], - auth_url=v2_auth_url) - - -class Libv2HTTPClientProjectTestBase(Libv2HTTPClientTestBase): - - def _get_client(self): - v2_auth_url, creds = self._setup_creds() - return v2_client.Client(username=creds['username'], - password=creds['password'], - project_name=creds['project_name'], - auth_url=v2_auth_url) - - -class Libv2SessionClientTestBase(LibraryTestBase): - - def _get_client(self): - cloud_config = func_base.get_cloud_config() - keystone_auth = cloud_config.get_auth() - (verify, cert) = cloud_config.get_requests_verify_args() - - ks_session = session.Session( - auth=keystone_auth, - verify=verify, - cert=cert) - return v2_client.Client(session=ks_session) - - -class LibraryTestCase(object): - - def test_list_network(self): - nets = self.client.list_networks() - self.assertIsInstance(nets['networks'], list) - - def test_post_put_delele_network(self): - name = str(uuid.uuid4()) - net = self.client.create_network({'network': {'name': name}}) - net_id = net['network']['id'] - self.assertEqual(name, net['network']['name']) - name2 = str(uuid.uuid4()) - net = self.client.update_network(net_id, {'network': {'name': name2}}) - self.assertEqual(name2, net['network']['name']) - self.client.delete_network(net_id) - with testtools.ExpectedException(exceptions.NetworkNotFoundClient): - self.client.show_network(net_id) - - -class LibraryHTTPClientTenantTest(LibraryTestCase, - Libv2HTTPClientTenantTestBase): - pass - - -class LibraryHTTPClientProjectTest(LibraryTestCase, - Libv2HTTPClientProjectTestBase): - pass - - -class LibrarySessionClientTest(LibraryTestCase, Libv2SessionClientTestBase): - pass diff --git a/neutronclient/tests/functional/core/test_purge.py b/neutronclient/tests/functional/core/test_purge.py deleted file mode 100644 index 33b8af44c..000000000 --- a/neutronclient/tests/functional/core/test_purge.py +++ /dev/null @@ -1,174 +0,0 @@ -# Copyright 2016 Cisco Systems -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from neutronclient.tests.functional import base - -from tempest.lib import exceptions - - -class PurgeNeutronClientCLITest(base.ClientTestBase): - - def _safe_cleanup(self, delete_command): - try: - self.neutron(delete_command) - except exceptions.CommandFailed: - # This resource was already purged successfully - pass - - def _create_subnet(self, name, tenant_id, cidr): - params = ('%(name)s --name %(name)s --tenant-id %(tenant)s ' - '%(cidr)s' % {'name': name, - 'tenant': tenant_id, - 'cidr': cidr}) - subnet = self.parser.listing(self.neutron('subnet-create', - params=params)) - for row in subnet: - if row['Field'] == 'id': - return row['Value'] - - def _create_router(self, name, tenant_id): - params = ('%(name)s --tenant_id %(tenant)s' % {'name': name, - 'tenant': tenant_id}) - router = self.parser.listing(self.neutron('router-create', - params=params)) - for row in router: - if row['Field'] == 'id': - return row['Value'] - - def _create_floatingip(self, network, tenant_id): - params = ('%(network)s --tenant-id %(tenant)s' % - {'network': network, 'tenant': tenant_id}) - floatingip = self.parser.listing(self.neutron('floatingip-create', - params=params)) - for row in floatingip: - if row['Field'] == 'id': - return row['Value'] - - def _create_resources(self, name, tenant_id, shared_tenant_id=None): - # If no shared_tenant_id is provided, create the resources for the - # current tenant to test that they will be deleted when not in use. - if not shared_tenant_id: - shared_tenant_id = tenant_id - - self.neutron('net-create', - params=('%(name)s --router:external True ' - '--tenant-id %(tenant)s' % {'name': name, - 'tenant': tenant_id})) - self.addCleanup(self._safe_cleanup, 'net-delete %s' % name) - - self.neutron('net-create', - params=('%(name)s-shared --shared ' - '--tenant-id %(tenant)s' % - {'name': name, 'tenant': shared_tenant_id})) - self.addCleanup(self._safe_cleanup, - 'net-delete %s-shared' % name) - - subnet = self._create_subnet(name, tenant_id, '192.168.71.0/24') - self.addCleanup(self._safe_cleanup, 'subnet-delete %s' % name) - - subnet = self._create_subnet('%s-shared' % name, tenant_id, - '192.168.81.0/24') - self.addCleanup(self._safe_cleanup, 'subnet-delete %s-shared' % name) - - router = self._create_router(name, tenant_id) - self.addCleanup(self._safe_cleanup, 'router-delete %s' % name) - - self.neutron('router-interface-add', - params=('%(router)s %(subnet)s ' - '--tenant-id %(tenant)s' % {'router': router, - 'subnet': subnet, - 'tenant': tenant_id})) - - self.neutron('port-create', - params=('%(name)s --name %(name)s ' - '--tenant-id %(tenant)s' % {'name': name, - 'tenant': tenant_id})) - self.addCleanup(self._safe_cleanup, 'port-delete %s' % name) - - self.neutron('port-create', - params=('%(name)s-shared --name %(name)s-shared ' - '--tenant-id %(tenant)s' % {'name': name, - 'tenant': tenant_id})) - self.addCleanup(self._safe_cleanup, 'port-delete %s-shared' % name) - - self.neutron('security-group-create', - params=('%(name)s --tenant-id %(tenant)s' % - {'name': name, 'tenant': tenant_id})) - self.addCleanup(self._safe_cleanup, 'security-group-delete %s' % name) - - floatingip = self._create_floatingip(name, tenant_id) - self.addCleanup(self._safe_cleanup, ('floatingip-delete ' - '%s' % floatingip)) - return floatingip - - def _verify_deletion(self, resources, resource_type): - purged = True - no_purge_purged = True - router_interface_owners = ['network:router_interface', - 'network:router_interface_distributed'] - for row in resources: - if resource_type == 'port' and row.get('id', None): - port = self.parser.listing(self.neutron('port-show', - params=row['id'])) - port_dict = {} - for row in port: - port_dict[row['Field']] = row['Value'] - if port_dict['device_owner'] in router_interface_owners: - if port_dict['tenant_id'] == 'purge-tenant': - purged = False - elif port_dict['tenant_id'] == 'no-purge-tenant': - no_purge_purged = False - if not purged or not no_purge_purged: - self.addCleanup(self.neutron, - ('router-interface-delete %(router)s ' - 'port=%(port)s' % - {'router': port_dict['device_id'], - 'port': port_dict['id']})) - if (row.get('name') == 'purge-me' or - row.get('id') == self.purge_floatingip): - purged = False - elif ('no-purge' in row.get('name', '') or - row.get('id') == self.no_purge_floatingip): - no_purge_purged = False - - if not purged: - self.fail('%s not deleted by neutron purge' % resource_type) - - if no_purge_purged: - self.fail('%s owned by another tenant incorrectly deleted ' - 'by neutron purge' % resource_type) - - def test_purge(self): - self.purge_floatingip = self._create_resources('purge-me', - 'purge-tenant') - self.no_purge_floatingip = self._create_resources('no-purge', - 'no-purge-tenant', - 'purge-tenant') - - purge_output = self.neutron('purge', params='purge-tenant').strip() - if not purge_output: - self.fail('Purge command did not return feedback') - - networks = self.parser.listing(self.neutron('net-list')) - subnets = self.parser.listing(self.neutron('subnet-list')) - routers = self.parser.listing(self.neutron('router-list')) - ports = self.parser.listing(self.neutron('port-list')) - floatingips = self.parser.listing(self.neutron('floatingip-list')) - - self._verify_deletion(networks, 'network') - self._verify_deletion(subnets, 'subnet') - self._verify_deletion(ports, 'port') - self._verify_deletion(routers, 'router') - self._verify_deletion(floatingips, 'floatingip') diff --git a/neutronclient/tests/functional/core/test_readonly_neutron.py b/neutronclient/tests/functional/core/test_readonly_neutron.py deleted file mode 100644 index a9de5bd0e..000000000 --- a/neutronclient/tests/functional/core/test_readonly_neutron.py +++ /dev/null @@ -1,170 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import re - -from tempest.lib import exceptions - -from neutronclient.tests.functional import base - - -class SimpleReadOnlyNeutronClientTest(base.ClientTestBase): - - """This is a first pass at a simple read only python-neutronclient test. - - This only exercises client commands that are read only. - This should test commands: - * as a regular user - * as a admin user - * with and without optional parameters - * initially just check return codes, and later test command outputs - """ - - def test_admin_fake_action(self): - self.assertRaises(exceptions.CommandFailed, - self.neutron, - 'this-does-neutron-exist') - - # NOTE(mestery): Commands in order listed in 'neutron help' - - # Optional arguments: - - def test_neutron_fake_action(self): - self.assertRaises(exceptions.CommandFailed, - self.neutron, - 'this-does-not-exist') - - def test_neutron_net_list(self): - net_list = self.parser.listing(self.neutron('net-list')) - self.assertTableStruct(net_list, ['id', 'name', 'subnets']) - - def test_neutron_ext_list(self): - ext = self.parser.listing(self.neutron('ext-list')) - self.assertTableStruct(ext, ['alias', 'name']) - - def test_neutron_dhcp_agent_list_hosting_net(self): - self.neutron('dhcp-agent-list-hosting-net', - params='private') - - def test_neutron_agent_list(self): - agents = self.parser.listing(self.neutron('agent-list')) - field_names = ['id', 'agent_type', 'host', 'alive', 'admin_state_up'] - self.assertTableStruct(agents, field_names) - - def test_neutron_floatingip_list(self): - self.neutron('floatingip-list') - - def test_neutron_meter_label_list(self): - self.neutron('meter-label-list') - - def test_neutron_meter_label_rule_list(self): - self.neutron('meter-label-rule-list') - - def _test_neutron_lbaas_command(self, command): - try: - self.neutron(command) - except exceptions.CommandFailed as e: - if '404 Not Found' not in e.stderr: - self.fail('%s: Unexpected failure.' % command) - - def test_neutron_lb_healthmonitor_list(self): - self._test_neutron_lbaas_command('lb-healthmonitor-list') - - def test_neutron_lb_member_list(self): - self._test_neutron_lbaas_command('lb-member-list') - - def test_neutron_lb_pool_list(self): - self._test_neutron_lbaas_command('lb-pool-list') - - def test_neutron_lb_vip_list(self): - self._test_neutron_lbaas_command('lb-vip-list') - - def test_neutron_net_external_list(self): - net_ext_list = self.parser.listing(self.neutron('net-external-list')) - self.assertTableStruct(net_ext_list, ['id', 'name', 'subnets']) - - def test_neutron_port_list(self): - port_list = self.parser.listing(self.neutron('port-list')) - self.assertTableStruct(port_list, ['id', 'name', 'mac_address', - 'fixed_ips']) - - def test_neutron_quota_list(self): - self.neutron('quota-list') - - def test_neutron_router_list(self): - router_list = self.parser.listing(self.neutron('router-list')) - self.assertTableStruct(router_list, ['id', 'name', - 'external_gateway_info']) - - def test_neutron_security_group_list(self): - security_grp = self.parser.listing(self.neutron('security-group-list')) - self.assertTableStruct(security_grp, ['id', 'name', - 'security_group_rules']) - - def test_neutron_security_group_rule_list(self): - security_grp = self.parser.listing(self.neutron - ('security-group-rule-list')) - self.assertTableStruct(security_grp, ['id', 'security_group', - 'direction', 'ethertype', - 'port/protocol', 'remote']) - - def test_neutron_subnet_list(self): - subnet_list = self.parser.listing(self.neutron('subnet-list')) - self.assertTableStruct(subnet_list, ['id', 'name', 'cidr', - 'allocation_pools']) - - def test_neutron_firewall_list(self): - firewall_list = self.parser.listing(self.neutron - ('firewall-list')) - self.assertTableStruct(firewall_list, ['id', 'name', - 'firewall_policy_id']) - - def test_neutron_firewall_policy_list(self): - firewall_policy = self.parser.listing(self.neutron - ('firewall-policy-list')) - self.assertTableStruct(firewall_policy, ['id', 'name', - 'firewall_rules']) - - def test_neutron_firewall_rule_list(self): - firewall_rule = self.parser.listing(self.neutron - ('firewall-rule-list')) - self.assertTableStruct(firewall_rule, ['id', 'name', - 'firewall_policy_id', - 'summary', 'enabled']) - - def test_neutron_help(self): - help_text = self.neutron('help') - lines = help_text.split('\n') - self.assertFirstLineStartsWith(lines, 'usage: neutron') - - commands = [] - cmds_start = lines.index('Commands for API v2.0:') - command_pattern = re.compile('^ {2}([a-z0-9\-\_]+)') - for line in lines[cmds_start:]: - match = command_pattern.match(line) - if match: - commands.append(match.group(1)) - commands = set(commands) - wanted_commands = set(('net-create', 'subnet-list', 'port-delete', - 'router-show', 'agent-update', 'help')) - self.assertFalse(wanted_commands - commands) - - # Optional arguments: - - def test_neutron_version(self): - self.neutron('', flags='--version') - - def test_neutron_debug_net_list(self): - self.neutron('net-list', flags='--debug') - - def test_neutron_quiet_net_list(self): - self.neutron('net-list', flags='--quiet') diff --git a/neutronclient/tests/functional/core/test_subnet_create.py b/neutronclient/tests/functional/core/test_subnet_create.py deleted file mode 100644 index ed7f390b8..000000000 --- a/neutronclient/tests/functional/core/test_subnet_create.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright 2015 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. - -from neutronclient.tests.functional import base - - -class SubnetCreateNeutronClientCLITest(base.ClientTestBase): - - def test_create_subnet_net_name_first(self): - self.neutron('net-create', params='netwrk-1') - self.addCleanup(self.neutron, 'net-delete netwrk-1') - self.neutron('subnet-create netwrk-1', - params='--name fake --gateway 192.168.51.1 ' - '192.168.51.0/24') - self.addCleanup(self.neutron, 'subnet-delete fake') - subnet_list = self.parser.listing(self.neutron('subnet-list')) - self.assertTableStruct(subnet_list, ['id', 'name', 'cidr', - 'allocation_pools']) - found = False - for row in subnet_list: - if row.get('name') == 'fake': - found = True - break - if not found: - self.fail('Created subnet not found in list') diff --git a/neutronclient/tests/functional/hooks/fwaas b/neutronclient/tests/functional/hooks/fwaas deleted file mode 100644 index 0f04168ad..000000000 --- a/neutronclient/tests/functional/hooks/fwaas +++ /dev/null @@ -1,2 +0,0 @@ -enable_plugin neutron-fwaas git://git.openstack.org/openstack/neutron-fwaas -enable_service q-fwaas \ No newline at end of file diff --git a/neutronclient/tests/functional/hooks/gate_hook.sh b/neutronclient/tests/functional/hooks/gate_hook.sh deleted file mode 100755 index 9ad05aabf..000000000 --- a/neutronclient/tests/functional/hooks/gate_hook.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env bash - -set -ex - -VENV=${1:-"functional"} - -GATE_DEST=$BASE/new -NEUTRONCLIENT_PATH=$GATE_DEST/python-neutronclient -GATE_HOOKS=$NEUTRONCLIENT_PATH/neutronclient/tests/functional/hooks -DEVSTACK_PATH=$GATE_DEST/devstack - -# Inject config from hook into localrc -function load_rc_hook { - local hook="$1" - config=$(cat $GATE_HOOKS/$hook) - export DEVSTACK_LOCAL_CONFIG+=" -# generated from hook '$hook' -${config} -" -} - -if [ "$VENV" == "functional" ] -then - load_rc_hook fwaas -fi - -if [ "$VENV" == "functional-adv-svcs" ] -then - export DEVSTACK_LOCAL_CONFIG="enable_plugin neutron-vpnaas git://git.openstack.org/openstack/neutron-vpnaas" -fi - -$BASE/new/devstack-gate/devstack-vm-gate.sh diff --git a/neutronclient/tests/functional/hooks/post_test_hook.sh b/neutronclient/tests/functional/hooks/post_test_hook.sh deleted file mode 100755 index 5fb66a109..000000000 --- a/neutronclient/tests/functional/hooks/post_test_hook.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/bin/bash -xe - -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -# This script is executed inside post_test_hook function in devstack gate. - -SCRIPTS_DIR="/usr/os-testr-env/bin/" - -function generate_test_logs { - local path="$1" - # Compress all $path/*.txt files and move the directories holding those - # files to /opt/stack/logs. Files with .log suffix have their - # suffix changed to .txt (so browsers will know to open the compressed - # files and not download them). - if [ -d "$path" ] - then - sudo find $path -iname "*.log" -type f -exec mv {} {}.txt \; -exec gzip -9 {}.txt \; - sudo mv $path/* /opt/stack/logs/ - fi -} - -function generate_testr_results { - # Give job user rights to access tox logs - sudo -H -u $owner chmod o+rw . - sudo -H -u $owner chmod o+rw -R .testrepository - if [ -f ".testrepository/0" ] ; then - .tox/$VENV/bin/subunit-1to2 < .testrepository/0 > ./testrepository.subunit - $SCRIPTS_DIR/subunit2html ./testrepository.subunit testr_results.html - gzip -9 ./testrepository.subunit - gzip -9 ./testr_results.html - sudo mv ./*.gz /opt/stack/logs/ - fi - - if [ "$venv" == "functional" ] || [ "$venv" == "functional-adv-svcs" ] - then - generate_test_logs "/tmp/${venv}-logs" - fi -} - -export NEUTRONCLIENT_DIR="$BASE/new/python-neutronclient" -owner=jenkins - -sudo chown -R $owner:stack $NEUTRONCLIENT_DIR - -# Go to the neutronclient dir -cd $NEUTRONCLIENT_DIR - -# Run tests -VENV=${1:-"functional"} -echo "Running neutronclient functional test suite" -set +e -# Preserve env for OS_ credentials -sudo -E -H -u $owner tox -e $VENV -EXIT_CODE=$? -set -e - -# Collect and parse result -generate_testr_results -exit $EXIT_CODE diff --git a/neutronclient/tests/unit/bgp/test_cli20_dragentscheduler.py b/neutronclient/tests/unit/bgp/test_cli20_dragentscheduler.py deleted file mode 100644 index cbe85d92f..000000000 --- a/neutronclient/tests/unit/bgp/test_cli20_dragentscheduler.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright 2016 Huawei Technologies India Pvt. Ltd. -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import sys - -from neutronclient.neutron.v2_0.bgp import dragentscheduler as bgp_drsched -from neutronclient.tests.unit import test_cli20 -from neutronclient.tests.unit import test_cli20_agentschedulers as test_as - - -BGP_DRAGENT_ID = 'bgp_dragent_id1' -BGP_SPEAKER = 'bgp_speaker_id1' - - -class CLITestV20DRAgentScheduler(test_as.CLITestV20AgentScheduler): - - def test_add_bgp_speaker_to_dragent(self): - resource = 'agent' - cmd = bgp_drsched.AddBGPSpeakerToDRAgent( - test_cli20.MyApp(sys.stdout), None) - args = (BGP_DRAGENT_ID, BGP_SPEAKER) - body = {'bgp_speaker_id': BGP_SPEAKER} - result = {'bgp_speaker_id': 'bgp_speaker_id', } - self._test_add_to_agent(resource, cmd, args, - self.client.BGP_DRINSTANCES, - body, result) - - def test_remove_bgp_speaker_from_dragent(self): - resource = 'agent' - cmd = bgp_drsched.RemoveBGPSpeakerFromDRAgent( - test_cli20.MyApp(sys.stdout), None) - args = (BGP_DRAGENT_ID, BGP_SPEAKER) - self._test_remove_from_agent(resource, cmd, args, - self.client.BGP_DRINSTANCES) - - def test_list_bgp_speakers_on_dragent(self): - resources = 'bgp_speakers' - cmd = bgp_drsched.ListBGPSpeakersOnDRAgent( - test_cli20.MyApp(sys.stdout), None) - path = ((self.client.agent_path + self.client.BGP_DRINSTANCES) % - BGP_DRAGENT_ID) - self._test_list_resources(resources, cmd, base_args=[BGP_DRAGENT_ID], - path=path) - - def test_list_dragents_hosting_bgp_speaker(self): - resources = 'agent' - cmd = bgp_drsched.ListDRAgentsHostingBGPSpeaker( - test_cli20.MyApp(sys.stdout), None) - path = ((self.client.bgp_speaker_path + self.client.BGP_DRAGENTS) % - BGP_DRAGENT_ID) - contents = {self.id_field: 'myid1', 'alive': True} - self._test_list_resources(resources, cmd, base_args=[BGP_DRAGENT_ID], - path=path, response_contents=contents) diff --git a/neutronclient/tests/unit/bgp/test_cli20_peer.py b/neutronclient/tests/unit/bgp/test_cli20_peer.py deleted file mode 100644 index b16532704..000000000 --- a/neutronclient/tests/unit/bgp/test_cli20_peer.py +++ /dev/null @@ -1,223 +0,0 @@ -# Copyright 2016 Huawei Technologies India Pvt. Ltd. -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import sys - -from neutronclient.common import exceptions -from neutronclient.neutron.v2_0.bgp import peer as bgp_peer -from neutronclient.neutron.v2_0.bgp import speaker as bgp_speaker -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20BGPPeerJSON(test_cli20.CLITestV20Base): - - non_admin_status_resources = ['bgp_peer'] - - def test_create_bgp_peer_with_mandatory_params(self): - # Create BGP peer with mandatory params. - resource = 'bgp_peer' - cmd = bgp_peer.CreatePeer(test_cli20.MyApp(sys.stdout), - None) - name = 'my-name' - my_id = 'my-id' - peerip = '1.1.1.1' - remote_asnum = '1' - args = [name, - '--peer-ip', peerip, - '--remote-as', remote_asnum, ] - position_names = ['name', 'peer_ip', 'remote_as', - 'auth_type'] - position_values = [name, peerip, remote_asnum, 'none'] - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values) - - def test_create_bgp_peer_with_all_params(self): - # Create BGP peer with all params. - resource = 'bgp_peer' - cmd = bgp_peer.CreatePeer(test_cli20.MyApp(sys.stdout), - None) - name = 'my-name' - my_id = 'my-id' - peerip = '1.1.1.1' - remote_asnum = '65535' - authType = 'md5' - password = 'abc' - args = [name, - '--peer-ip', peerip, - '--remote-as', remote_asnum, - '--auth-type', authType, - '--password', password] - position_names = ['name', 'peer_ip', 'remote_as', - 'auth_type', 'password'] - position_values = [name, peerip, remote_asnum, authType, password] - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values) - - def test_create_bgp_peer_with_invalid_min_remote_asnum(self): - # Create BGP peer with invalid minimum remote-asnum. - resource = 'bgp_peer' - cmd = bgp_peer.CreatePeer(test_cli20.MyApp(sys.stdout), - None) - name = 'my-name' - my_id = 'my-id' - peerip = '1.1.1.1' - remote_asnum = '0' - args = [name, - '--peer-ip', peerip, - '--remote-as', remote_asnum, ] - position_names = ['name', 'peer_ip', 'remote_as', ] - position_values = [name, peerip, remote_asnum, ] - exc = self.assertRaises(exceptions.CommandError, - self._test_create_resource, - resource, cmd, name, my_id, args, - position_names, position_values) - self.assertEqual('remote-as "0" should be an integer [%s:%s].' % - (bgp_speaker.MIN_AS_NUM, bgp_speaker.MAX_AS_NUM), - str(exc)) - - def test_create_bgp_peer_with_invalid_max_remote_asnum(self): - # Create BGP peer with invalid maximum remote-asnum. - resource = 'bgp_peer' - cmd = bgp_peer.CreatePeer(test_cli20.MyApp(sys.stdout), - None) - name = 'my-name' - my_id = 'my-id' - peerip = '1.1.1.1' - remote_asnum = '65536' - args = [name, - '--peer-ip', peerip, - '--remote-as', remote_asnum, ] - position_names = ['name', 'peer_ip', 'remote_as', - 'auth_type', 'password'] - position_values = [name, peerip, remote_asnum, 'none', ''] - exc = self.assertRaises(exceptions.CommandError, - self._test_create_resource, - resource, cmd, name, my_id, args, - position_names, position_values) - self.assertEqual('remote-as "65536" should be an integer [%s:%s].' % - (bgp_speaker.MIN_AS_NUM, bgp_speaker.MAX_AS_NUM), - str(exc)) - - def test_create_authenticated_bgp_peer_without_authtype(self): - # Create authenticated BGP peer without auth-type. - resource = 'bgp_peer' - cmd = bgp_peer.CreatePeer(test_cli20.MyApp(sys.stdout), - None) - name = 'my-name' - my_id = 'my-id' - peerip = '1.1.1.1' - remote_asnum = '2048' - password = 'abc' - args = [name, - '--peer-ip', peerip, - '--remote-as', remote_asnum, - '--password', password] - position_names = ['name', 'peer_ip', 'remote_as', 'password'] - position_values = [name, peerip, remote_asnum, password] - exc = self.assertRaises(exceptions.CommandError, - self._test_create_resource, - resource, cmd, name, my_id, args, - position_names, position_values) - self.assertEqual('Must provide auth-type if password is specified.', - str(exc)) - - def test_create_authenticated_bgp_peer_without_password(self): - # Create authenticated BGP peer without password. - resource = 'bgp_peer' - cmd = bgp_peer.CreatePeer(test_cli20.MyApp(sys.stdout), - None) - name = 'my-name' - my_id = 'my-id' - peerip = '1.1.1.1' - remote_asnum = '2048' - authType = 'md5' - args = [name, - '--peer-ip', peerip, - '--remote-as', remote_asnum, - '--auth-type', authType] - position_names = ['name', 'peer_ip', 'remote_as', 'auth-type'] - position_values = [name, peerip, remote_asnum, authType] - exc = self.assertRaises(exceptions.CommandError, - self._test_create_resource, - resource, cmd, name, my_id, args, - position_names, position_values) - self.assertEqual('Must provide password if auth-type is specified.', - str(exc)) - - def test_update_bgp_peer(self): - # Update BGP peer: - # myid --advertise-tenant-networks True - # --advertise-floating-ip-host-routes False - resource = 'bgp_peer' - cmd = bgp_peer.UpdatePeer(test_cli20.MyApp(sys.stdout), - None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'new-name', - '--password', 'abc'], - {'name': 'new-name', 'password': 'abc'}) - - def test_update_bgp_peer_exception(self): - # Update BGP peer: myid. - resource = 'bgp_peer' - cmd = bgp_peer.UpdatePeer(test_cli20.MyApp(sys.stdout), - None) - self.assertRaises(exceptions.CommandError, - self._test_update_resource, - resource, cmd, 'myid', ['myid'], {}) - - def test_list_bgp_peer(self): - # List all BGP peers. - resources = "bgp_peers" - cmd = bgp_peer.ListPeers(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, True) - - # TODO(Vikram): Add test_list_bgp_peer_pagination - - def test_list_bgp_peer_sort(self): - # sorted list: bgp-peer-list --sort-key name --sort-key id - # --sort-key asc --sort-key desc - resources = "bgp_peers" - cmd = bgp_peer.ListPeers(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_bgp_peer_limit(self): - # size (1000) limited list: bgp-peer-list -P. - resources = "bgp_peers" - cmd = bgp_peer.ListPeers(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_show_bgp_peer(self): - # Show BGP peer: --fields id --fields name myid. - resource = 'bgp_peer' - cmd = bgp_peer.ShowPeer(test_cli20.MyApp(sys.stdout), - None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, - ['id', 'name']) - - def test_delete_bgp_peer(self): - # Delete BGP peer: bgp_peer_id. - resource = 'bgp_peer' - cmd = bgp_peer.DeletePeer(test_cli20.MyApp(sys.stdout), - None) - myid = 'myid' - args = [myid] - self._test_delete_resource(resource, cmd, myid, args) diff --git a/neutronclient/tests/unit/bgp/test_cli20_speaker.py b/neutronclient/tests/unit/bgp/test_cli20_speaker.py deleted file mode 100644 index 63ce5fc82..000000000 --- a/neutronclient/tests/unit/bgp/test_cli20_speaker.py +++ /dev/null @@ -1,267 +0,0 @@ -# Copyright 2016 Huawei Technologies India Pvt. Ltd. -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import sys - -from mox3 import mox - -from neutronclient.common import exceptions -from neutronclient.neutron.v2_0.bgp import speaker as bgp_speaker -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20BGPSpeakerJSON(test_cli20.CLITestV20Base): - - non_admin_status_resources = ['bgp_speaker'] - - def test_create_bgp_speaker_with_minimal_options(self): - # Create BGP Speaker with mandatory params. - resource = 'bgp_speaker' - cmd = bgp_speaker.CreateSpeaker(test_cli20.MyApp(sys.stdout), - None) - name = 'my-name' - my_id = 'my-id' - local_asnum = '1' - args = [name, '--local-as', local_asnum, ] - position_names = ['name', 'local_as', 'ip_version'] - position_values = [name, local_asnum, 4] - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values) - - def test_create_ipv4_bgp_speaker_with_all_params(self): - # Create BGP Speaker with all params. - resource = 'bgp_speaker' - cmd = bgp_speaker.CreateSpeaker(test_cli20.MyApp(sys.stdout), - None) - name = 'my-name' - my_id = 'my-id' - local_asnum = '1' - args = [name, - '--local-as', local_asnum, - '--ip-version', '4', - '--advertise-floating-ip-host-routes', 'True', - '--advertise-tenant-networks', 'True'] - position_names = ['name', 'local_as', 'ip_version', - 'advertise_floating_ip_host_routes', - 'advertise_tenant_networks'] - position_values = [name, local_asnum, 4, 'True', 'True'] - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values) - - def test_create_ipv6_bgp_speaker_with_all_params(self): - # Create BGP Speaker with all params. - resource = 'bgp_speaker' - cmd = bgp_speaker.CreateSpeaker(test_cli20.MyApp(sys.stdout), - None) - name = 'my-name' - my_id = 'my-id' - local_asnum = '65535' - args = [name, - '--local-as', local_asnum, - '--ip-version', '6', - '--advertise-floating-ip-host-routes', 'True', - '--advertise-tenant-networks', 'True'] - position_names = ['name', 'local_as', 'ip_version', - 'advertise_floating_ip_host_routes', - 'advertise_tenant_networks'] - position_values = [name, local_asnum, 6, 'True', 'True'] - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values) - - def test_create_bgp_speaker_with_invalid_min_local_asnum(self): - # Create BGP Speaker with invalid minimum local-asnum. - resource = 'bgp_speaker' - cmd = bgp_speaker.CreateSpeaker(test_cli20.MyApp(sys.stdout), - None) - name = 'my-name' - my_id = 'my-id' - local_asnum = '0' - args = [name, - '--local-as', local_asnum] - position_names = ['name', 'local_as'] - position_values = [name, local_asnum] - exc = self.assertRaises(exceptions.CommandError, - self._test_create_resource, - resource, cmd, name, my_id, args, - position_names, position_values) - self.assertEqual('local-as "0" should be an integer [%s:%s].' % - (bgp_speaker.MIN_AS_NUM, bgp_speaker.MAX_AS_NUM), - str(exc)) - - def test_create_bgp_speaker_with_invalid_max_local_asnum(self): - # Create BGP Speaker with invalid maximum local-asnum. - resource = 'bgp_speaker' - cmd = bgp_speaker.CreateSpeaker(test_cli20.MyApp(sys.stdout), - None) - name = 'my-name' - my_id = 'my-id' - local_asnum = '65536' - args = [name, - '--local-as', local_asnum] - position_names = ['name', 'local_as', ] - position_values = [name, local_asnum, ] - exc = self.assertRaises(exceptions.CommandError, - self._test_create_resource, - resource, cmd, name, my_id, args, - position_names, position_values) - self.assertEqual('local-as "65536" should be an integer [%s:%s].' % - (bgp_speaker.MIN_AS_NUM, bgp_speaker.MAX_AS_NUM), - str(exc)) - - def test_update_bgp_speaker(self): - # Update BGP Speaker: - # myid --advertise-tenant-networks True - # --advertise-floating-ip-host-routes False - resource = 'bgp_speaker' - cmd = bgp_speaker.UpdateSpeaker(test_cli20.MyApp(sys.stdout), - None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', - '--name', 'new-name', - '--advertise-tenant-networks', 'True', - '--advertise-floating-ip-host-routes', - 'False'], - {'name': 'new-name', - 'advertise_tenant_networks': 'True', - 'advertise_floating_ip_host_routes': - 'False'}) - - def test_update_bgp_speaker_exception(self): - # Update BGP Speaker: myid. - resource = 'bgp_speaker' - cmd = bgp_speaker.UpdateSpeaker(test_cli20.MyApp(sys.stdout), - None) - self.assertRaises(exceptions.CommandError, - self._test_update_resource, - resource, cmd, 'myid', ['myid'], {}) - - def test_list_bgp_speaker(self): - # List all BGP Speakers. - resources = "bgp_speakers" - cmd = bgp_speaker.ListSpeakers(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, True) - - def test_list_bgp_speaker_pagination(self): - # List all BGP Speakers with pagination support. - cmd = bgp_speaker.ListSpeakers(test_cli20.MyApp(sys.stdout), - None) - self.mox.StubOutWithMock(bgp_speaker.ListSpeakers, - "extend_list") - bgp_speaker.ListSpeakers.extend_list(mox.IsA(list), - mox.IgnoreArg()) - self._test_list_resources_with_pagination("bgp_speakers", - cmd) - self.mox.VerifyAll() - self.mox.UnsetStubs() - - def test_list_bgp_speaker_sort(self): - # sorted list: bgp-speaker-list --sort-key name --sort-key id - # --sort-key asc --sort-key desc - resources = "bgp_speakers" - cmd = bgp_speaker.ListSpeakers(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_bgp_speaker_limit(self): - # size (1000) limited list: bgp-speaker-list -P. - resources = "bgp_speakers" - cmd = bgp_speaker.ListSpeakers(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_show_bgp_speaker(self): - # Show BGP Speaker: --fields id --fields name myid. - resource = 'bgp_speaker' - cmd = bgp_speaker.ShowSpeaker(test_cli20.MyApp(sys.stdout), - None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, - ['id', 'name']) - - def test_delete_bgp_speaker(self): - # Delete BGP Speaker: bgp_speaker_id. - resource = 'bgp_speaker' - cmd = bgp_speaker.DeleteSpeaker(test_cli20.MyApp(sys.stdout), - None) - myid = 'myid' - args = [myid] - self._test_delete_resource(resource, cmd, myid, args) - - def _test_add_remove_peer(self, action, cmd, args): - """Add or Remove BGP Peer to/from a BGP Speaker.""" - resource = 'bgp_speaker' - subcmd = '%s_bgp_peer' % action - body = {'bgp_peer_id': 'peerid'} - if action == 'add': - retval = {'bgp_peer': 'peerid'} - else: - retval = None - self._test_update_resource_action(resource, cmd, 'myid', - subcmd, args, body, retval) - - def test_add_peer_to_bgp_speaker(self): - # Add peer to BGP speaker: myid peer_id=peerid - cmd = bgp_speaker.AddPeerToSpeaker(test_cli20.MyApp(sys.stdout), - None) - args = ['myid', 'peerid'] - self._test_add_remove_peer('add', cmd, args) - - def test_remove_peer_from_bgp_speaker(self): - # Remove peer from BGP speaker: myid peer_id=peerid - cmd = bgp_speaker.RemovePeerFromSpeaker(test_cli20.MyApp(sys.stdout), - None) - args = ['myid', 'peerid'] - self._test_add_remove_peer('remove', cmd, args) - - def _test_add_remove_network(self, action, cmd, args): - # Add or Remove network to/from a BGP Speaker. - resource = 'bgp_speaker' - subcmd = '%s_gateway_network' % action - body = {'network_id': 'netid'} - if action == 'add': - retval = {'network': 'netid'} - else: - retval = None - self._test_update_resource_action(resource, cmd, 'myid', - subcmd, args, body, retval) - - def test_add_network_to_bgp_speaker(self): - # Add peer to BGP speaker: myid network_id=netid - cmd = bgp_speaker.AddNetworkToSpeaker(test_cli20.MyApp(sys.stdout), - None) - args = ['myid', 'netid'] - self._test_add_remove_network('add', cmd, args) - - def test_remove_network_from_bgp_speaker(self): - # Remove network from BGP speaker: myid network_id=netid - cmd = bgp_speaker.RemoveNetworkFromSpeaker( - test_cli20.MyApp(sys.stdout), None) - args = ['myid', 'netid'] - self._test_add_remove_network('remove', cmd, args) - - def test_list_routes_advertised_by_a_bgp_speaker(self): - # Retrieve advertised route list - resources = 'advertised_routes' - cmd = bgp_speaker.ListRoutesAdvertisedBySpeaker( - test_cli20.MyApp(sys.stdout), None) - bs_id = 'bgp_speaker_id1' - path = ((self.client.bgp_speaker_path + '/get_advertised_routes') % - bs_id) - self._test_list_resources(resources, cmd, base_args=[bs_id], - path=path) diff --git a/neutronclient/tests/unit/flavor/test_cli20_flavor.py b/neutronclient/tests/unit/flavor/test_cli20_flavor.py deleted file mode 100644 index 22bd8bb9b..000000000 --- a/neutronclient/tests/unit/flavor/test_cli20_flavor.py +++ /dev/null @@ -1,154 +0,0 @@ -# Copyright 2015 Hewlett-Packard Development Company, L.P. -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import sys - -from neutronclient.neutron.v2_0.flavor import flavor -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20FlavorJSON(test_cli20.CLITestV20Base): - - def setUp(self): - """Prepare test environment.""" - super(CLITestV20FlavorJSON, self).setUp(plurals={'flavors': 'flavor'}) - self.register_non_admin_status_resource('flavor') - self.register_non_admin_status_resource('service_profile') - - def test_create_flavor_with_missing_params(self): - """Create test flavor with missing parameters.""" - resource = 'flavor' - cmd = flavor.CreateFlavor( - test_cli20.MyApp(sys.stdout), None) - name = 'Test flavor' - myid = 'myid' - position_names = [] - position_values = [] - args = [] - self.assertRaises( - SystemExit, self._test_create_resource, - resource, cmd, name, myid, args, position_names, position_values) - - def test_create_flavor_with_mandatory_params(self): - """Create test flavor with minimal parameters.""" - resource = 'flavor' - cmd = flavor.CreateFlavor( - test_cli20.MyApp(sys.stdout), None) - name = 'Test flavor' - myid = 'myid' - service_type = 'DUMMY' - # Defaults are returned in body - position_names = ['name', 'service_type'] - position_values = [name, service_type] - args = [name, service_type] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_flavor_with_optional_params(self): - """Create test flavor including optional parameters.""" - resource = 'flavor' - cmd = flavor.CreateFlavor( - test_cli20.MyApp(sys.stdout), None) - name = 'Test flavor' - myid = 'myid' - service_type = 'DUMMY' - description = 'Test description' - position_names = ['name', 'service_type', 'description', 'enabled'] - position_values = [name, service_type, description, 'False'] - args = [name, service_type, - '--description', description, - '--enabled=False'] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_delete_flavor(self): - """Delete flavor.""" - resource = 'flavor' - cmd = flavor.DeleteFlavor(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - args = [my_id] - self._test_delete_resource(resource, cmd, my_id, args) - - def test_list_flavors(self): - """List flavors test.""" - resources = 'flavors' - cmd = flavor.ListFlavor( - test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True) - - def test_list_flavors_with_pagination(self): - """List flavors test with pagination.""" - resources = 'flavors' - cmd = flavor.ListFlavor( - test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_flavors_with_sort(self): - """List flavors test with sorting by name and id.""" - resources = 'flavors' - cmd = flavor.ListFlavor( - test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_show_flavor(self): - """Show flavor test.""" - resource = 'flavor' - cmd = flavor.ShowFlavor( - test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id']) - - def test_update_flavor_with_name(self): - """Update flavor test.""" - resource = 'flavor' - cmd = flavor.UpdateFlavor( - test_cli20.MyApp(sys.stdout), None) - newname = 'Test New Name' - newdescription = 'New Description' - args = ['--name', newname, - '--description', newdescription, - '--enabled', 'False', self.test_id] - self._test_update_resource(resource, cmd, self.test_id, args, - {'name': newname, - 'description': newdescription, - 'enabled': 'False'}) - - def test_associate_flavor(self): - """Associate flavor test.""" - resource = 'service_profile' - cmd = flavor.AssociateFlavor(test_cli20.MyApp(sys.stdout), None) - flavor_id = 'flavor-id' - profile_id = 'profile-id' - name = '' - args = [flavor_id, profile_id] - position_names = ['id'] - position_values = [profile_id] - self._test_create_resource(resource, cmd, name, profile_id, args, - position_names, position_values, - cmd_resource='flavor_profile_binding', - parent_id=flavor_id) - - def test_disassociate_flavor(self): - """Disassociate flavor test.""" - resource = 'flavor_profile_binding' - cmd = flavor.DisassociateFlavor(test_cli20.MyApp(sys.stdout), None) - flavor_id = 'flavor-id' - profile_id = 'profile-id' - args = [flavor_id, profile_id] - self._test_delete_resource(resource, cmd, profile_id, args, - parent_id=flavor_id) diff --git a/neutronclient/tests/unit/flavor/test_cli20_flavor_profile.py b/neutronclient/tests/unit/flavor/test_cli20_flavor_profile.py deleted file mode 100644 index d13090297..000000000 --- a/neutronclient/tests/unit/flavor/test_cli20_flavor_profile.py +++ /dev/null @@ -1,122 +0,0 @@ -# Copyright 2015 Hewlett-Packard Development Company, L.P. -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import sys - -from neutronclient.neutron.v2_0.flavor import flavor_profile -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20FlavorProfileJSON(test_cli20.CLITestV20Base): - - def setUp(self): - """Prepare test environment.""" - super(CLITestV20FlavorProfileJSON, self).setUp( - plurals={'service_profiles': 'service_profile'}) - self.register_non_admin_status_resource('service_profile') - - def test_create_flavor_profile_with_mandatory_params(self): - """Create test flavor profile test.""" - resource = 'service_profile' - cmd = flavor_profile.CreateFlavorProfile( - test_cli20.MyApp(sys.stdout), None) - name = '' - description = 'Test flavor profile' - myid = 'myid' - metainfo = "{'a':'b'}" - # Defaults are returned in body - position_names = ['description', 'metainfo'] - position_values = [description, metainfo] - args = ['--description', description, '--metainfo', metainfo] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_flavor_profile_with_optional_params(self): - """Create test flavor profile disabled test.""" - resource = 'service_profile' - cmd = flavor_profile.CreateFlavorProfile( - test_cli20.MyApp(sys.stdout), None) - name = '' - description = 'Test flavor profile - disabled' - myid = 'myid' - driver = 'mydriver' - metainfo = "{'a':'b'}" - position_names = ['description', 'driver', 'metainfo', 'enabled'] - position_values = [description, driver, metainfo, 'False'] - args = ['--description', description, '--driver', driver, - '--metainfo', metainfo, '--enabled=False'] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_list_flavor_profiles(self): - """List flavor profiles test.""" - resources = 'service_profiles' - cmd = flavor_profile.ListFlavorProfile( - test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True) - - def test_list_flavor_profiles_with_pagination(self): - """List flavor profiles test with pagination.""" - resources = 'service_profiles' - cmd = flavor_profile.ListFlavorProfile( - test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_flavor_profiles_with_sort(self): - """List flavor profiles test with sort by description.""" - resources = 'service_profiles' - cmd = flavor_profile.ListFlavorProfile( - test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - sort_key=["description"], - sort_dir=["asc"]) - - def test_show_flavor_profile(self): - """Show flavor profile test.""" - resource = 'service_profile' - cmd = flavor_profile.ShowFlavorProfile( - test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id']) - - def test_update_flavor_profile(self): - """Update flavor profile test.""" - resource = 'service_profile' - cmd = flavor_profile.UpdateFlavorProfile( - test_cli20.MyApp(sys.stdout), None) - newdescription = 'Test new description' - newdriver = 'NewDriver' - newmetainfo = "{'c':'d'}" - newenabled = "False" - args = ['--description', newdescription, - '--driver', newdriver, - '--metainfo', newmetainfo, - '--enabled', newenabled, - self.test_id] - self._test_update_resource(resource, cmd, self.test_id, args, - {'description': newdescription, - 'driver': newdriver, - 'metainfo': newmetainfo, - 'enabled': newenabled}) - - def test_delete_flavor_profile(self): - """Delete flavor profile.""" - resource = 'service_profile' - cmd = flavor_profile.DeleteFlavorProfile(test_cli20.MyApp(sys.stdout), - None) - my_id = 'my-id' - args = [my_id] - self._test_delete_resource(resource, cmd, my_id, args) diff --git a/neutronclient/tests/unit/fw/__init__.py b/neutronclient/tests/unit/fw/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutronclient/tests/unit/fw/test_cli20_firewall.py b/neutronclient/tests/unit/fw/test_cli20_firewall.py deleted file mode 100644 index f9a2deebf..000000000 --- a/neutronclient/tests/unit/fw/test_cli20_firewall.py +++ /dev/null @@ -1,170 +0,0 @@ -# Copyright 2013 Big Switch Networks Inc. -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import sys - -from neutronclient.neutron.v2_0.fw import firewall -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20FirewallJSON(test_cli20.CLITestV20Base): - - def test_create_firewall_with_mandatory_params(self): - # firewall-create with mandatory (none) params. - resource = 'firewall' - cmd = firewall.CreateFirewall(test_cli20.MyApp(sys.stdout), None) - name = '' - tenant_id = 'my-tenant' - my_id = 'my-id' - policy_id = 'my-policy-id' - args = ['--tenant-id', tenant_id, policy_id, ] - position_names = ['firewall_policy_id', ] - position_values = [policy_id, ] - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values, - admin_state_up=True, tenant_id=tenant_id) - - def test_create_firewall_with_all_params(self): - # firewall-create with all params set. - resource = 'firewall' - cmd = firewall.CreateFirewall(test_cli20.MyApp(sys.stdout), None) - name = 'my-name' - description = 'my-desc' - policy_id = 'my-policy-id' - tenant_id = 'my-tenant' - my_id = 'my-id' - args = ['--description', description, - '--admin-state-down', - '--tenant-id', tenant_id, - policy_id] - position_names = ['firewall_policy_id', ] - position_values = [policy_id, ] - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values, - description=description, - admin_state_up=False, - tenant_id=tenant_id) - - def test_create_firewall_with_routers(self): - resource = 'firewall' - cmd = firewall.CreateFirewall(test_cli20.MyApp(sys.stdout), None) - name = 'my-name' - policy_id = 'my-policy-id' - my_id = 'my-id' - args = ['--router', 'fake-id', '--router', 'fake-name', policy_id] - router_ids = ['fake-id', 'fake-name'] - position_names = ['firewall_policy_id', 'router_ids'] - position_values = [policy_id, router_ids] - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values) - - def test_list_firewalls(self): - # firewall-list. - resources = "firewalls" - cmd = firewall.ListFirewall(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True) - - def test_list_firewalls_pagination(self): - # firewall-list with pagination. - resources = "firewalls" - cmd = firewall.ListFirewall(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_firewalls_sort(self): - # sorted list: firewall-list --sort-key name --sort-key id - # --sort-key asc --sort-key desc - resources = "firewalls" - cmd = firewall.ListFirewall(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_firewalls_limit(self): - # size (1000) limited list: firewall-list -P. - resources = "firewalls" - cmd = firewall.ListFirewall(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_show_firewall_id(self): - # firewall-show test_id. - resource = 'firewall' - cmd = firewall.ShowFirewall(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id']) - - def test_show_firewall_id_name(self): - # firewall-show. - resource = 'firewall' - cmd = firewall.ShowFirewall(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name']) - - def test_update_firewall(self): - # firewall-update myid --name newname --tags a b. - resource = 'firewall' - cmd = firewall.UpdateFirewall(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'newname'], - {'name': 'newname', }) - - def test_update_firewall_using_policy_name(self): - # firewall-update myid --policy newpolicy. - resource = 'firewall' - cmd = firewall.UpdateFirewall(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--policy', 'newpolicy'], - {'firewall_policy_id': 'newpolicy'}) - - def test_update_firewall_with_routers(self): - resource = 'firewall' - cmd = firewall.UpdateFirewall(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource( - resource, cmd, 'myid', - ['myid', '--router', 'fake-id', '--router', 'fake-name'], - {'router_ids': ['fake-id', 'fake-name']}) - - def test_update_firewall_with_no_routers(self): - resource = 'firewall' - cmd = firewall.UpdateFirewall(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource( - resource, cmd, 'myid', - ['myid', '--no-routers'], {'router_ids': []}) - - def test_update_firewall_with_bad_router_options(self): - resource = 'firewall' - cmd = firewall.UpdateFirewall(test_cli20.MyApp(sys.stdout), None) - self.assertRaises( - SystemExit, - self._test_update_resource, - resource, cmd, 'myid', - ['myid', '--no-routers', '--router', 'fake-id'], {}) - - def test_delete_firewall(self): - # firewall-delete my-id. - resource = 'firewall' - cmd = firewall.DeleteFirewall(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - args = [my_id] - self._test_delete_resource(resource, cmd, my_id, args) - - def test_update_firewall_admin_state(self): - # firewall-update myid --admin-state-up True. - resource = 'firewall' - cmd = firewall.UpdateFirewall(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--admin-state-up', 'True'], - {'admin_state_up': 'True'}) diff --git a/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py b/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py deleted file mode 100644 index 8ec611c86..000000000 --- a/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py +++ /dev/null @@ -1,226 +0,0 @@ -# Copyright 2013 Big Switch Networks Inc. -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import sys - -from mox3 import mox - -from neutronclient.neutron.v2_0.fw import firewallpolicy -from neutronclient import shell -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20FirewallPolicyJSON(test_cli20.CLITestV20Base): - def setUp(self): - super(CLITestV20FirewallPolicyJSON, self).setUp() - - def test_create_firewall_policy_with_mandatory_params(self): - # firewall-policy-create with mandatory (none) params only. - resource = 'firewall_policy' - cmd = firewallpolicy.CreateFirewallPolicy(test_cli20.MyApp(sys.stdout), - None) - tenant_id = 'my-tenant' - name = 'my-name' - my_id = 'myid' - args = ['--tenant-id', tenant_id, - '--admin-state_up', - name, ] - position_names = ['name', ] - position_values = [name, ] - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values, - admin_state_up=True, tenant_id=tenant_id) - - def test_create_firewall_policy_with_all_params(self): - # firewall-policy-create with rule param of misc format. - resource = 'firewall_policy' - cmd = firewallpolicy.CreateFirewallPolicy(test_cli20.MyApp(sys.stdout), - None) - name = 'my-name' - description = 'my-desc' - firewall_rules_res = ['rule_id1', 'rule_id2'] - tenant_id = 'my-tenant' - my_id = 'myid' - position_names = ['name', ] - position_values = [name, ] - - # check for both str and unicode format firewall_rules_arg - for firewall_rules_arg in ['rule_id1 rule_id2', u'rule_id1 rule_id2']: - args = ['--description', description, - '--shared', - '--firewall-rules', firewall_rules_arg, - '--audited', - '--tenant-id', tenant_id, - '--admin-state_up', - name] - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values, - description=description, shared=True, - firewall_rules=firewall_rules_res, - audited=True, admin_state_up=True, - tenant_id=tenant_id) - - def test_list_firewall_policies(self): - # firewall-policy-list. - resources = "firewall_policies" - cmd = firewallpolicy.ListFirewallPolicy(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, True) - - def test_list_firewall_policies_pagination(self): - # firewall-policy-list.""" - resources = "firewall_policies" - cmd = firewallpolicy.ListFirewallPolicy(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_firewall_policies_sort(self): - # sorted list: firewall-policy-list --sort-key name --sort-key id - # --sort-key asc --sort-key desc - resources = "firewall_policies" - cmd = firewallpolicy.ListFirewallPolicy(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_firewall_policies_limit(self): - # size (1000) limited list: firewall-policy-list -P. - resources = "firewall_policies" - cmd = firewallpolicy.ListFirewallPolicy(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_show_firewall_policy_id(self): - # firewall-policy-show test_id. - resource = 'firewall_policy' - cmd = firewallpolicy.ShowFirewallPolicy(test_cli20.MyApp(sys.stdout), - None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id']) - - def test_show_firewall_policy_id_name(self): - # firewall-policy-show. - resource = 'firewall_policy' - cmd = firewallpolicy.ShowFirewallPolicy(test_cli20.MyApp(sys.stdout), - None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name']) - - def test_update_firewall_policy(self): - # firewall-policy-update myid --name newname. - resource = 'firewall_policy' - cmd = firewallpolicy.UpdateFirewallPolicy(test_cli20.MyApp(sys.stdout), - None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'newname'], - {'name': 'newname', }) - - def test_update_firewall_policy_with_rules(self): - # firewall-policy-update myid --firewall-rules "rule1 rule2". - resource = 'firewall_policy' - cmd = firewallpolicy.UpdateFirewallPolicy(test_cli20.MyApp(sys.stdout), - None) - firewall_rules_arg = u'rule_id3 rule_id4' - firewall_rules_res = ['rule_id3', 'rule_id4'] - self._test_update_resource( - resource, cmd, 'myid', - ['myid', '--firewall-rules', firewall_rules_arg], - {'firewall_rules': firewall_rules_res, }) - - def test_delete_firewall_policy(self): - # firewall-policy-delete my-id. - resource = 'firewall_policy' - cmd = firewallpolicy.DeleteFirewallPolicy(test_cli20.MyApp(sys.stdout), - None) - my_id = 'myid1' - args = [my_id] - self._test_delete_resource(resource, cmd, my_id, args) - - def test_insert_firewall_rule(self): - # firewall-policy-insert-rule myid newruleid --insert-before ruleAid - # --insert-after ruleBid - resource = 'firewall_policy' - cmd = firewallpolicy.FirewallPolicyInsertRule( - test_cli20.MyApp(sys.stdout), - None) - myid = 'myid' - args = ['myid', 'newrule', - '--insert-before', 'rule2', - '--insert-after', 'rule1'] - extrafields = {'firewall_rule_id': 'newrule', - 'insert_before': 'rule2', - 'insert_after': 'rule1'} - - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) - body = extrafields - path = getattr(self.client, resource + "_insert_path") - self.client.httpclient.request( - test_cli20.MyUrlComparator( - test_cli20.end_url(path % myid, format=self.format), - self.client), - 'PUT', body=test_cli20.MyComparator(body, self.client), - headers=mox.ContainsKeyValue( - 'X-Auth-Token', - test_cli20.TOKEN)).AndReturn((test_cli20.MyResp(204), None)) - self.mox.ReplayAll() - cmd_parser = cmd.get_parser(resource + "_insert_rule") - shell.run_command(cmd, cmd_parser, args) - self.mox.VerifyAll() - self.mox.UnsetStubs() - - def test_remove_firewall_rule(self): - # firewall-policy-remove-rule myid ruleid - resource = 'firewall_policy' - cmd = firewallpolicy.FirewallPolicyRemoveRule( - test_cli20.MyApp(sys.stdout), - None) - myid = 'myid' - args = ['myid', 'removerule'] - extrafields = {'firewall_rule_id': 'removerule', } - - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) - body = extrafields - path = getattr(self.client, resource + "_remove_path") - self.client.httpclient.request( - test_cli20.MyUrlComparator( - test_cli20.end_url(path % myid, format=self.format), - self.client), - 'PUT', body=test_cli20.MyComparator(body, self.client), - headers=mox.ContainsKeyValue( - 'X-Auth-Token', - test_cli20.TOKEN)).AndReturn((test_cli20.MyResp(204), None)) - self.mox.ReplayAll() - cmd_parser = cmd.get_parser(resource + "_remove_rule") - shell.run_command(cmd, cmd_parser, args) - self.mox.VerifyAll() - self.mox.UnsetStubs() - - def test_update_firewall_policy_name_shared_audited(self): - # firewall-policy-update myid --name newname2 --shared --audited - resource = 'firewall_policy' - cmd = firewallpolicy.UpdateFirewallPolicy(test_cli20.MyApp(sys.stdout), - None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'newname2', - '--shared', 'True', '--audited', 'True'], - {'name': 'newname2', - 'shared': 'True', 'audited': 'True'}) diff --git a/neutronclient/tests/unit/fw/test_cli20_firewallrule.py b/neutronclient/tests/unit/fw/test_cli20_firewallrule.py deleted file mode 100644 index 50fabca8e..000000000 --- a/neutronclient/tests/unit/fw/test_cli20_firewallrule.py +++ /dev/null @@ -1,251 +0,0 @@ -# Copyright 2013 Big Switch Networks Inc. -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import sys - -from neutronclient.neutron.v2_0.fw import firewallrule -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20FirewallRuleJSON(test_cli20.CLITestV20Base): - - def _test_create_firewall_rule_with_mandatory_params(self, enabled): - # firewall-rule-create with mandatory (none) params only. - resource = 'firewall_rule' - cmd = firewallrule.CreateFirewallRule(test_cli20.MyApp(sys.stdout), - None) - tenant_id = 'my-tenant' - name = '' - my_id = 'myid' - protocol = 'tcp' - action = 'allow' - ip_version = 4 - args = ['--tenant-id', tenant_id, - '--admin-state-up', - '--protocol', protocol, - '--action', action, - '--enabled', enabled] - position_names = [] - position_values = [] - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values, - protocol=protocol, action=action, - enabled=enabled, tenant_id=tenant_id, - ip_version=ip_version) - - def test_create_enabled_firewall_rule_with_mandatory_params_lcase(self): - self._test_create_firewall_rule_with_mandatory_params(enabled='true') - - def test_create_disabled_firewall_rule_with_mandatory_params_lcase(self): - self._test_create_firewall_rule_with_mandatory_params(enabled='false') - - def test_create_enabled_firewall_rule_with_mandatory_params(self): - self._test_create_firewall_rule_with_mandatory_params(enabled='True') - - def test_create_disabled_firewall_rule_with_mandatory_params(self): - self._test_create_firewall_rule_with_mandatory_params(enabled='False') - - def _setup_create_firewall_rule_with_all_params( - self, protocol='tcp', protocol_cli=None, - action='allow', action_cli=None, ip_version='4'): - # firewall-rule-create with all params set. - resource = 'firewall_rule' - cmd = firewallrule.CreateFirewallRule(test_cli20.MyApp(sys.stdout), - None) - name = 'my-name' - description = 'my-desc' - source_ip = '192.168.1.0/24' - destination_ip = '192.168.2.0/24' - source_port = '0:65535' - destination_port = '0:65535' - tenant_id = 'my-tenant' - my_id = 'myid' - enabled = 'True' - args = ['--description', description, - '--shared', - '--protocol', protocol_cli or protocol, - '--ip-version', ip_version, - '--source-ip-address', source_ip, - '--destination-ip-address', destination_ip, - '--source-port', source_port, - '--destination-port', destination_port, - '--action', action_cli or action, - '--enabled', enabled, - '--admin-state-up', - '--tenant-id', tenant_id] - position_names = [] - position_values = [] - if protocol == 'any': - protocol = None - if ip_version == '4' or ip_version == '6': - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values, - description=description, shared=True, - protocol=protocol, - ip_version=int(ip_version), - source_ip_address=source_ip, - destination_ip_address=destination_ip, - source_port=source_port, - destination_port=destination_port, - action=action, enabled='True', - tenant_id=tenant_id) - else: - self.assertRaises(SystemExit, self._test_create_resource, - resource, cmd, name, my_id, args, - position_names, position_values, - ip_version=int(ip_version), - source_ip_address=source_ip, - destination_ip_address=destination_ip, - source_port=source_port, - destination_port=destination_port, - action=action, enabled='True', - tenant_id=tenant_id) - - def test_create_firewall_rule_with_all_params(self): - self._setup_create_firewall_rule_with_all_params() - - def test_create_firewall_rule_with_proto_any(self): - self._setup_create_firewall_rule_with_all_params(protocol='any') - - def test_create_firewall_rule_with_IP_version_6(self): - self._setup_create_firewall_rule_with_all_params(ip_version='6') - - def test_create_firewall_rule_with_invalid_IP_version(self): - self._setup_create_firewall_rule_with_all_params(ip_version='5') - - def test_create_firewall_rule_with_proto_action_upper_capitalized(self): - for protocol in ('TCP', 'Tcp', 'ANY', 'AnY'): - self._setup_create_firewall_rule_with_all_params( - protocol=protocol.lower(), - protocol_cli=protocol) - for action in ('Allow', 'DENY', 'reject'): - self._setup_create_firewall_rule_with_all_params( - action=action.lower(), - action_cli=action) - - def test_list_firewall_rules(self): - # firewall-rule-list. - resources = "firewall_rules" - cmd = firewallrule.ListFirewallRule(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, True) - - def test_list_firewall_rules_pagination(self): - # firewall-rule-list. - resources = "firewall_rules" - cmd = firewallrule.ListFirewallRule(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_firewall_rules_sort(self): - # firewall-rule-list --sort-key name --sort-key id --sort-key asc - # --sort-key desc - resources = "firewall_rules" - cmd = firewallrule.ListFirewallRule(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_firewall_rules_limit(self): - # firewall-rule-list -P.""" - resources = "firewall_rules" - cmd = firewallrule.ListFirewallRule(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_show_firewall_rule_id(self): - # firewall-rule-show test_id. - resource = 'firewall_rule' - cmd = firewallrule.ShowFirewallRule(test_cli20.MyApp(sys.stdout), - None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id']) - - def test_show_firewall_rule_id_name(self): - # firewall-rule-show. - resource = 'firewall_rule' - cmd = firewallrule.ShowFirewallRule(test_cli20.MyApp(sys.stdout), - None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name']) - - def test_update_firewall_rule(self): - # firewall-rule-update myid --name newname. - resource = 'firewall_rule' - cmd = firewallrule.UpdateFirewallRule(test_cli20.MyApp(sys.stdout), - None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'newname'], - {'name': 'newname', }) - - # firewall-rule-update myid --protocol any. - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--protocol', 'any'], - {'protocol': None, }) - - # firewall-rule-update myid --description any - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--description', 'any'], - {'description': 'any', }) - - # firewall-rule-update myid --source_ip_address 192.192.192.192 - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--source_ip_address', - '192.192.192.192'], - {'source_ip_address': '192.192.192.192', }) - - # firewall-rule-update myid --source_port 32767 - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--source_port', '32767'], - {'source_port': '32767', }) - - # firewall-rule-update myid --destination_ip_address 0.1.0.1 - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--destination_ip_address', - '0.1.0.1'], - {'destination_ip_address': '0.1.0.1', }) - - # firewall-rule-update myid --destination_port 65432 - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--destination_port', - '65432'], - {'destination_port': '65432', }) - - # firewall-rule-update myid --enabled False - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--enabled', 'False'], - {'enabled': 'False', }) - - # firewall-rule-update myid --action reject - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--action', 'reject'], - {'action': 'reject', }) - - # firewall-rule-update myid --shared false - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--shared', 'false'], - {'shared': 'false', }) - - def test_delete_firewall_rule(self): - # firewall-rule-delete my-id. - resource = 'firewall_rule' - cmd = firewallrule.DeleteFirewallRule(test_cli20.MyApp(sys.stdout), - None) - my_id = 'myid1' - args = [my_id] - self._test_delete_resource(resource, cmd, my_id, args) diff --git a/neutronclient/tests/unit/lb/__init__.py b/neutronclient/tests/unit/lb/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutronclient/tests/unit/lb/test_cli20_healthmonitor.py b/neutronclient/tests/unit/lb/test_cli20_healthmonitor.py deleted file mode 100644 index 999116e2a..000000000 --- a/neutronclient/tests/unit/lb/test_cli20_healthmonitor.py +++ /dev/null @@ -1,202 +0,0 @@ -# Copyright 2013 Mirantis Inc. -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import sys - -from mox3 import mox - -from neutronclient.neutron.v2_0.lb import healthmonitor -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20LbHealthmonitorJSON(test_cli20.CLITestV20Base): - def test_create_healthmonitor_with_mandatory_params(self): - # lb-healthmonitor-create with mandatory params only. - resource = 'health_monitor' - cmd = healthmonitor.CreateHealthMonitor(test_cli20.MyApp(sys.stdout), - None) - admin_state_up = False - delay = '60' - max_retries = '2' - timeout = '10' - type = 'TCP' - tenant_id = 'my-tenant' - my_id = 'my-id' - args = ['--admin-state-down', - '--delay', delay, - '--max-retries', max_retries, - '--timeout', timeout, - '--type', type, - '--tenant-id', tenant_id] - position_names = ['admin_state_up', 'delay', 'max_retries', 'timeout', - 'type', 'tenant_id'] - position_values = [admin_state_up, delay, max_retries, timeout, type, - tenant_id] - self._test_create_resource(resource, cmd, '', my_id, args, - position_names, position_values) - - def test_create_healthmonitor_with_all_params(self): - # lb-healthmonitor-create with all params set. - resource = 'health_monitor' - cmd = healthmonitor.CreateHealthMonitor(test_cli20.MyApp(sys.stdout), - None) - admin_state_up = False - delay = '60' - expected_codes = '200-202,204' - http_method = 'HEAD' - max_retries = '2' - timeout = '10' - type = 'TCP' - tenant_id = 'my-tenant' - url_path = '/health' - my_id = 'my-id' - args = ['--admin-state-down', - '--delay', delay, - '--expected-codes', expected_codes, - '--http-method', http_method, - '--max-retries', max_retries, - '--timeout', timeout, - '--type', type, - '--tenant-id', tenant_id, - '--url-path', url_path] - position_names = ['admin_state_up', 'delay', - 'expected_codes', 'http_method', - 'max_retries', 'timeout', - 'type', 'tenant_id', 'url_path'] - position_values = [admin_state_up, delay, - expected_codes, http_method, - max_retries, timeout, - type, tenant_id, url_path] - self._test_create_resource(resource, cmd, '', my_id, args, - position_names, position_values) - - def test_list_healthmonitors(self): - # lb-healthmonitor-list. - resources = "health_monitors" - cmd = healthmonitor.ListHealthMonitor(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, True) - - def test_list_healthmonitors_pagination(self): - # lb-healthmonitor-list. - resources = "health_monitors" - cmd = healthmonitor.ListHealthMonitor(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_healthmonitors_sort(self): - # lb-healthmonitor-list --sort-key name --sort-key id --sort-key asc - # --sort-key desc - resources = "health_monitors" - cmd = healthmonitor.ListHealthMonitor(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_healthmonitors_limit(self): - # lb-healthmonitor-list -P. - resources = "health_monitors" - cmd = healthmonitor.ListHealthMonitor(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_show_healthmonitor_id(self): - # lb-healthmonitor-show test_id. - resource = 'health_monitor' - cmd = healthmonitor.ShowHealthMonitor(test_cli20.MyApp(sys.stdout), - None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id']) - - def test_update_health_monitor(self): - # lb-healthmonitor-update myid --name myname --tags a b. - resource = 'health_monitor' - cmd = healthmonitor.UpdateHealthMonitor(test_cli20.MyApp(sys.stdout), - None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--timeout', '5'], - {'timeout': '5', }) - - def test_delete_healthmonitor(self): - # lb-healthmonitor-delete my-id.""" - resource = 'health_monitor' - cmd = healthmonitor.DeleteHealthMonitor(test_cli20.MyApp(sys.stdout), - None) - my_id = 'my-id' - args = [my_id] - self._test_delete_resource(resource, cmd, my_id, args) - - def test_associate_healthmonitor(self): - cmd = healthmonitor.AssociateHealthMonitor( - test_cli20.MyApp(sys.stdout), - None) - resource = 'health_monitor' - health_monitor_id = 'hm-id' - pool_id = 'p_id' - args = [health_monitor_id, pool_id] - - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) - - body = {resource: {'id': health_monitor_id}} - result = {resource: {'id': health_monitor_id}, } - result_str = self.client.serialize(result) - - path = getattr(self.client, - "associate_pool_health_monitors_path") % pool_id - return_tup = (test_cli20.MyResp(200), result_str) - self.client.httpclient.request( - test_cli20.end_url(path), 'POST', - body=test_cli20.MyComparator(body, self.client), - headers=mox.ContainsKeyValue( - 'X-Auth-Token', test_cli20.TOKEN)).AndReturn(return_tup) - self.mox.ReplayAll() - cmd_parser = cmd.get_parser('test_' + resource) - parsed_args = cmd_parser.parse_args(args) - cmd.run(parsed_args) - self.mox.VerifyAll() - self.mox.UnsetStubs() - - def test_disassociate_healthmonitor(self): - cmd = healthmonitor.DisassociateHealthMonitor( - test_cli20.MyApp(sys.stdout), - None) - resource = 'health_monitor' - health_monitor_id = 'hm-id' - pool_id = 'p_id' - args = [health_monitor_id, pool_id] - - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) - - path = (getattr(self.client, - "disassociate_pool_health_monitors_path") % - {'pool': pool_id, 'health_monitor': health_monitor_id}) - return_tup = (test_cli20.MyResp(204), None) - self.client.httpclient.request( - test_cli20.end_url(path), 'DELETE', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', test_cli20.TOKEN)).AndReturn(return_tup) - self.mox.ReplayAll() - cmd_parser = cmd.get_parser('test_' + resource) - parsed_args = cmd_parser.parse_args(args) - cmd.run(parsed_args) - self.mox.VerifyAll() - self.mox.UnsetStubs() diff --git a/neutronclient/tests/unit/lb/test_cli20_member.py b/neutronclient/tests/unit/lb/test_cli20_member.py deleted file mode 100644 index 8ce2a0f09..000000000 --- a/neutronclient/tests/unit/lb/test_cli20_member.py +++ /dev/null @@ -1,118 +0,0 @@ -# Copyright 2013 Mirantis Inc. -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import sys - -from neutronclient.neutron.v2_0.lb import member -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20LbMemberJSON(test_cli20.CLITestV20Base): - def setUp(self): - super(CLITestV20LbMemberJSON, self).setUp(plurals={'tags': 'tag'}) - - def test_create_member(self): - # lb-member-create with mandatory params only. - resource = 'member' - cmd = member.CreateMember(test_cli20.MyApp(sys.stdout), None) - address = '10.0.0.1' - port = '8080' - tenant_id = 'my-tenant' - my_id = 'my-id' - pool_id = 'pool-id' - args = ['--address', address, '--protocol-port', port, - '--tenant-id', tenant_id, pool_id] - position_names = ['address', 'protocol_port', 'tenant_id', 'pool_id', - 'admin_state_up'] - position_values = [address, port, tenant_id, pool_id, True] - self._test_create_resource(resource, cmd, None, my_id, args, - position_names, position_values, - admin_state_up=None) - - def test_create_member_all_params(self): - # lb-member-create with all available params. - resource = 'member' - cmd = member.CreateMember(test_cli20.MyApp(sys.stdout), None) - address = '10.0.0.1' - admin_state_up = False - port = '8080' - weight = '1' - tenant_id = 'my-tenant' - my_id = 'my-id' - pool_id = 'pool-id' - args = ['--address', address, '--admin-state-down', - '--protocol-port', port, '--weight', weight, - '--tenant-id', tenant_id, pool_id] - position_names = [ - 'address', 'admin_state_up', 'protocol_port', 'weight', - 'tenant_id', 'pool_id' - ] - position_values = [address, admin_state_up, port, weight, - tenant_id, pool_id] - self._test_create_resource(resource, cmd, None, my_id, args, - position_names, position_values, - admin_state_up=None) - - def test_list_members(self): - # lb-member-list. - resources = "members" - cmd = member.ListMember(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True) - - def test_list_members_pagination(self): - # lb-member-list. - resources = "members" - cmd = member.ListMember(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_members_sort(self): - # lb-member-list --sort-key name --sort-key id --sort-key asc - # --sort-key desc - resources = "members" - cmd = member.ListMember(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_members_limit(self): - # lb-member-list -P. - resources = "members" - cmd = member.ListMember(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_show_member_id(self): - # lb-member-show test_id. - resource = 'member' - cmd = member.ShowMember(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id']) - - def test_update_member(self): - # lb-member-update myid --name myname --tags a b. - resource = 'member' - cmd = member.UpdateMember(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'myname', - '--tags', 'a', 'b'], - {'name': 'myname', 'tags': ['a', 'b'], }) - - def test_delete_member(self): - # lb-member-delete my-id. - resource = 'member' - cmd = member.DeleteMember(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - args = [my_id] - self._test_delete_resource(resource, cmd, my_id, args) diff --git a/neutronclient/tests/unit/lb/test_cli20_pool.py b/neutronclient/tests/unit/lb/test_cli20_pool.py deleted file mode 100644 index 9a08f66fe..000000000 --- a/neutronclient/tests/unit/lb/test_cli20_pool.py +++ /dev/null @@ -1,165 +0,0 @@ -# Copyright 2013 Mirantis Inc. -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import sys - -from mox3 import mox - -from neutronclient.neutron.v2_0.lb import pool -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20LbPoolJSON(test_cli20.CLITestV20Base): - - def test_create_pool_with_mandatory_params(self): - # lb-pool-create with mandatory params only. - resource = 'pool' - cmd = pool.CreatePool(test_cli20.MyApp(sys.stdout), None) - name = 'my-name' - lb_method = 'ROUND_ROBIN' - protocol = 'HTTP' - subnet_id = 'subnet-id' - tenant_id = 'my-tenant' - my_id = 'my-id' - args = ['--lb-method', lb_method, - '--name', name, - '--protocol', protocol, - '--subnet-id', subnet_id, - '--tenant-id', tenant_id] - position_names = ['admin_state_up', 'lb_method', 'name', - 'protocol', 'subnet_id', 'tenant_id'] - position_values = [True, lb_method, name, - protocol, subnet_id, tenant_id] - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values) - - def test_create_pool_with_all_params(self): - # lb-pool-create with all params set. - resource = 'pool' - cmd = pool.CreatePool(test_cli20.MyApp(sys.stdout), None) - name = 'my-name' - description = 'my-desc' - lb_method = 'ROUND_ROBIN' - protocol = 'HTTP' - subnet_id = 'subnet-id' - tenant_id = 'my-tenant' - my_id = 'my-id' - provider = 'lbaas' - args = ['--admin-state-down', - '--description', description, - '--lb-method', lb_method, - '--name', name, - '--protocol', protocol, - '--subnet-id', subnet_id, - '--tenant-id', tenant_id, - '--provider', provider] - position_names = ['admin_state_up', 'description', 'lb_method', 'name', - 'protocol', 'subnet_id', 'tenant_id', 'provider'] - position_values = [False, description, lb_method, name, - protocol, subnet_id, tenant_id, provider] - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values) - - def test_list_pools(self): - # lb-pool-list. - resources = "pools" - cmd = pool.ListPool(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True) - - def test_list_pools_pagination(self): - # lb-pool-list. - resources = "pools" - cmd = pool.ListPool(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_pools_sort(self): - # lb-pool-list --sort-key name --sort-key id --sort-key asc - # --sort-key desc - resources = "pools" - cmd = pool.ListPool(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_pools_limit(self): - # lb-pool-list -P. - resources = "pools" - cmd = pool.ListPool(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_show_pool_id(self): - # lb-pool-show test_id. - resource = 'pool' - cmd = pool.ShowPool(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id']) - - def test_show_pool_id_name(self): - # lb-pool-show. - resource = 'pool' - cmd = pool.ShowPool(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name']) - - def test_update_pool(self): - # lb-pool-update myid --name newname --tags a b. - resource = 'pool' - cmd = pool.UpdatePool(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'newname'], - {'name': 'newname', }) - - def test_delete_pool(self): - # lb-pool-delete my-id. - resource = 'pool' - cmd = pool.DeletePool(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - args = [my_id] - self._test_delete_resource(resource, cmd, my_id, args) - - def test_retrieve_pool_stats(self): - # lb-pool-stats test_id. - resource = 'pool' - cmd = pool.RetrievePoolStats(test_cli20.MyApp(sys.stdout), None) - my_id = self.test_id - fields = ['bytes_in', 'bytes_out'] - args = ['--fields', 'bytes_in', '--fields', 'bytes_out', my_id] - - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) - query = "&".join(["fields=%s" % field for field in fields]) - expected_res = {'stats': {'bytes_in': '1234', 'bytes_out': '4321'}} - resstr = self.client.serialize(expected_res) - path = getattr(self.client, "pool_path_stats") - return_tup = (test_cli20.MyResp(200), resstr) - self.client.httpclient.request( - test_cli20.end_url(path % my_id, query), 'GET', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', test_cli20.TOKEN)).AndReturn(return_tup) - self.mox.ReplayAll() - - cmd_parser = cmd.get_parser("test_" + resource) - parsed_args = cmd_parser.parse_args(args) - cmd.run(parsed_args) - - self.mox.VerifyAll() - self.mox.UnsetStubs() - _str = self.fake_stdout.make_string() - self.assertIn('bytes_in', _str) - self.assertIn('bytes_out', _str) diff --git a/neutronclient/tests/unit/lb/test_cli20_vip.py b/neutronclient/tests/unit/lb/test_cli20_vip.py deleted file mode 100644 index b08de447e..000000000 --- a/neutronclient/tests/unit/lb/test_cli20_vip.py +++ /dev/null @@ -1,206 +0,0 @@ -# Copyright 2013 Mirantis Inc. -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import sys - -from neutronclient.neutron.v2_0.lb import vip -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20LbVipJSON(test_cli20.CLITestV20Base): - def setUp(self): - super(CLITestV20LbVipJSON, self).setUp(plurals={'tags': 'tag'}) - - def test_create_vip_with_mandatory_params(self): - # lb-vip-create with all mandatory params. - resource = 'vip' - cmd = vip.CreateVip(test_cli20.MyApp(sys.stdout), None) - pool_id = 'my-pool-id' - name = 'my-name' - subnet_id = 'subnet-id' - protocol_port = '1000' - protocol = 'TCP' - tenant_id = 'my-tenant' - my_id = 'my-id' - args = ['--name', name, - '--protocol-port', protocol_port, - '--protocol', protocol, - '--subnet-id', subnet_id, - '--tenant-id', tenant_id, - pool_id] - position_names = ['pool_id', 'name', 'protocol_port', 'protocol', - 'subnet_id', 'tenant_id'] - position_values = [pool_id, name, protocol_port, protocol, - subnet_id, tenant_id] - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values, - admin_state_up=True) - - def test_create_vip_with_all_params(self): - # lb-vip-create with all params. - resource = 'vip' - cmd = vip.CreateVip(test_cli20.MyApp(sys.stdout), None) - pool_id = 'my-pool-id' - name = 'my-name' - description = 'my-desc' - address = '10.0.0.2' - admin_state = False - connection_limit = '1000' - subnet_id = 'subnet-id' - protocol_port = '80' - protocol = 'TCP' - tenant_id = 'my-tenant' - my_id = 'my-id' - args = ['--name', name, - '--description', description, - '--address', address, - '--admin-state-down', - '--connection-limit', connection_limit, - '--protocol-port', protocol_port, - '--protocol', protocol, - '--subnet-id', subnet_id, - '--tenant-id', tenant_id, - pool_id] - position_names = ['pool_id', 'name', 'description', 'address', - 'admin_state_up', 'connection_limit', - 'protocol_port', 'protocol', 'subnet_id', - 'tenant_id'] - position_values = [pool_id, name, description, address, - admin_state, connection_limit, protocol_port, - protocol, subnet_id, - tenant_id] - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values) - - def test_create_vip_with_session_persistence_params(self): - # lb-vip-create with mandatory and session-persistence params. - resource = 'vip' - cmd = vip.CreateVip(test_cli20.MyApp(sys.stdout), None) - pool_id = 'my-pool-id' - name = 'my-name' - subnet_id = 'subnet-id' - protocol_port = '1000' - protocol = 'TCP' - tenant_id = 'my-tenant' - my_id = 'my-id' - args = ['--name', name, - '--protocol-port', protocol_port, - '--protocol', protocol, - '--subnet-id', subnet_id, - '--tenant-id', tenant_id, - pool_id, - '--session-persistence', 'type=dict', - 'type=cookie,cookie_name=pie', - '--optional-param', 'any'] - position_names = ['pool_id', 'name', 'protocol_port', 'protocol', - 'subnet_id', 'tenant_id', 'optional_param'] - position_values = [pool_id, name, protocol_port, protocol, - subnet_id, tenant_id, 'any'] - extra_body = { - 'session_persistence': { - 'type': 'cookie', - 'cookie_name': 'pie', - }, - } - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values, - admin_state_up=True, extra_body=extra_body) - - def test_list_vips(self): - # lb-vip-list. - resources = "vips" - cmd = vip.ListVip(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True) - - def test_list_vips_pagination(self): - # lb-vip-list. - resources = "vips" - cmd = vip.ListVip(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_vips_sort(self): - # lb-vip-list --sort-key name --sort-key id --sort-key asc - # --sort-key desc - resources = "vips" - cmd = vip.ListVip(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_vips_limit(self): - # lb-vip-list -P. - resources = "vips" - cmd = vip.ListVip(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_show_vip_id(self): - # lb-vip-show test_id. - resource = 'vip' - cmd = vip.ShowVip(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id']) - - def test_show_vip_id_name(self): - # lb-vip-show. - resource = 'vip' - cmd = vip.ShowVip(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name']) - - def test_update_vip(self): - # lb-vip-update myid --name myname --tags a b. - resource = 'vip' - cmd = vip.UpdateVip(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'myname', - '--tags', 'a', 'b'], - {'name': 'myname', 'tags': ['a', 'b'], }) - - def test_update_vip_with_session_persistence(self): - resource = 'vip' - cmd = vip.UpdateVip(test_cli20.MyApp(sys.stdout), None) - body = { - 'session_persistence': { - 'type': 'source', - }, - } - args = ['myid', '--session-persistence', 'type=dict', - 'type=source'] - self._test_update_resource(resource, cmd, 'myid', args, body) - - def test_update_vip_with_session_persistence_and_name(self): - resource = 'vip' - cmd = vip.UpdateVip(test_cli20.MyApp(sys.stdout), None) - body = { - 'name': 'newname', - 'session_persistence': { - 'type': 'cookie', - 'cookie_name': 'pie', - }, - } - args = ['myid', '--name', 'newname', - '--session-persistence', 'type=dict', - 'type=cookie,cookie_name=pie'] - self._test_update_resource(resource, cmd, 'myid', args, body) - - def test_delete_vip(self): - # lb-vip-delete my-id. - resource = 'vip' - cmd = vip.DeleteVip(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - args = [my_id] - self._test_delete_resource(resource, cmd, my_id, args) diff --git a/neutronclient/tests/unit/lb/v2/__init__.py b/neutronclient/tests/unit/lb/v2/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_healthmonitor.py b/neutronclient/tests/unit/lb/v2/test_cli20_healthmonitor.py deleted file mode 100644 index 945ac18e3..000000000 --- a/neutronclient/tests/unit/lb/v2/test_cli20_healthmonitor.py +++ /dev/null @@ -1,175 +0,0 @@ -# Copyright 2014 Blue Box Group, Inc. -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import sys - -from neutronclient.neutron.v2_0.lb.v2 import healthmonitor -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20LbHealthMonitorJSON(test_cli20.CLITestV20Base): - - def test_create_healthmonitor_with_mandatory_params(self): - # lbaas-healthmonitor-create with mandatory params only. - resource = 'healthmonitor' - cmd_resource = 'lbaas_healthmonitor' - cmd = healthmonitor.CreateHealthMonitor(test_cli20.MyApp(sys.stdout), - None) - my_id = 'my-id' - type = 'PING' - max_retries = '3' - delay = '10' - timeout = '60' - pool = 'pool1' - args = ['--type', type, '--max-retries', max_retries, - '--delay', delay, '--timeout', timeout, '--pool', pool] - position_names = ['type', 'max_retries', 'delay', 'timeout', 'pool_id'] - position_values = [type, max_retries, delay, timeout, pool] - self._test_create_resource(resource, cmd, '', my_id, args, - position_names, position_values, - cmd_resource=cmd_resource) - - def test_create_healthmonitor_with_all_params(self): - # lbaas-healthmonitor-create with all params set. - resource = 'healthmonitor' - cmd_resource = 'lbaas_healthmonitor' - cmd = healthmonitor.CreateHealthMonitor(test_cli20.MyApp(sys.stdout), - None) - my_id = 'my-id' - type = 'PING' - max_retries = '3' - delay = '10' - timeout = '60' - http_method = 'GET' - expected_codes = '201' - url_path = '/somepath' - pool = 'pool1' - name = 'healthmonitor1' - args = ['--admin-state-down', '--http-method', http_method, - '--expected-codes', expected_codes, '--url-path', url_path, - '--type', type, '--max-retries', max_retries, - '--delay', delay, '--timeout', timeout, '--pool', pool, - '--name', name] - position_names = ['admin_state_up', 'http_method', 'expected_codes', - 'url_path', 'type', 'max_retries', 'delay', - 'timeout', 'pool_id', 'name'] - position_values = [False, http_method, expected_codes, url_path, - type, max_retries, delay, timeout, pool, name] - self._test_create_resource(resource, cmd, '', my_id, args, - position_names, position_values, - cmd_resource=cmd_resource) - - def test_list_healthmonitors(self): - # lbaas-healthmonitor-list. - resources = 'healthmonitors' - cmd_resources = 'lbaas_healthmonitors' - cmd = healthmonitor.ListHealthMonitor(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, True, - cmd_resources=cmd_resources) - - def test_list_healthmonitors_pagination(self): - # lbaas-healthmonitor-list with pagination. - resources = 'healthmonitors' - cmd_resources = 'lbaas_healthmonitors' - cmd = healthmonitor.ListHealthMonitor(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources_with_pagination(resources, cmd, - cmd_resources=cmd_resources) - - def test_list_healthmonitors_sort(self): - # lbaas-healthmonitor-list --sort-key id --sort-key asc. - resources = 'healthmonitors' - cmd_resources = 'lbaas_healthmonitors' - cmd = healthmonitor.ListHealthMonitor(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, True, - cmd_resources=cmd_resources) - - def test_list_healthmonitors_limit(self): - # lbaas-healthmonitor-list -P. - resources = 'healthmonitors' - cmd_resources = 'lbaas_healthmonitors' - cmd = healthmonitor.ListHealthMonitor(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, page_size=1000, - cmd_resources=cmd_resources) - - def test_show_healthmonitor_id(self): - # lbaas-healthmonitor-show test_id. - resource = 'healthmonitor' - cmd_resource = 'lbaas_healthmonitor' - cmd = healthmonitor.ShowHealthMonitor(test_cli20.MyApp(sys.stdout), - None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id'], - cmd_resource=cmd_resource) - - def test_show_healthmonitor_id_name(self): - # lbaas-healthmonitor-show. - resource = 'healthmonitor' - cmd_resource = 'lbaas_healthmonitor' - cmd = healthmonitor.ShowHealthMonitor(test_cli20.MyApp(sys.stdout), - None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name'], - cmd_resource=cmd_resource) - - def _test_update_hm(self, args, expected_values): - resource = 'healthmonitor' - cmd_resource = 'lbaas_healthmonitor' - my_id = 'myid' - cmd = healthmonitor.UpdateHealthMonitor(test_cli20.MyApp(sys.stdout), - None) - args.insert(0, my_id) - self._test_update_resource(resource, cmd, my_id, - args, - expected_values, - cmd_resource=cmd_resource) - - def test_update_healthmonitor(self): - # lbaas-healthmonitor-update myid --name newname. - self._test_update_hm(['--name', 'newname'], {'name': 'newname', }) - # lbaas-healthmonitor-update myid --delay 10. - self._test_update_hm(['--delay', '10'], {'delay': '10'}) - # lbaas-healthmonitor-update myid --timeout 5. - self._test_update_hm(['--timeout', '5'], {'timeout': '5', }) - # lbaas-healthmonitor-update myid --delay 10. - self._test_update_hm(['--http-method', 'OPTIONS'], - {'http_method': 'OPTIONS'}) - # lbaas-healthmonitor-update myid --url-path /test/string . - self._test_update_hm(['--url-path', '/test/string'], - {'url_path': '/test/string', }) - # lbaas-healthmonitor-update myid --max-retries 5 - self._test_update_hm(['--max-retries', '5'], {'max_retries': '5'}) - # lbaas-healthmonitor-update myid --expected-codes 201 - self._test_update_hm(['--expected-codes', '201'], - {'expected_codes': '201'}) - # lbaas-healthmonitor-update myid --admin-state-up False - self._test_update_hm(['--admin-state-up', 'False'], - {'admin_state_up': 'False'}) - - def test_delete_healthmonitor(self): - # lbaas-healthmonitor-delete my-id. - resource = 'healthmonitor' - cmd_resource = 'lbaas_healthmonitor' - cmd = healthmonitor.DeleteHealthMonitor(test_cli20.MyApp(sys.stdout), - None) - my_id = 'my-id' - args = [my_id] - self._test_delete_resource(resource, cmd, my_id, args, - cmd_resource=cmd_resource) diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_l7policy.py b/neutronclient/tests/unit/lb/v2/test_cli20_l7policy.py deleted file mode 100644 index 71869315b..000000000 --- a/neutronclient/tests/unit/lb/v2/test_cli20_l7policy.py +++ /dev/null @@ -1,260 +0,0 @@ -# Copyright 2016 Radware LTD. -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import sys - -from neutronclient.common import exceptions -from neutronclient.neutron.v2_0.lb.v2 import l7policy -from neutronclient.tests.unit import test_cli20 - -"""Structure for mapping cli and api arguments - -The structure maps cli arguments and a list of its -api argument name, default cli value and default api value. -It helps to make tests more general for different argument types. -""" -args_conf = { - 'name': ['name', 'test_policy', 'test_policy'], - 'description': ['description', 'test policy', 'test policy'], - 'listener': ['listener_id', 'test_listener', 'test_listener'], - 'admin-state-up': ['admin_state_up', True, True], - 'admin-state-down': ['admin_state_up', None, False], - 'action': ['action', 'REJECT', 'REJECT'], - 'redirect-url': ['redirect_url', 'http://url', 'http://url'], - 'redirect-pool': ['redirect_pool_id', 'test_pool', 'test_pool'], - 'position': ['position', '1', 1]} - - -class CLITestV20LbL7PolicyJSON(test_cli20.CLITestV20Base): - - def _get_test_args(self, *args, **kwargs): - """Function for generically building testing arguments""" - cli_args = [] - api_args = {} - for arg in args: - cli_args.append('--' + arg) - if not args_conf[arg][1]: - pass - elif arg in kwargs: - cli_args.append(str(kwargs[arg])) - else: - cli_args.append(args_conf[arg][1]) - - if arg in kwargs: - api_args[args_conf[arg][0]] = kwargs[arg] - else: - api_args[args_conf[arg][0]] = args_conf[arg][2] - - return cli_args, api_args - - def _test_create_policy(self, *args, **kwargs): - resource = 'l7policy' - cmd_resource = 'lbaas_l7policy' - cmd = l7policy.CreateL7Policy(test_cli20.MyApp(sys.stdout), None) - cli_args, api_args = self._get_test_args(*args, **kwargs) - position_names = list(api_args.keys()) - position_values = list(api_args.values()) - self._test_create_resource(resource, cmd, None, 'test_id', - cli_args, position_names, position_values, - cmd_resource=cmd_resource) - - def _test_update_policy(self, *args, **kwargs): - resource = 'l7policy' - cmd_resource = 'lbaas_l7policy' - cmd = l7policy.UpdateL7Policy(test_cli20.MyApp(sys.stdout), None) - cli_args, api_args = self._get_test_args(*args, **kwargs) - cli_args.append('test_id') - self._test_update_resource(resource, cmd, 'test_id', - cli_args, api_args, - cmd_resource=cmd_resource) - - def test_create_policy_with_mandatory_params(self): - # lbaas-l7policy-create with mandatory params only. - self._test_create_policy('action', 'listener') - - def test_create_policy_with_all_params(self): - # lbaas-l7policy-create REJECT policy. - self._test_create_policy('name', 'description', - 'action', 'listener', - 'position') - - def test_create_disabled_policy(self): - # lbaas-l7policy-create disabled REJECT policy. - self._test_create_policy('action', 'listener', 'admin-state-down') - - def test_create_url_redirect_policy(self): - # lbaas-l7policy-create REDIRECT_TO_URL policy. - self._test_create_policy('name', 'description', - 'action', 'listener', - 'redirect-url', - action='REDIRECT_TO_URL') - - def test_create_url_redirect_policy_no_url(self): - # lbaas-l7policy-create REDIRECT_TO_URL policy without url argument. - self.assertRaises(exceptions.CommandError, - self._test_create_policy, - 'name', 'description', - 'action', 'listener', - action='REDIRECT_TO_URL') - - def test_create_pool_redirect_policy(self): - # lbaas-l7policy-create REDIRECT_TO_POOL policy. - self._test_create_policy('name', 'description', - 'action', 'listener', - 'redirect-pool', - action='REDIRECT_TO_POOL') - - def test_create_pool_redirect_policy_no_pool(self): - # lbaas-l7policy-create REDIRECT_TO_POOL policy without pool argument. - self.assertRaises(exceptions.CommandError, - self._test_create_policy, - 'name', 'description', - 'action', 'listener', - action='REDIRECT_TO_POOL') - - def test_create_reject_policy_with_url(self): - # lbaas-l7policy-create REJECT policy while specifying url argument. - self.assertRaises(exceptions.CommandError, - self._test_create_policy, - 'action', 'listener', - 'redirect-url') - - def test_create_reject_policy_with_pool(self): - # lbaas-l7policy-create REJECT policy while specifying pool argument. - self.assertRaises(exceptions.CommandError, - self._test_create_policy, - 'action', 'listener', - 'redirect-pool') - - def test_create_pool_redirect_policy_with_url(self): - # lbaas-l7policy-create REDIRECT_TO_POOL policy with url argument. - self.assertRaises(exceptions.CommandError, - self._test_create_policy, - 'action', 'listener', - 'redirect-pool', 'redirect-url', - action='REDIRECT_TO_POOL') - - def test_create_url_redirect_policy_with_pool(self): - # lbaas-l7policy-create REDIRECT_TO_URL policy with pool argument. - self.assertRaises(exceptions.CommandError, - self._test_create_policy, - 'action', 'listener', - 'redirect-pool', 'redirect-url', - action='REDIRECT_TO_URL') - - def test_list_policies(self): - # lbaas-l7policy-list. - - resources = 'l7policies' - cmd_resources = 'lbaas_l7policies' - cmd = l7policy.ListL7Policy(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True, - cmd_resources=cmd_resources) - - def test_list_policies_pagination(self): - # lbaas-l7policy-list with pagination. - - resources = 'l7policies' - cmd_resources = 'lbaas_l7policies' - cmd = l7policy.ListL7Policy(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination( - resources, cmd, cmd_resources=cmd_resources) - - def test_list_policies_sort(self): - # lbaas-l7policy-list --sort-key id --sort-key asc. - - resources = 'l7policies' - cmd_resources = 'lbaas_l7policies' - cmd = l7policy.ListL7Policy(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources( - resources, cmd, True, cmd_resources=cmd_resources) - - def test_list_policies_limit(self): - # lbaas-l7policy-list -P. - - resources = 'l7policies' - cmd_resources = 'lbaas_l7policies' - cmd = l7policy.ListL7Policy(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources( - resources, cmd, page_size=1000, cmd_resources=cmd_resources) - - def test_show_policy_id(self): - # lbaas-l7policy-show test_id. - - resource = 'l7policy' - cmd_resource = 'lbaas_l7policy' - cmd = l7policy.ShowL7Policy(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'test_id', self.test_id] - self._test_show_resource( - resource, cmd, self.test_id, args, - ['test_id'], cmd_resource=cmd_resource) - - def test_show_policy_id_name(self): - # lbaas-l7policy-show. - - resource = 'l7policy' - cmd_resource = 'lbaas_l7policy' - cmd = l7policy.ShowL7Policy(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'test_id', '--fields', 'name', self.test_id] - self._test_show_resource( - resource, cmd, self.test_id, args, - ['test_id', 'name'], cmd_resource=cmd_resource) - - def test_disable_policy(self): - # lbaas-l7policy-update test_id --admin-state-up False. - - self._test_update_policy('admin-state-up', - **{'admin-state-up': 'False'}) - - def test_update_policy_name_and_description(self): - # lbaas-l7policy-update test_id --name other --description other_desc. - - self._test_update_policy('name', 'description', - name='name', - description='other desc') - - def test_update_pool_redirect_policy(self): - # lbaas-l7policy-update test_id --action REDIRECT_TO_POOL - # --redirect-pool id. - - self._test_update_policy('action', 'redirect-pool', - **{'action': 'REDIRECT_TO_POOL', - 'redirect-pool': 'id'}) - - def test_update_url_redirect_policy(self): - # lbaas-l7policy-update test_id --action REDIRECT_TO_URL - # --redirect-url http://other_url. - - self._test_update_policy('action', 'redirect-url', - **{'action': 'REDIRECT_TO_URL', - 'redirect-url': 'http://other_url'}) - - def test_update_policy_position(self): - # lbaas-l7policy-update test_id --position 2. - - self._test_update_policy('position', - position=2) - - def test_delete_policy(self): - # lbaas-l7policy-delete test_id. - - resource = 'l7policy' - cmd_resource = 'lbaas_l7policy' - cmd = l7policy.DeleteL7Policy(test_cli20.MyApp(sys.stdout), None) - test_id = 'test_id' - args = [test_id] - self._test_delete_resource(resource, cmd, test_id, args, - cmd_resource=cmd_resource) diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_l7rule.py b/neutronclient/tests/unit/lb/v2/test_cli20_l7rule.py deleted file mode 100644 index 54830542d..000000000 --- a/neutronclient/tests/unit/lb/v2/test_cli20_l7rule.py +++ /dev/null @@ -1,210 +0,0 @@ -# Copyright 2016 Radware LTD. -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import sys - -from neutronclient.neutron.v2_0.lb.v2 import l7rule -from neutronclient.tests.unit import test_cli20 - - -"""Structure for mapping cli and api arguments - -The structure maps cli arguments and a list of its -api argument name, default cli value and default api value. -It helps to make tests more general for different argument types. -""" -args_conf = { - 'admin-state-up': ['admin_state_up', True, True], - 'admin-state-down': ['admin_state_up', None, False], - 'type': ['type', 'HOST_NAME', 'HOST_NAME'], - 'compare-type': ['compare_type', 'EQUAL_TO', 'EQUAL_TO'], - 'invert-compare': ['invert', None, True], - 'key': ['key', 'key', 'key'], - 'value': ['value', 'value', 'value']} - - -class CLITestV20LbL7RuleJSON(test_cli20.CLITestV20Base): - - def _get_test_args(self, *args, **kwargs): - """Function for generically building testing arguments""" - cli_args = [] - api_args = {} - for arg in args: - cli_args.append('--' + arg) - if not args_conf[arg][1]: - pass - elif arg in kwargs: - cli_args.append(str(kwargs[arg])) - else: - cli_args.append(args_conf[arg][1]) - - if arg in kwargs: - api_args[args_conf[arg][0]] = kwargs[arg] - else: - api_args[args_conf[arg][0]] = args_conf[arg][2] - - if 'invert' not in api_args: - api_args['invert'] = False - return cli_args, api_args - - def _test_create_rule(self, *args, **kwargs): - resource = 'rule' - cmd_resource = 'lbaas_l7rule' - cmd = l7rule.CreateL7Rule(test_cli20.MyApp(sys.stdout), None) - cli_args, api_args = self._get_test_args(*args, **kwargs) - position_names = list(api_args.keys()) - position_values = list(api_args.values()) - cli_args.append('test_policy') - self._test_create_resource(resource, cmd, None, 'test_id', - cli_args, position_names, position_values, - cmd_resource=cmd_resource, - parent_id='test_policy') - - def _test_update_rule(self, *args, **kwargs): - resource = 'rule' - cmd_resource = 'lbaas_l7rule' - cmd = l7rule.UpdateL7Rule(test_cli20.MyApp(sys.stdout), None) - cli_args, api_args = self._get_test_args(*args, **kwargs) - cli_args.append('test_id') - cli_args.append('test_policy') - self._test_update_resource(resource, cmd, 'test_id', - cli_args, api_args, - cmd_resource=cmd_resource, - parent_id='test_policy') - - def test_create_rule_with_mandatory_params(self): - # lbaas-l7rule-create with mandatory params only. - - self._test_create_rule('type', 'compare-type', - 'value') - - def test_create_disabled_rule(self): - # lbaas-l7rule-create disabled rule. - - self._test_create_rule('type', 'compare-type', - 'value', 'admin-state-down') - - def test_create_rule_with_all_params(self): - # lbaas-l7rule-create with all params set. - - self._test_create_rule('type', 'compare-type', - 'invert-compare', 'key', 'value', - type='HEADER', compare_type='CONTAINS', - key='other_key', value='other_value') - - def test_create_rule_with_inverted_compare(self): - # lbaas-l7rule-create with invertted compare type. - - self._test_create_rule('type', 'compare-type', - 'invert-compare', 'value') - - def test_list_rules(self): - # lbaas-l7rule-list. - - resources = 'rules' - cmd_resources = 'lbaas_l7rules' - cmd = l7rule.ListL7Rule(test_cli20.MyApp(sys.stdout), None) - - policy_id = 'policy_id' - self._test_list_resources(resources, cmd, True, - base_args=[policy_id], - cmd_resources=cmd_resources, - parent_id=policy_id, - query="l7policy_id=%s" % policy_id) - - def test_list_rules_pagination(self): - # lbaas-l7rule-list with pagination. - - resources = 'rules' - cmd_resources = 'lbaas_l7rules' - cmd = l7rule.ListL7Rule(test_cli20.MyApp(sys.stdout), None) - policy_id = 'policy_id' - self._test_list_resources_with_pagination( - resources, cmd, base_args=[policy_id], - cmd_resources=cmd_resources, parent_id=policy_id, - query="l7policy_id=%s" % policy_id) - - def test_list_rules_sort(self): - # lbaas-l7rule-list --sort-key id --sort-key asc. - - resources = 'rules' - cmd_resources = 'lbaas_l7rules' - cmd = l7rule.ListL7Rule(test_cli20.MyApp(sys.stdout), None) - policy_id = 'policy_id' - self._test_list_resources( - resources, cmd, True, base_args=[policy_id], - cmd_resources=cmd_resources, parent_id=policy_id, - query="l7policy_id=%s" % policy_id) - - def test_list_rules_limit(self): - # lbaas-l7rule-list -P. - - resources = 'rules' - cmd_resources = 'lbaas_l7rules' - cmd = l7rule.ListL7Rule(test_cli20.MyApp(sys.stdout), None) - policy_id = 'policy_id' - self._test_list_resources(resources, cmd, page_size=1000, - base_args=[policy_id], - cmd_resources=cmd_resources, - parent_id=policy_id, - query="l7policy_id=%s" % policy_id) - - def test_show_rule_id(self): - # lbaas-l7rule-show test_id. - - resource = 'rule' - cmd_resource = 'lbaas_l7rule' - policy_id = 'policy_id' - cmd = l7rule.ShowL7Rule(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id, policy_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id'], - cmd_resource=cmd_resource, - parent_id=policy_id) - - def test_update_rule_type(self): - # lbaas-l7rule-update test_id --type HEADER test_policy - - self._test_update_rule('type', type='HEADER') - - def test_update_rule_compare_type(self): - # lbaas-l7rule-update test_id --compare-type CONTAINS test_policy. - - self._test_update_rule('compare-type', - **{'compare-type': 'CONTAINS'}) - - def test_update_rule_inverted_compare_type(self): - # lbaas-l7rule-update test_id --invert-compare test_policy. - - self._test_update_rule('invert-compare') - - def test_update_rule_key_value(self): - # lbaas-l7rule-update test_id --key other --value other test_policy. - - self._test_update_rule('key', 'value', - key='other', value='other') - - def test_delete_rule(self): - # lbaas-l7rule-delete test_id policy_id. - - resource = 'rule' - cmd_resource = 'lbaas_l7rule' - policy_id = 'policy_id' - test_id = 'test_id' - cmd = l7rule.DeleteL7Rule(test_cli20.MyApp(sys.stdout), None) - args = [test_id, policy_id] - self._test_delete_resource(resource, cmd, test_id, args, - cmd_resource=cmd_resource, - parent_id=policy_id) diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_listener.py b/neutronclient/tests/unit/lb/v2/test_cli20_listener.py deleted file mode 100644 index 3993ea7f7..000000000 --- a/neutronclient/tests/unit/lb/v2/test_cli20_listener.py +++ /dev/null @@ -1,194 +0,0 @@ -# Copyright 2014 Blue Box Group, Inc. -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import sys - -from neutronclient.common import exceptions -from neutronclient.neutron.v2_0.lb.v2 import listener -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20LbListenerJSON(test_cli20.CLITestV20Base): - - def test_create_listener_with_loadbalancer(self): - # lbaas-listener-create with --loadbalancer - resource = 'listener' - cmd_resource = 'lbaas_listener' - cmd = listener.CreateListener(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - loadbalancer_id = 'loadbalancer' - protocol = 'TCP' - protocol_port = '80' - args = ['--protocol', protocol, '--protocol-port', protocol_port, - '--loadbalancer', loadbalancer_id] - position_names = ['protocol', 'protocol_port', 'loadbalancer_id'] - position_values = [protocol, protocol_port, loadbalancer_id, - True] - self._test_create_resource(resource, cmd, '', my_id, args, - position_names, position_values, - cmd_resource=cmd_resource) - - def test_create_listener_with_default_pool(self): - # lbaas-listener-create with --default-pool and no --loadbalancer. - resource = 'listener' - cmd_resource = 'lbaas_listener' - cmd = listener.CreateListener(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - default_pool_id = 'default-pool' - protocol = 'TCP' - protocol_port = '80' - args = ['--protocol', protocol, '--protocol-port', protocol_port, - '--default-pool', default_pool_id] - position_names = ['protocol', 'protocol_port', 'default_pool_id'] - position_values = [protocol, protocol_port, default_pool_id, - True] - self._test_create_resource(resource, cmd, '', my_id, args, - position_names, position_values, - cmd_resource=cmd_resource) - - def test_create_listener_with_no_loadbalancer_or_default_pool(self): - # lbaas-listener-create without --default-pool or --loadbalancer. - resource = 'listener' - cmd_resource = 'lbaas_listener' - cmd = listener.CreateListener(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - protocol = 'TCP' - protocol_port = '80' - args = ['--protocol', protocol, '--protocol-port', protocol_port] - position_names = ['protocol', 'protocol_port'] - position_values = [protocol, protocol_port, True] - self._test_create_resource(resource, cmd, '', my_id, args, - position_names, position_values, - cmd_resource=cmd_resource, - no_api_call=True, - expected_exception=exceptions.CommandError) - - def test_create_listener_with_all_params(self): - # lbaas-listener-create with all params set. - resource = 'listener' - cmd_resource = 'lbaas_listener' - cmd = listener.CreateListener(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - loadbalancer = 'loadbalancer' - default_pool_id = 'default-pool' - protocol = 'TCP' - protocol_port = '80' - connection_limit = 10 - def_tls_cont_ref = '11111' - args = ['--admin-state-down', - '--protocol', protocol, '--protocol-port', protocol_port, - '--loadbalancer', loadbalancer, - '--default-pool', default_pool_id, - '--default-tls-container-ref', def_tls_cont_ref, - '--sni-container-refs', '1111', '2222', '3333', - '--connection-limit', '10'] - position_names = ['admin_state_up', - 'protocol', 'protocol_port', 'loadbalancer_id', - 'default_pool_id', - 'default_tls_container_ref', 'sni_container_refs', - 'connection_limit'] - position_values = [False, protocol, protocol_port, loadbalancer, - default_pool_id, - def_tls_cont_ref, ['1111', '2222', '3333'], - connection_limit] - self._test_create_resource(resource, cmd, '', my_id, args, - position_names, position_values, - cmd_resource=cmd_resource) - - def test_list_listeners(self): - # lbaas-listener-list. - resources = 'listeners' - cmd_resources = 'lbaas_listeners' - cmd = listener.ListListener(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True, - cmd_resources=cmd_resources) - - def test_list_listeners_pagination(self): - # lbaas-listener-list with pagination. - resources = 'listeners' - cmd_resources = 'lbaas_listeners' - cmd = listener.ListListener(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd, - cmd_resources=cmd_resources) - - def test_list_listeners_sort(self): - # lbaas-listener-list --sort-key id --sort-key asc. - resources = 'listeners' - cmd_resources = 'lbaas_listeners' - cmd = listener.ListListener(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True, - cmd_resources=cmd_resources) - - def test_list_listeners_limit(self): - # lbaas-listener-list -P. - resources = 'listeners' - cmd_resources = 'lbaas_listeners' - cmd = listener.ListListener(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, page_size=1000, - cmd_resources=cmd_resources) - - def test_show_listener_id(self): - # lbaas-listener-show test_id. - resource = 'listener' - cmd_resource = 'lbaas_listener' - cmd = listener.ShowListener(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id'], - cmd_resource=cmd_resource) - - def test_show_listener_id_name(self): - # lbaas-listener-show. - resource = 'listener' - cmd_resource = 'lbaas_listener' - cmd = listener.ShowListener(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name'], - cmd_resource=cmd_resource) - - def _test_update_listener(self, args, expected_values): - resource = 'listener' - cmd_resource = 'lbaas_listener' - my_id = 'myid' - args.insert(0, my_id) - cmd = listener.UpdateListener(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, my_id, - args, expected_values, - cmd_resource=cmd_resource) - - def test_update_listener(self): - # lbaas-listener-update myid --name newname. - self._test_update_listener(['--name', 'newname'], - {'name': 'newname', }) - # lbaas-listener-update myid --description check. - self._test_update_listener(['--description', 'check'], - {'description': 'check', }) - # lbaas-listener-update myid --connection-limit -1 - self._test_update_listener(['--connection-limit', '-1'], - {'connection_limit': -1, }) - # lbaas-listener-update myid --admin-state-up False. - self._test_update_listener(['--admin-state-up', 'False'], - {'admin_state_up': 'False', }) - - def test_delete_listener(self): - # lbaas-listener-delete my-id. - resource = 'listener' - cmd_resource = 'lbaas_listener' - cmd = listener.DeleteListener(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - args = [my_id] - self._test_delete_resource(resource, cmd, my_id, args, - cmd_resource=cmd_resource) diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py b/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py deleted file mode 100644 index b62ce3435..000000000 --- a/neutronclient/tests/unit/lb/v2/test_cli20_loadbalancer.py +++ /dev/null @@ -1,215 +0,0 @@ -# Copyright 2014 Blue Box Group, Inc. -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import sys - -from mox3 import mox - -from neutronclient.neutron.v2_0.lb.v2 import loadbalancer as lb -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20LbLoadBalancerJSON(test_cli20.CLITestV20Base): - - def test_create_loadbalancer_with_mandatory_params(self): - # lbaas-loadbalancer-create with mandatory params only. - resource = 'loadbalancer' - cmd_resource = 'lbaas_loadbalancer' - cmd = lb.CreateLoadBalancer(test_cli20.MyApp(sys.stdout), None) - name = 'lbaas-loadbalancer-name' - vip_subnet_id = 'vip-subnet' - my_id = 'my-id' - args = [vip_subnet_id] - position_names = ['vip_subnet_id'] - position_values = [vip_subnet_id] - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values, - cmd_resource=cmd_resource) - - def test_create_loadbalancer_with_all_params(self): - # lbaas-loadbalancer-create with all params set. - resource = 'loadbalancer' - cmd_resource = 'lbaas_loadbalancer' - cmd = lb.CreateLoadBalancer(test_cli20.MyApp(sys.stdout), None) - name = 'lbaas-loadbalancer-name' - description = 'lbaas-loadbalancer-desc' - flavor_id = 'lbaas-loadbalancer-flavor' - vip_subnet_id = 'vip-subnet' - my_id = 'my-id' - args = ['--admin-state-down', '--description', description, - '--name', name, '--flavor', flavor_id, vip_subnet_id] - position_names = ['admin_state_up', 'description', 'name', - 'flavor_id', 'vip_subnet_id'] - position_values = [False, description, name, flavor_id, vip_subnet_id] - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values, - cmd_resource=cmd_resource) - - def test_list_loadbalancers(self): - # lbaas-loadbalancer-list. - resources = 'loadbalancers' - cmd_resources = 'lbaas_loadbalancers' - cmd = lb.ListLoadBalancer(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True, - cmd_resources=cmd_resources) - - def test_list_loadbalancers_pagination(self): - # lbaas-loadbalancer-list with pagination. - resources = 'loadbalancers' - cmd_resources = 'lbaas_loadbalancers' - cmd = lb.ListLoadBalancer(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd, - cmd_resources=cmd_resources) - - def test_list_loadbalancers_sort(self): - # lbaas-loadbalancer-list --sort-key name --sort-key id --sort-key asc - # --sort-key desc - resources = 'loadbalancers' - cmd_resources = 'lbaas_loadbalancers' - cmd = lb.ListLoadBalancer(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"], - cmd_resources=cmd_resources) - - def test_list_loadbalancers_limit(self): - # lbaas-loadbalancer-list -P. - resources = 'loadbalancers' - cmd_resources = 'lbaas_loadbalancers' - cmd = lb.ListLoadBalancer(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, page_size=1000, - cmd_resources=cmd_resources) - - def test_show_loadbalancer_id(self): - # lbaas-loadbalancer-loadbalancer-show test_id. - resource = 'loadbalancer' - cmd_resource = 'lbaas_loadbalancer' - cmd = lb.ShowLoadBalancer(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id'], - cmd_resource=cmd_resource) - - def test_show_loadbalancer_id_name(self): - # lbaas-loadbalancer-loadbalancer-show. - resource = 'loadbalancer' - cmd_resource = 'lbaas_loadbalancer' - cmd = lb.ShowLoadBalancer(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name'], - cmd_resource=cmd_resource) - - def _test_update_lb(self, args, expected_values): - resource = 'loadbalancer' - cmd_resource = 'lbaas_loadbalancer' - my_id = 'myid' - args.insert(0, my_id) - cmd = lb.UpdateLoadBalancer(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, my_id, - args, expected_values, - cmd_resource=cmd_resource) - - def test_update_loadbalancer(self): - # lbaas-loadbalancer-update myid --name newname. - self._test_update_lb(['--name', 'newname'], {'name': 'newname', }) - # lbaas-loadbalancer-update myid --description check. - self._test_update_lb(['--description', 'check'], - {'description': 'check', }) - # lbaas-loadbalancer-update myid --admin-state-up False. - self._test_update_lb(['--admin-state-up', 'False'], - {'admin_state_up': 'False', }) - - def test_delete_loadbalancer(self): - # lbaas-loadbalancer-loadbalancer-delete my-id. - resource = 'loadbalancer' - cmd_resource = 'lbaas_loadbalancer' - cmd = lb.DeleteLoadBalancer(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - args = [my_id] - self._test_delete_resource(resource, cmd, my_id, args, - cmd_resource=cmd_resource) - - def test_retrieve_loadbalancer_stats(self): - # lbaas-loadbalancer-stats test_id. - resource = 'loadbalancer' - cmd = lb.RetrieveLoadBalancerStats(test_cli20.MyApp(sys.stdout), None) - my_id = self.test_id - fields = ['bytes_in', 'bytes_out'] - args = ['--fields', 'bytes_in', '--fields', 'bytes_out', my_id] - - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) - query = "&".join(["fields=%s" % field for field in fields]) - expected_res = {'stats': {'bytes_in': '1234', 'bytes_out': '4321'}} - resstr = self.client.serialize(expected_res) - path = getattr(self.client, "lbaas_loadbalancer_path_stats") - return_tup = (test_cli20.MyResp(200), resstr) - self.client.httpclient.request( - test_cli20.end_url(path % my_id, query), 'GET', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', test_cli20.TOKEN)).AndReturn(return_tup) - self.mox.ReplayAll() - - cmd_parser = cmd.get_parser("test_" + resource) - parsed_args = cmd_parser.parse_args(args) - cmd.run(parsed_args) - - self.mox.VerifyAll() - self.mox.UnsetStubs() - _str = self.fake_stdout.make_string() - self.assertIn('bytes_in', _str) - self.assertIn('1234', _str) - self.assertIn('bytes_out', _str) - self.assertIn('4321', _str) - - def test_get_loadbalancer_statuses(self): - # lbaas-loadbalancer-status test_id. - resource = 'loadbalancer' - cmd = lb.RetrieveLoadBalancerStatus(test_cli20.MyApp(sys.stdout), None) - my_id = self.test_id - args = [my_id] - - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) - - expected_res = {'statuses': {'operating_status': 'ONLINE', - 'provisioning_status': 'ACTIVE'}} - - resstr = self.client.serialize(expected_res) - - path = getattr(self.client, "lbaas_loadbalancer_path_status") - return_tup = (test_cli20.MyResp(200), resstr) - self.client.httpclient.request( - test_cli20.end_url(path % my_id), 'GET', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', test_cli20.TOKEN)).AndReturn(return_tup) - self.mox.ReplayAll() - - cmd_parser = cmd.get_parser("test_" + resource) - parsed_args = cmd_parser.parse_args(args) - cmd.run(parsed_args) - - self.mox.VerifyAll() - self.mox.UnsetStubs() - _str = self.fake_stdout.make_string() - self.assertIn('operating_status', _str) - self.assertIn('ONLINE', _str) - self.assertIn('provisioning_status', _str) - self.assertIn('ACTIVE', _str) diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_member.py b/neutronclient/tests/unit/lb/v2/test_cli20_member.py deleted file mode 100644 index 057da4254..000000000 --- a/neutronclient/tests/unit/lb/v2/test_cli20_member.py +++ /dev/null @@ -1,171 +0,0 @@ -# Copyright 2014 Blue Box Group, Inc. -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import sys - -from neutronclient.neutron.v2_0.lb.v2 import member -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20LbMemberJSON(test_cli20.CLITestV20Base): - - def test_create_member_with_mandatory_params(self): - # lbaas-member-create with mandatory params only. - resource = 'member' - cmd_resource = 'lbaas_member' - cmd = member.CreateMember(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - address = '10.1.1.1' - protocol_port = '80' - pool_id = 'pool-id' - subnet_id = 'subnet-id' - args = ['--address', address, '--protocol-port', protocol_port, - '--subnet', subnet_id, pool_id] - position_names = ['admin_state_up', 'address', - 'protocol_port', 'subnet_id'] - position_values = [True, address, protocol_port, subnet_id] - self._test_create_resource(resource, cmd, '', my_id, args, - position_names, position_values, - cmd_resource=cmd_resource, - parent_id=pool_id) - - def test_create_member_with_all_params(self): - # lbaas-member-create with all params set. - resource = 'member' - cmd_resource = 'lbaas_member' - cmd = member.CreateMember(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - address = '10.1.1.1' - protocol_port = '80' - pool_id = 'pool-id' - subnet_id = 'subnet-id' - weight = '100' - name = 'member1' - args = ['--address', address, '--protocol-port', protocol_port, - '--subnet', subnet_id, pool_id, '--weight', weight, - '--admin-state-down', '--name', name] - position_names = ['admin_state_up', 'address', 'protocol_port', - 'subnet_id', 'weight', 'name'] - position_values = [False, address, protocol_port, - subnet_id, weight, name] - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values, - cmd_resource=cmd_resource, - parent_id=pool_id) - - def test_list_members(self): - # lbaas-member-list. - resources = 'members' - cmd_resources = 'lbaas_members' - pool_id = 'pool-id' - cmd = member.ListMember(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True, base_args=[pool_id], - cmd_resources=cmd_resources, - parent_id=pool_id, - query="pool_id=%s" % pool_id) - - def test_list_members_pagination(self): - # lbaas-member-list with pagination. - resources = 'members' - cmd_resources = 'lbaas_members' - pool_id = 'pool-id' - cmd = member.ListMember(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd, - base_args=[pool_id], - cmd_resources=cmd_resources, - parent_id=pool_id, - query="pool_id=%s" % pool_id) - - def test_list_members_sort(self): - # lbaas-member-list --sort-key id --sort-key asc. - resources = 'members' - cmd_resources = 'lbaas_members' - pool_id = 'pool-id' - cmd = member.ListMember(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True, base_args=[pool_id], - cmd_resources=cmd_resources, - parent_id=pool_id, - query="pool_id=%s" % pool_id) - - def test_list_members_limit(self): - # lbaas-member-list -P. - resources = 'members' - cmd_resources = 'lbaas_members' - pool_id = 'pool-id' - cmd = member.ListMember(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, page_size=1000, - base_args=[pool_id], - cmd_resources=cmd_resources, - parent_id=pool_id, - query="pool_id=%s" % pool_id) - - def test_show_member_id(self): - # lbaas-member-show test_id. - resource = 'member' - cmd_resource = 'lbaas_member' - pool_id = 'pool-id' - cmd = member.ShowMember(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id, pool_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id'], - cmd_resource=cmd_resource, parent_id=pool_id) - - def test_show_member_id_name(self): - # lbaas-member-show. - resource = 'member' - cmd_resource = 'lbaas_member' - pool_id = 'pool-id' - cmd = member.ShowMember(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id, pool_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name'], - cmd_resource=cmd_resource, parent_id=pool_id) - - def test_update_member(self): - # lbaas-member-update myid --name newname. - resource = 'member' - cmd_resource = 'lbaas_member' - my_id = 'my-id' - pool_id = 'pool-id' - args = [my_id, pool_id, '--name', 'newname'] - cmd = member.UpdateMember(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, my_id, args, - {'name': 'newname', }, - cmd_resource=cmd_resource, - parent_id=pool_id) - # lbaas-member-update myid --weight 100. - args = [my_id, pool_id, '--weight', '100'] - self._test_update_resource(resource, cmd, my_id, args, - {'weight': '100', }, - cmd_resource=cmd_resource, - parent_id=pool_id) - # lbaas-member-update myid --admin-state-up False - args = [my_id, pool_id, '--admin-state-up', 'False'] - self._test_update_resource(resource, cmd, my_id, args, - {'admin_state_up': 'False', }, - cmd_resource=cmd_resource, - parent_id=pool_id) - - def test_delete_member(self): - # lbaas-member-delete my-id. - resource = 'member' - cmd_resource = 'lbaas_member' - cmd = member.DeleteMember(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - pool_id = 'pool-id' - args = [my_id, pool_id] - self._test_delete_resource(resource, cmd, my_id, args, - cmd_resource=cmd_resource, - parent_id=pool_id) diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_pool.py b/neutronclient/tests/unit/lb/v2/test_cli20_pool.py deleted file mode 100644 index 7a4f7e7cb..000000000 --- a/neutronclient/tests/unit/lb/v2/test_cli20_pool.py +++ /dev/null @@ -1,191 +0,0 @@ -# Copyright 2014 Blue Box Group, Inc. -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import sys - -from neutronclient.common import exceptions -from neutronclient.neutron.v2_0.lb.v2 import pool -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20LbPoolJSON(test_cli20.CLITestV20Base): - - def test_create_pool_with_listener(self): - # lbaas-pool-create with listener - resource = 'pool' - cmd_resource = 'lbaas_pool' - cmd = pool.CreatePool(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - lb_algorithm = 'ROUND_ROBIN' - listener = 'listener' - protocol = 'TCP' - args = ['--lb-algorithm', lb_algorithm, '--protocol', protocol, - '--listener', listener] - position_names = ['admin_state_up', 'lb_algorithm', 'protocol', - 'listener_id'] - position_values = [True, lb_algorithm, protocol, listener] - self._test_create_resource(resource, cmd, '', my_id, args, - position_names, position_values, - cmd_resource=cmd_resource) - - def test_create_pool_with_loadbalancer_no_listener(self): - """lbaas-pool-create with loadbalancer, no listener.""" - resource = 'pool' - cmd_resource = 'lbaas_pool' - cmd = pool.CreatePool(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - lb_algorithm = 'ROUND_ROBIN' - loadbalancer = 'loadbalancer' - protocol = 'TCP' - args = ['--lb-algorithm', lb_algorithm, '--protocol', protocol, - '--loadbalancer', loadbalancer] - position_names = ['admin_state_up', 'lb_algorithm', 'protocol', - 'loadbalancer_id'] - position_values = [True, lb_algorithm, protocol, loadbalancer] - self._test_create_resource(resource, cmd, '', my_id, args, - position_names, position_values, - cmd_resource=cmd_resource) - - def test_create_pool_with_no_listener_or_loadbalancer(self): - """lbaas-pool-create with no listener or loadbalancer.""" - resource = 'pool' - cmd_resource = 'lbaas_pool' - cmd = pool.CreatePool(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - lb_algorithm = 'ROUND_ROBIN' - protocol = 'TCP' - args = ['--lb-algorithm', lb_algorithm, '--protocol', protocol] - position_names = ['admin_state_up', 'lb_algorithm', 'protocol'] - position_values = [True, lb_algorithm, protocol] - self._test_create_resource(resource, cmd, '', my_id, args, - position_names, position_values, - cmd_resource=cmd_resource, - no_api_call=True, - expected_exception=exceptions.CommandError) - - def test_create_pool_with_all_params(self): - # lbaas-pool-create with all params set. - resource = 'pool' - cmd_resource = 'lbaas_pool' - cmd = pool.CreatePool(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - lb_algorithm = 'ROUND_ROBIN' - listener = 'listener' - loadbalancer = 'loadbalancer' - protocol = 'TCP' - description = 'description' - session_persistence_str = 'type=APP_COOKIE,cookie_name=1234' - session_persistence = {'type': 'APP_COOKIE', - 'cookie_name': '1234'} - name = 'my-pool' - args = ['--lb-algorithm', lb_algorithm, '--protocol', protocol, - '--description', description, '--session-persistence', - session_persistence_str, '--admin-state-down', '--name', name, - '--listener', listener, '--loadbalancer', loadbalancer] - position_names = ['lb_algorithm', 'protocol', 'description', - 'session_persistence', 'admin_state_up', 'name', - 'listener_id', 'loadbalancer_id'] - position_values = [lb_algorithm, protocol, description, - session_persistence, False, name, listener, - loadbalancer] - self._test_create_resource(resource, cmd, '', my_id, args, - position_names, position_values, - cmd_resource=cmd_resource) - - def test_list_pools(self): - # lbaas-pool-list. - resources = 'pools' - cmd_resources = 'lbaas_pools' - cmd = pool.ListPool(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True, - cmd_resources=cmd_resources) - - def test_list_pools_pagination(self): - # lbaas-pool-list with pagination. - resources = 'pools' - cmd_resources = 'lbaas_pools' - cmd = pool.ListPool(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd, - cmd_resources=cmd_resources) - - def test_list_pools_sort(self): - # lbaas-pool-list --sort-key id --sort-key asc. - resources = 'pools' - cmd_resources = 'lbaas_pools' - cmd = pool.ListPool(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True, - cmd_resources=cmd_resources) - - def test_list_pools_limit(self): - # lbaas-pool-list -P. - resources = 'pools' - cmd_resources = 'lbaas_pools' - cmd = pool.ListPool(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, page_size=1000, - cmd_resources=cmd_resources) - - def test_show_pool_id(self): - # lbaas-pool-show test_id. - resource = 'pool' - cmd_resource = 'lbaas_pool' - cmd = pool.ShowPool(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id'], - cmd_resource=cmd_resource) - - def test_show_pool_id_name(self): - # lbaas-pool-show. - resource = 'pool' - cmd_resource = 'lbaas_pool' - cmd = pool.ShowPool(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name'], - cmd_resource=cmd_resource) - - def test_update_pool(self): - # lbaas-pool-update myid --name newname --description SuperPool - # --lb-algorithm SOURCE_IP --admin-state-up - # --session-persistence type=dict,type=HTTP_COOKIE,cookie_name=pie - - resource = 'pool' - cmd_resource = 'lbaas_pool' - cmd = pool.UpdatePool(test_cli20.MyApp(sys.stdout), None) - args = ['myid', '--name', 'newname', - '--description', 'SuperPool', '--lb-algorithm', "SOURCE_IP", - '--admin-state-up', 'True', - '--session-persistence', 'type=dict,' - 'type=HTTP_COOKIE,cookie_name=pie'] - body = {'name': 'newname', - "description": "SuperPool", - "lb_algorithm": "SOURCE_IP", - "admin_state_up": 'True', - 'session_persistence': { - 'type': 'HTTP_COOKIE', - 'cookie_name': 'pie', - }, } - self._test_update_resource(resource, cmd, 'myid', args, body, - cmd_resource=cmd_resource) - - def test_delete_pool(self): - # lbaas-pool-delete my-id. - resource = 'pool' - cmd_resource = 'lbaas_pool' - cmd = pool.DeletePool(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - args = [my_id] - self._test_delete_resource(resource, cmd, my_id, args, - cmd_resource=cmd_resource) diff --git a/neutronclient/tests/unit/osc/v2/dynamic_routing/fakes.py b/neutronclient/tests/unit/osc/v2/dynamic_routing/fakes.py new file mode 100644 index 000000000..188f06871 --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/dynamic_routing/fakes.py @@ -0,0 +1,145 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# 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 +import uuid + +from openstack.network.v2 import agent as _agent +from openstack.network.v2 import bgp_peer as _bgp_peer +from openstack.network.v2 import bgp_speaker as _bgp_speaker + +from neutronclient.tests.unit.osc.v2 import fakes + + +class TestNeutronDynamicRoutingOSCV2(fakes.TestNeutronClientOSCV2): + def setUp(self): + super(TestNeutronDynamicRoutingOSCV2, self).setUp() + self.neutronclient.find_resource = mock.Mock( + side_effect=lambda resource, name_or_id, project_id=None, + cmd_resource=None, parent_id=None, fields=None: + {'id': name_or_id}) + + self.networkclient.find_bgp_speaker = mock.Mock( + side_effect=lambda name_or_id, project_id=None, + cmd_resource=None, parent_id=None, fields=None, + ignore_missing=False: + _bgp_speaker.BgpSpeaker(id=name_or_id)) + self.networkclient.find_bgp_peer = mock.Mock( + side_effect=lambda name_or_id, project_id=None, + cmd_resource=None, parent_id=None, fields=None, + ignore_missing=False: + _bgp_peer.BgpPeer(id=name_or_id)) + + +class FakeBgpSpeaker(object): + """Fake one or more bgp speakers.""" + + @staticmethod + def create_one_bgp_speaker(attrs=None): + attrs = attrs or {} + # Set default attributes. + bgp_speaker_attrs = { + 'peers': [], + 'local_as': 200, + 'advertise_tenant_networks': True, + 'networks': [], + 'ip_version': 4, + 'advertise_floating_ip_host_routes': True, + 'id': uuid.uuid4().hex, + 'name': 'bgp-speaker-' + uuid.uuid4().hex, + 'tenant_id': uuid.uuid4().hex, + } + + # Overwrite default attributes. + bgp_speaker_attrs.update(attrs) + ret_bgp_speaker = _bgp_speaker.BgpSpeaker(**bgp_speaker_attrs) + + return ret_bgp_speaker + + @staticmethod + def create_bgp_speakers(attrs=None, count=1): + """Create multiple fake bgp speakers. + + """ + bgp_speakers = [] + for i in range(count): + bgp_speaker = FakeBgpSpeaker.create_one_bgp_speaker(attrs) + bgp_speakers.append(bgp_speaker) + + return bgp_speakers + + +class FakeBgpPeer(object): + """Fake one or more bgp peers.""" + + @staticmethod + def create_one_bgp_peer(attrs=None): + attrs = attrs or {} + # Set default attributes. + bgp_peer_attrs = { + 'auth_type': None, + 'peer_ip': '1.1.1.1', + 'remote_as': 100, + 'id': uuid.uuid4().hex, + 'name': 'bgp-peer-' + uuid.uuid4().hex, + 'tenant_id': uuid.uuid4().hex, + } + + # Overwrite default attributes. + bgp_peer_attrs.update(attrs) + ret_bgp_peer = _bgp_peer.BgpPeer(**bgp_peer_attrs) + + return ret_bgp_peer + + @staticmethod + def create_bgp_peers(attrs=None, count=1): + """Create one or multiple fake bgp peers.""" + bgp_peers = [] + for i in range(count): + bgp_peer = FakeBgpPeer.create_one_bgp_peer(attrs) + bgp_peers.append(bgp_peer) + + return bgp_peers + + +class FakeDRAgent(object): + """Fake one or more dynamic routing agents.""" + + @staticmethod + def create_one_dragent(attrs=None): + attrs = attrs or {} + # Set default attributes. + dragent_attrs = { + 'binary': 'neutron-bgp-dragent', + 'admin_state_up': True, + 'availability_zone': None, + 'alive': True, + 'topic': 'bgp_dragent', + 'host': 'network-' + uuid.uuid4().hex, + 'name': 'bgp-dragent-' + uuid.uuid4().hex, + 'agent_type': 'BGP dynamic routing agent', + 'id': uuid.uuid4().hex, + } + + # Overwrite default attributes. + dragent_attrs.update(attrs) + return _agent.Agent(**dragent_attrs) + + @staticmethod + def create_dragents(attrs=None, count=1): + """Create one or multiple fake dynamic routing agents.""" + agents = [] + for i in range(count): + agent = FakeDRAgent.create_one_dragent(attrs) + agents.append(agent) + + return agents diff --git a/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_dragent.py b/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_dragent.py new file mode 100644 index 000000000..6842cbbd1 --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_dragent.py @@ -0,0 +1,84 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# 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.osc.v2.dynamic_routing import bgp_dragent +from neutronclient.tests.unit.osc.v2.dynamic_routing import fakes + + +class TestAddBgpSpeakerToDRAgent(fakes.TestNeutronDynamicRoutingOSCV2): + _bgp_speaker = fakes.FakeBgpSpeaker.create_one_bgp_speaker() + _bgp_dragent = fakes.FakeDRAgent.create_one_dragent() + _bgp_speaker_id = _bgp_speaker['id'] + _bgp_dragent_id = _bgp_dragent['id'] + + def setUp(self): + super(TestAddBgpSpeakerToDRAgent, self).setUp() + + # Get the command object to test + self.cmd = bgp_dragent.AddBgpSpeakerToDRAgent(self.app, self.namespace) + + def test_add_bgp_speaker_to_dragent(self): + arglist = [ + self._bgp_dragent_id, + self._bgp_speaker_id, + ] + verifylist = [ + ('dragent_id', self._bgp_dragent_id), + ('bgp_speaker', self._bgp_speaker_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + with mock.patch.object(self.networkclient, + "add_bgp_speaker_to_dragent", + return_value=None): + + result = self.cmd.take_action(parsed_args) + self.networkclient.add_bgp_speaker_to_dragent.\ + assert_called_once_with( + self._bgp_dragent_id, self._bgp_speaker_id) + self.assertIsNone(result) + + +class TestRemoveBgpSpeakerFromDRAgent(fakes.TestNeutronDynamicRoutingOSCV2): + _bgp_speaker = fakes.FakeBgpSpeaker.create_one_bgp_speaker() + _bgp_dragent = fakes.FakeDRAgent.create_one_dragent() + _bgp_speaker_id = _bgp_speaker['id'] + _bgp_dragent_id = _bgp_dragent['id'] + + def setUp(self): + super(TestRemoveBgpSpeakerFromDRAgent, self).setUp() + + # Get the command object to test + self.cmd = bgp_dragent.RemoveBgpSpeakerFromDRAgent( + self.app, self.namespace) + + def test_remove_bgp_speaker_from_dragent(self): + arglist = [ + self._bgp_dragent_id, + self._bgp_speaker_id, + ] + verifylist = [ + ('dragent_id', self._bgp_dragent_id), + ('bgp_speaker', self._bgp_speaker_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + with mock.patch.object(self.networkclient, + "remove_bgp_speaker_from_dragent", + return_value=None): + result = self.cmd.take_action(parsed_args) + self.networkclient.remove_bgp_speaker_from_dragent.\ + assert_called_once_with(self._bgp_dragent_id, + self._bgp_speaker_id) + self.assertIsNone(result) diff --git a/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_peer.py b/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_peer.py new file mode 100644 index 000000000..73b6bf7ab --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_peer.py @@ -0,0 +1,150 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# 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.osc.v2.dynamic_routing import bgp_peer +from neutronclient.tests.unit.osc.v2.dynamic_routing import fakes + + +class TestListBgpPeer(fakes.TestNeutronDynamicRoutingOSCV2): + _bgp_peers = fakes.FakeBgpPeer.create_bgp_peers(count=1) + columns = ('ID', 'Name', 'Peer IP', 'Remote AS') + data = [] + for _bgp_peer in _bgp_peers: + data.append(( + _bgp_peer['id'], + _bgp_peer['name'], + _bgp_peer['peer_ip'], + _bgp_peer['remote_as'])) + + def setUp(self): + super(TestListBgpPeer, self).setUp() + + self.networkclient.bgp_peers = mock.Mock( + return_value=self._bgp_peers + ) + + # Get the command object to test + self.cmd = bgp_peer.ListBgpPeer(self.app, self.namespace) + + def test_bgp_peer_list(self): + parsed_args = self.check_parser(self.cmd, [], []) + + columns, data = self.cmd.take_action(parsed_args) + self.networkclient.bgp_peers.assert_called_once_with( + retrieve_all=True) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + +class TestDeleteBgpPeer(fakes.TestNeutronDynamicRoutingOSCV2): + + _bgp_peer = fakes.FakeBgpPeer.create_one_bgp_peer() + + def setUp(self): + super(TestDeleteBgpPeer, self).setUp() + + self.networkclient.delete_bgp_peer = mock.Mock(return_value=None) + + self.cmd = bgp_peer.DeleteBgpPeer(self.app, self.namespace) + + def test_delete_bgp_peer(self): + arglist = [ + self._bgp_peer['name'], + ] + verifylist = [ + ('bgp_peer', self._bgp_peer['name']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.networkclient.delete_bgp_peer.assert_called_once_with( + self._bgp_peer['name']) + self.assertIsNone(result) + + +class TestShowBgpPeer(fakes.TestNeutronDynamicRoutingOSCV2): + _one_bgp_peer = fakes.FakeBgpPeer.create_one_bgp_peer() + data = ( + _one_bgp_peer['auth_type'], + _one_bgp_peer['id'], + _one_bgp_peer['name'], + _one_bgp_peer['peer_ip'], + _one_bgp_peer['tenant_id'], + _one_bgp_peer['remote_as'], + ) + _bgp_peer = _one_bgp_peer + _bgp_peer_name = _one_bgp_peer['name'] + columns = ( + 'auth_type', + 'id', + 'name', + 'peer_ip', + 'project_id', + 'remote_as', + ) + + def setUp(self): + super(TestShowBgpPeer, self).setUp() + + self.networkclient.get_bgp_peer = mock.Mock( + return_value=self._bgp_peer + ) + # Get the command object to test + self.cmd = bgp_peer.ShowBgpPeer(self.app, self.namespace) + + def test_bgp_peer_show(self): + arglist = [ + self._bgp_peer_name, + ] + verifylist = [ + ('bgp_peer', self._bgp_peer_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + data = self.cmd.take_action(parsed_args) + self.networkclient.get_bgp_peer.assert_called_once_with( + self._bgp_peer_name) + self.assertEqual(self.columns, data[0]) + self.assertEqual(self.data, data[1]) + + +class TestSetBgpPeer(fakes.TestNeutronDynamicRoutingOSCV2): + _one_bgp_peer = fakes.FakeBgpPeer.create_one_bgp_peer() + _bgp_peer_name = _one_bgp_peer['name'] + + def setUp(self): + super(TestSetBgpPeer, self).setUp() + self.networkclient.update_bgp_peer = mock.Mock(return_value=None) + bgp_peer.get_bgp_peer_id = mock.Mock(return_value=self._bgp_peer_name) + + self.cmd = bgp_peer.SetBgpPeer(self.app, self.namespace) + + def test_set_bgp_peer(self): + arglist = [ + self._bgp_peer_name, + '--name', 'noob', + ] + verifylist = [ + ('bgp_peer', self._bgp_peer_name), + ('name', 'noob'), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + attrs = {'name': 'noob', 'password': None} + self.networkclient.update_bgp_peer.assert_called_once_with( + self._bgp_peer_name, **attrs) + self.assertIsNone(result) diff --git a/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_speaker.py b/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_speaker.py new file mode 100644 index 000000000..c5e42e588 --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/dynamic_routing/test_bgp_speaker.py @@ -0,0 +1,157 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# 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.osc.v2.dynamic_routing import bgp_speaker +from neutronclient.tests.unit.osc.v2.dynamic_routing import fakes + + +class TestListBgpSpeaker(fakes.TestNeutronDynamicRoutingOSCV2): + _bgp_speakers = fakes.FakeBgpSpeaker.create_bgp_speakers() + columns = ('ID', 'Name', 'Local AS', 'IP Version') + data = [] + for _bgp_speaker in _bgp_speakers: + data.append(( + _bgp_speaker['id'], + _bgp_speaker['name'], + _bgp_speaker['local_as'], + _bgp_speaker['ip_version'])) + + def setUp(self): + super(TestListBgpSpeaker, self).setUp() + + self.networkclient.bgp_speakers = mock.Mock( + return_value=self._bgp_speakers + ) + + # Get the command object to test + self.cmd = bgp_speaker.ListBgpSpeaker(self.app, self.namespace) + + def test_bgp_speaker_list(self): + parsed_args = self.check_parser(self.cmd, [], []) + + columns, data = self.cmd.take_action(parsed_args) + self.networkclient.bgp_speakers.assert_called_once_with( + retrieve_all=True) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + +class TestDeleteBgpSpeaker(fakes.TestNeutronDynamicRoutingOSCV2): + + _bgp_speaker = fakes.FakeBgpSpeaker.create_one_bgp_speaker() + + def setUp(self): + super(TestDeleteBgpSpeaker, self).setUp() + + self.networkclient.delete_bgp_speaker = mock.Mock(return_value=None) + + self.cmd = bgp_speaker.DeleteBgpSpeaker(self.app, self.namespace) + + def test_delete_bgp_speaker(self): + arglist = [ + self._bgp_speaker['name'], + ] + verifylist = [ + ('bgp_speaker', self._bgp_speaker['name']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.networkclient.delete_bgp_speaker.assert_called_once_with( + self._bgp_speaker['name']) + self.assertIsNone(result) + + +class TestShowBgpSpeaker(fakes.TestNeutronDynamicRoutingOSCV2): + _one_bgp_speaker = fakes.FakeBgpSpeaker.create_one_bgp_speaker() + data = ( + _one_bgp_speaker['advertise_floating_ip_host_routes'], + _one_bgp_speaker['advertise_tenant_networks'], + _one_bgp_speaker['id'], + _one_bgp_speaker['ip_version'], + _one_bgp_speaker['local_as'], + _one_bgp_speaker['name'], + _one_bgp_speaker['networks'], + _one_bgp_speaker['peers'], + _one_bgp_speaker['tenant_id'] + ) + _bgp_speaker = _one_bgp_speaker + _bgp_speaker_name = _one_bgp_speaker['name'] + columns = ( + 'advertise_floating_ip_host_routes', + 'advertise_tenant_networks', + 'id', + 'ip_version', + 'local_as', + 'name', + 'networks', + 'peers', + 'project_id' + ) + + def setUp(self): + super(TestShowBgpSpeaker, self).setUp() + + self.networkclient.get_bgp_speaker = mock.Mock( + return_value=self._bgp_speaker + ) + # Get the command object to test + self.cmd = bgp_speaker.ShowBgpSpeaker(self.app, self.namespace) + + def test_bgp_speaker_show(self): + arglist = [ + self._bgp_speaker_name, + ] + verifylist = [ + ('bgp_speaker', self._bgp_speaker_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + data = self.cmd.take_action(parsed_args) + self.networkclient.get_bgp_speaker.assert_called_once_with( + self._bgp_speaker_name) + self.assertEqual(self.columns, data[0]) + self.assertEqual(self.data, data[1]) + + +class TestSetBgpSpeaker(fakes.TestNeutronDynamicRoutingOSCV2): + _one_bgp_speaker = fakes.FakeBgpSpeaker.create_one_bgp_speaker() + _bgp_speaker_name = _one_bgp_speaker['name'] + + def setUp(self): + super(TestSetBgpSpeaker, self).setUp() + self.networkclient.update_bgp_speaker = mock.Mock( + return_value=None) + + self.cmd = bgp_speaker.SetBgpSpeaker(self.app, self.namespace) + + def test_set_bgp_speaker(self): + arglist = [ + self._bgp_speaker_name, + '--name', 'noob', + ] + verifylist = [ + ('bgp_speaker', self._bgp_speaker_name), + ('name', 'noob'), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + attrs = {'name': 'noob'} + self.networkclient.update_bgp_speaker.assert_called_once_with( + self._bgp_speaker_name, **attrs) + self.assertIsNone(result) diff --git a/neutronclient/tests/unit/osc/v2/fakes.py b/neutronclient/tests/unit/osc/v2/fakes.py index 75277a952..7fec7347c 100644 --- a/neutronclient/tests/unit/osc/v2/fakes.py +++ b/neutronclient/tests/unit/osc/v2/fakes.py @@ -12,8 +12,9 @@ # import argparse -import mock +from unittest import mock +from cliff import columns as cliff_columns from osc_lib.tests import utils @@ -25,3 +26,25 @@ def setUp(self): self.app.client_manager.session = mock.Mock() self.app.client_manager.neutronclient = mock.Mock() self.neutronclient = self.app.client_manager.neutronclient + + self.app.client_manager.network = mock.Mock() + self.networkclient = self.app.client_manager.network + + self.addCleanup(mock.patch.stopall) + + # TODO(amotoki): Move this to osc_lib + def assertListItemEqual(self, expected, actual): + self.assertEqual(len(expected), len(actual)) + for item_expected, item_actual in zip(expected, actual): + self.assertItemEqual(item_expected, item_actual) + + # TODO(amotoki): Move this to osc_lib + def assertItemEqual(self, expected, actual): + self.assertEqual(len(expected), len(actual)) + for col_expected, col_actual in zip(expected, actual): + if isinstance(col_expected, cliff_columns.FormattableColumn): + self.assertIsInstance(col_actual, col_expected.__class__) + self.assertEqual(col_expected.human_readable(), + col_actual.human_readable()) + else: + self.assertEqual(col_expected, col_actual) diff --git a/neutronclient/tests/unit/osc/v2/fwaas/__init__.py b/neutronclient/tests/unit/osc/v2/fwaas/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutronclient/tests/functional/core/__init__.py b/neutronclient/tests/unit/osc/v2/logging/__init__.py similarity index 100% rename from neutronclient/tests/functional/core/__init__.py rename to neutronclient/tests/unit/osc/v2/logging/__init__.py diff --git a/neutronclient/tests/unit/osc/v2/logging/fakes.py b/neutronclient/tests/unit/osc/v2/logging/fakes.py new file mode 100644 index 000000000..a20e3712e --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/logging/fakes.py @@ -0,0 +1,78 @@ +# Copyright 2017 FUJITSU LIMITED +# All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +import collections +import copy +from unittest import mock +import uuid + + +class FakeLogging(object): + + def create(self, attrs={}): + """Create a fake network logs + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A OrderedDict faking the network log + """ + self.ordered.update(attrs) + return copy.deepcopy(self.ordered) + + def bulk_create(self, attrs=None, count=2): + """Create multiple fake network logs + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of network logs to fake + :return: + A list of dictionaries faking the network logs + """ + return [self.create(attrs=attrs) for i in range(0, count)] + + def get(self, attrs=None, count=2): + """Create multiple fake network logs + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of network logs to fake + :return: + A list of dictionaries faking the network log + """ + if attrs is None: + self.attrs = self.bulk_create(count=count) + return mock.Mock(side_effect=attrs) + + +class NetworkLog(FakeLogging): + """Fake one or more network log""" + + def __init__(self): + super(NetworkLog, self).__init__() + self.ordered = collections.OrderedDict(( + ('id', 'log-id-' + uuid.uuid4().hex), + ('description', 'my-desc-' + uuid.uuid4().hex), + ('enabled', False), + ('name', 'my-log-' + uuid.uuid4().hex), + ('target_id', None), + ('project_id', 'project-id-' + uuid.uuid4().hex), + ('resource_id', None), + ('resource_type', 'security_group'), + ('event', 'all'), + )) diff --git a/neutronclient/tests/unit/osc/v2/logging/test_network_log.py b/neutronclient/tests/unit/osc/v2/logging/test_network_log.py new file mode 100644 index 000000000..c2e0390ae --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/logging/test_network_log.py @@ -0,0 +1,749 @@ +# Copyright 2017-2018 FUJITSU LIMITED +# All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +import copy +from unittest import mock + +from osc_lib import exceptions +from osc_lib.tests import utils +import testtools + +from neutronclient.osc import utils as osc_utils +from neutronclient.osc.v2.logging import network_log +from neutronclient.tests.unit.osc.v2 import fakes as test_fakes +from neutronclient.tests.unit.osc.v2.logging import fakes + + +_log = fakes.NetworkLog().create() +RES_TYPE_SG = 'security_group' +RES_TYPE_FWG = 'firewall_group' +CONVERT_MAP = { + 'project': 'project_id', + 'enable': 'enabled', + 'disable': 'enabled', + 'target': 'target_id', + 'resource': 'resource_id', + 'event': 'event', +} + + +def _generate_data(ordered_dict=None, data=None): + source = ordered_dict if ordered_dict else _log + if data: + source.update(data) + return tuple(source[key] for key in source) + + +def _generate_req_and_res(verifylist): + request = dict(verifylist) + response = copy.deepcopy(_log) + for key, val in verifylist: + converted = CONVERT_MAP.get(key, key) + del request[key] + if key == 'enable' and val: + new_value = True + elif key == 'disable' and val: + new_value = False + else: + new_value = val + request[converted] = new_value + response[converted] = new_value + return request, response + + +class TestNetworkLog(test_fakes.TestNeutronClientOSCV2): + + def check_results(self, headers, data, exp_req, is_list=False): + if is_list: + req_body = {'logs': [exp_req]} + else: + req_body = {'log': exp_req} + self.mocked.assert_called_once_with(req_body) + self.assertEqual(self.ordered_headers, headers) + self.assertEqual(self.ordered_data, data) + + def setUp(self): + super(TestNetworkLog, self).setUp() + self.neutronclient.find_resource = mock.Mock() + self.neutronclient.find_resource.side_effect = \ + lambda x, y, **k: {'id': y} + osc_utils.find_project = mock.Mock() + osc_utils.find_project.id = _log['project_id'] + self.res = _log + self.headers = ( + 'ID', + 'Description', + 'Enabled', + 'Name', + 'Target', + 'Project', + 'Resource', + 'Type', + 'Event', + ) + self.data = _generate_data() + self.ordered_headers = ( + 'Description', + 'Enabled', + 'Event', + 'ID', + 'Name', + 'Project', + 'Resource', + 'Target', + 'Type', + ) + self.ordered_data = ( + _log['description'], + _log['enabled'], + _log['event'], + _log['id'], + _log['name'], + _log['project_id'], + _log['resource_id'], + _log['target_id'], + _log['resource_type'], + ) + self.ordered_columns = ( + 'description', + 'enabled', + 'event', + 'id', + 'name', + 'project_id', + 'resource_id', + 'target_id', + 'resource_type', + ) + + +class TestCreateNetworkLog(TestNetworkLog): + + def setUp(self): + super(TestCreateNetworkLog, self).setUp() + self.neutronclient.create_network_log = mock.Mock( + return_value={'log': _log}) + self.mocked = self.neutronclient.create_network_log + self.cmd = network_log.CreateNetworkLog(self.app, self.namespace) + loggables = { + "loggable_resources": [{"type": RES_TYPE_SG}, + {"type": RES_TYPE_FWG}] + } + self.neutronclient.list_network_loggable_resources = mock.Mock( + return_value=loggables) + + def _update_expect_response(self, request, response): + """Set expected request and response + + :param request + A dictionary of request body(dict of verifylist) + :param response + A OrderedDict of request body + """ + # Update response body + self.neutronclient.create_network_log.return_value = \ + {'log': dict(response)} + osc_utils.find_project.return_value.id = response['project_id'] + # Update response(finally returns 'data') + self.data = _generate_data(ordered_dict=response) + self.ordered_data = tuple( + response[column] for column in self.ordered_columns + ) + + def _set_all_params(self, args={}): + name = args.get('name', 'my-log') + desc = args.get('description', 'my-description-for-log') + event = args.get('event', 'ACCEPT') + resource = args.get('resource', 'id-target-log') + target = args.get('target', 'id-target-log') + resource_type = args.get('resource_type', 'security_group') + project = args.get('project_id', 'id-my-project') + + arglist = [ + name, + '--description', desc, + '--enable', + '--target', target, + '--resource', resource, + '--event', event, + '--project', project, + '--resource-type', resource_type, + ] + verifylist = [ + ('description', desc), + ('enable', True), + ('event', event), + ('name', name), + ('target', target), + ('project', project), + ('resource', target), + ('resource_type', resource_type), + ] + return arglist, verifylist + + def _test_create_with_all_params(self, args={}): + arglist, verifylist = self._set_all_params(args) + request, response = _generate_req_and_res(verifylist) + self._update_expect_response(request, response) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.check_results(headers, data, request) + + def test_create_with_no_options_and_raise(self): + arglist = [] + verifylist = [] + + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_create_with_mandatory_params(self): + name = self.res['name'] + arglist = [ + name, + '--resource-type', RES_TYPE_SG, + ] + verifylist = [ + ('name', name), + ('resource_type', RES_TYPE_SG), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + expect = { + 'name': self.res['name'], + 'resource_type': self.res['resource_type'], + } + self.mocked.assert_called_once_with({'log': expect}) + self.assertEqual(self.ordered_headers, headers) + self.assertEqual(self.ordered_data, data) + + def test_create_with_disable(self): + name = self.res['name'] + arglist = [ + name, + '--resource-type', RES_TYPE_SG, + '--disable', + ] + verifylist = [ + ('name', name), + ('resource_type', RES_TYPE_SG), + ('disable', True), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + expect = { + 'name': self.res['name'], + 'resource_type': self.res['resource_type'], + 'enabled': False, + } + self.mocked.assert_called_once_with({'log': expect}) + self.assertEqual(self.ordered_headers, headers) + self.assertEqual(self.ordered_data, data) + + def test_create_with_all_params(self): + self._test_create_with_all_params() + + def test_create_with_all_params_event_drop(self): + self._test_create_with_all_params({'event': 'DROP'}) + + def test_create_with_all_params_event_all(self): + self._test_create_with_all_params({'event': 'ALL'}) + + def test_create_with_all_params_except_event(self): + arglist, verifylist = self._set_all_params({'event': ''}) + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_create_with_all_params_event_upper_capitalized(self): + for event in ('all', 'All', 'dROP', 'accePt', 'accept', 'drop'): + arglist, verifylist = self._set_all_params({'event': event}) + self.assertRaises( + testtools.matchers._impl.MismatchError, + self.check_parser, self.cmd, arglist, verifylist) + + def test_create_with_all_params_resource_type_upper_capitalized(self): + for res_type in ('SECURITY_GROUP', 'Security_group', 'security_Group'): + arglist, verifylist = self._set_all_params( + {'resource_type': res_type}) + self.assertRaises( + testtools.matchers._impl.MismatchError, + self.check_parser, self.cmd, arglist, verifylist) + + def test_create_with_valid_fwg_resource(self): + name = self.res['name'] + resource_id = 'valid_fwg_id' + resource_type = RES_TYPE_FWG + # Test with valid FWG ID + with mock.patch.object(self.neutronclient, 'find_resource', + return_value={'id': resource_id}): + arglist = [name, + '--resource-type', resource_type, + '--resource', resource_id + ] + verifylist = [ + ('name', name), + ('resource_type', resource_type), + ('resource', resource_id) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + expect = { + 'name': self.res['name'], + 'resource_type': RES_TYPE_FWG, + 'resource_id': 'valid_fwg_id', + } + self.neutronclient.find_resource.assert_called_with( + resource_type, + resource_id, + cmd_resource='fwaas_firewall_group') + self.mocked.assert_called_once_with({'log': expect}) + self.assertEqual(self.ordered_headers, headers) + self.assertEqual(self.ordered_data, data) + + def test_create_with_invalid_fwg_resource(self): + name = self.res['name'] + resource_id = 'invalid_fwg_id' + resource_type = RES_TYPE_FWG + # Test with invalid FWG ID + with mock.patch.object(self.neutronclient, 'find_resource', + side_effect=exceptions.NotFound(code=0)): + arglist = [name, + '--resource-type', resource_type, + '--resource', resource_id + ] + verifylist = [ + ('name', name), + ('resource_type', resource_type), + ('resource', resource_id) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises(exceptions.NotFound, + self.cmd.take_action, + parsed_args) + self.neutronclient.find_resource.assert_called_with( + resource_type, + resource_id, + cmd_resource='fwaas_firewall_group') + self.mocked.assert_not_called() + + def test_create_with_invalid_resource_type(self): + name = self.res['name'] + resource_type = 'invalid_resource_type' + resource_id = 'valid_fwg_id' + with mock.patch.object(self.neutronclient, 'find_resource', + side_effect=exceptions.NotFound(code=0)): + arglist = [name, + '--resource-type', resource_type, + '--resource', resource_id + ] + verifylist = [ + ('name', name), + ('resource_type', resource_type), + ('resource', resource_id) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises(exceptions.NotFound, + self.cmd.take_action, + parsed_args) + self.neutronclient.find_resource.assert_called_with( + resource_type, + resource_id, + cmd_resource=None) + self.mocked.assert_not_called() + + +class TestListNetworkLog(TestNetworkLog): + + def _setup_summary(self, expect=None): + event = 'Event: ' + self.res['event'].upper() + target = 'Logged: (None specified)' + if expect: + if expect.get('event'): + event = expect['event'] + if expect.get('resource'): + target = expect['resource'] + summary = ',\n'.join([event, target]) + self.short_data = ( + expect['id'] if expect else self.res['id'], + expect['enabled'] if expect else self.res['enabled'], + expect['name'] if expect else self.res['name'], + expect['resource_type'] if expect else self.res['resource_type'], + summary + ) + + def setUp(self): + super(TestListNetworkLog, self).setUp() + self.cmd = network_log.ListNetworkLog(self.app, self.namespace) + + self.short_header = ( + 'ID', + 'Enabled', + 'Name', + 'Type', + 'Summary', + ) + self._setup_summary() + self.neutronclient.list_network_logs = mock.Mock( + return_value={'logs': [self.res]}) + self.mocked = self.neutronclient.list_network_logs + + def test_list_with_long_option(self): + arglist = ['--long'] + verifylist = [('long', True)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.headers), headers) + self.assertEqual([self.data], list(data)) + + def test_list_with_no_option(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.short_header), headers) + self.assertEqual([self.short_data], list(data)) + + def test_list_with_target_and_resource(self): + arglist = [] + verifylist = [] + target_id = 'aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaaaaaaa' + resource_id = 'bbbbbbbb-bbbb-bbbb-bbbbbbbbbbbbbbbbb' + log = fakes.NetworkLog().create({ + 'target_id': target_id, + 'resource_id': resource_id}) + self.mocked.return_value = {'logs': [log]} + logged = 'Logged: (security_group) %(res_id)s on (port) %(t_id)s' % { + 'res_id': resource_id, 't_id': target_id} + expect_log = copy.deepcopy(log) + expect_log.update({ + 'resource': logged, + 'event': 'Event: ALL'}) + self._setup_summary(expect=expect_log) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.short_header), headers) + self.assertEqual([self.short_data], list(data)) + + def test_list_with_resource(self): + arglist = [] + verifylist = [] + resource_id = 'bbbbbbbb-bbbb-bbbb-bbbbbbbbbbbbbbbbb' + log = fakes.NetworkLog().create({'resource_id': resource_id}) + self.mocked.return_value = {'logs': [log]} + logged = 'Logged: (security_group) %s' % resource_id + expect_log = copy.deepcopy(log) + expect_log.update({ + 'resource': logged, + 'event': 'Event: ALL'}) + self._setup_summary(expect=expect_log) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.short_header), headers) + self.assertEqual([self.short_data], list(data)) + + def test_list_with_target(self): + arglist = [] + verifylist = [] + target_id = 'aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaaaaaaa' + log = fakes.NetworkLog().create({'target_id': target_id}) + self.mocked.return_value = {'logs': [log]} + logged = 'Logged: (port) %s' % target_id + expect_log = copy.deepcopy(log) + expect_log.update({ + 'resource': logged, + 'event': 'Event: ALL'}) + self._setup_summary(expect=expect_log) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.short_header), headers) + self.assertEqual([self.short_data], list(data)) + + +class TestShowNetworkLog(TestNetworkLog): + + def setUp(self): + super(TestShowNetworkLog, self).setUp() + self.neutronclient.show_network_log = mock.Mock( + return_value={'log': self.res}) + self.mocked = self.neutronclient.show_network_log + self.cmd = network_log.ShowNetworkLog(self.app, self.namespace) + + def test_show_filtered_by_id_or_name(self): + target = self.res['id'] + arglist = [target] + verifylist = [('network_log', target)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with(target) + self.assertEqual(self.ordered_headers, headers) + self.assertEqual(self.ordered_data, data) + + +class TestSetNetworkLog(TestNetworkLog): + + def setUp(self): + super(TestSetNetworkLog, self).setUp() + self.neutronclient.update_network_log = mock.Mock( + return_value={'log': self.res}) + self.mocked = self.neutronclient.update_network_log + self.cmd = network_log.SetNetworkLog(self.app, self.namespace) + + def test_set_name(self): + target = self.res['id'] + update = 'change' + arglist = [target, '--name', update] + verifylist = [ + ('network_log', target), + ('name', update), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {'log': {'name': update}}) + self.assertIsNone(result) + + def test_set_description(self): + target = self.res['id'] + update = 'change-desc' + arglist = [target, '--description', update] + verifylist = [ + ('network_log', target), + ('description', update), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {'log': {'description': update}}) + self.assertIsNone(result) + + def test_set_enable(self): + target = self.res['id'] + arglist = [target, '--enable'] + verifylist = [ + ('network_log', target), + ('enable', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {'log': {'enabled': True}}) + self.assertIsNone(result) + + def test_set_disable(self): + target = self.res['id'] + arglist = [target, '--disable'] + verifylist = [ + ('network_log', target), + ('disable', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, {'log': {'enabled': False}}) + self.assertIsNone(result) + + # Illegal tests + def test_illegal_set_resource_type(self): + target = self.res['id'] + resource_type = 'security_group' + arglist = [target, '--resource-type', resource_type] + verifylist = [ + ('network_log', target), + ('resource_type', resource_type), + ] + + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_illegal_set_event(self): + target = self.res['id'] + for event in ['all', 'accept', 'drop']: + arglist = [target, '--event', event] + verifylist = [ + ('network_log', target), + ('event', event), + ] + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_illegal_set_resource_id(self): + target = self.res['id'] + resource_id = 'resource-id-for-logged-target' + arglist = [target, '--resource', resource_id] + verifylist = [ + ('network_log', target), + ('resource', resource_id), + ] + + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_illegal_set_project(self): + target = self.res['id'] + arglist = [ + target, + '--project', + ] + verifylist = [ + ('network_log', target), + ('project', 'other-project'), + ] + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_illegal_set_project_domain(self): + target = self.res['id'] + arglist = [ + target, + '--project-domain', + ] + verifylist = [ + ('network_log', target), + ('project_domain', 'other-project-domain'), + ] + self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_illegal_set_and_raises(self): + self.neutronclient.update_network_log = mock.Mock( + side_effect=Exception) + target = self.res['id'] + arglist = [target, '--name', 'my-name'] + verifylist = [('network_log', target), ('name', 'my-name')] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args) + + +class TestDeleteNetworkLog(TestNetworkLog): + + def setUp(self): + super(TestDeleteNetworkLog, self).setUp() + self.neutronclient.delete_network_log = mock.Mock( + return_value={'log': self.res}) + self.mocked = self.neutronclient.delete_network_log + self.cmd = network_log.DeleteNetworkLog(self.app, self.namespace) + + def test_delete_with_one_resource(self): + target = self.res['id'] + arglist = [target] + verifylist = [('network_log', [target])] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with(target) + self.assertIsNone(result) + + def test_delete_with_multiple_resources(self): + target1 = 'target1' + target2 = 'target2' + arglist = [target1, target2] + verifylist = [('network_log', [target1, target2])] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) + + self.assertEqual(2, self.mocked.call_count) + for idx, reference in enumerate([target1, target2]): + actual = ''.join(self.mocked.call_args_list[idx][0]) + self.assertEqual(reference, actual) + + def test_delete_with_no_exist_id(self): + self.neutronclient.find_resource.side_effect = Exception + target = 'not_exist' + arglist = [target] + verifylist = [('network_log', [target])] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args) + + +class TestLoggableResource(test_fakes.TestNeutronClientOSCV2): + + def check_results(self, headers, data, exp_req, is_list=False): + if is_list: + req_body = {'logs': [exp_req]} + else: + req_body = {'log': exp_req} + self.mocked.assert_called_once_with(req_body) + self.assertEqual(self.ordered_headers, headers) + self.assertEqual(self.ordered_data, data) + + def setUp(self): + super(TestLoggableResource, self).setUp() + self.headers = ('Supported types',) + self.data = ('security_group', ) + + +class TestListLoggableResource(TestLoggableResource): + + def setUp(self): + super(TestListLoggableResource, self).setUp() + self.cmd = network_log.ListLoggableResource(self.app, self.namespace) + + loggables = { + "loggable_resources": [{"type": "security_group"}] + } + self.neutronclient.list_network_loggable_resources = mock.Mock( + return_value=loggables) + self.mocked = self.neutronclient.list_network_loggable_resources + + def test_list_with_long_option(self): + arglist = ['--long'] + verifylist = [('long', True)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.headers), headers) + self.assertEqual([self.data], list(data)) + + def test_list_with_no_option(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.headers), headers) + self.assertEqual([self.data], list(data)) diff --git a/neutronclient/tests/unit/bgp/__init__.py b/neutronclient/tests/unit/osc/v2/sfc/__init__.py similarity index 100% rename from neutronclient/tests/unit/bgp/__init__.py rename to neutronclient/tests/unit/osc/v2/sfc/__init__.py diff --git a/neutronclient/tests/unit/osc/v2/sfc/fakes.py b/neutronclient/tests/unit/osc/v2/sfc/fakes.py new file mode 100755 index 000000000..390c67e4e --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/sfc/fakes.py @@ -0,0 +1,302 @@ +# Copyright (c) 2017 Huawei Technologies India Pvt.Limited. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import argparse +from unittest import mock + + +from osc_lib.tests import utils +from oslo_utils import uuidutils + +from openstack.network.v2 import sfc_flow_classifier as flow_classifier +from openstack.network.v2 import sfc_port_chain as port_chain +from openstack.network.v2 import sfc_port_pair as port_pair +from openstack.network.v2 import sfc_port_pair_group as port_pair_group +from openstack.network.v2 import sfc_service_graph as service_graph + + +class TestNeutronClientOSCV2(utils.TestCommand): + + def setUp(self): + super(TestNeutronClientOSCV2, self).setUp() + self.namespace = argparse.Namespace() + self.app.client_manager.session = mock.Mock() + self.app.client_manager.network = mock.Mock() + self.network = self.app.client_manager.network + self.network.find_sfc_flow_classifier = mock.Mock( + side_effect=lambda name_or_id, ignore_missing=False: + {'id': name_or_id} + ) + self.network.find_sfc_port_chain = mock.Mock( + side_effect=lambda name_or_id, ignore_missing=False: + {'id': name_or_id} + ) + self.network.find_sfc_port_pair = mock.Mock( + side_effect=lambda name_or_id, ignore_missing=False: + {'id': name_or_id} + ) + self.network.find_sfc_port_pair_group = mock.Mock( + side_effect=lambda name_or_id, ignore_missing=False: + {'id': name_or_id} + ) + self.network.find_sfc_service_graph = mock.Mock( + side_effect=lambda name_or_id, ignore_missing=False: + {'id': name_or_id} + ) + self.network.find_port = mock.Mock( + side_effect=lambda name_or_id, ignore_missing=False: + {'id': name_or_id} + ) + + +class FakeSfcPortPair(object): + """Fake port pair attributes.""" + + @staticmethod + def create_port_pair(attrs=None): + """Create a fake port pair. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A Dictionary with id, name, description, ingress, egress, + service-function-parameter, project_id + """ + attrs = attrs or {} + + # Set default attributes. + port_pair_attrs = { + 'description': 'description', + 'egress': uuidutils.generate_uuid(), + 'id': uuidutils.generate_uuid(), + 'ingress': uuidutils.generate_uuid(), + 'name': 'port-pair-name', + 'service_function_parameters': [('correlation', None), + ('weight', 1)], + 'project_id': uuidutils.generate_uuid(), + } + + # Overwrite default attributes. + port_pair_attrs.update(attrs) + return port_pair.SfcPortPair(**port_pair_attrs) + + @staticmethod + def create_port_pairs(attrs=None, count=1): + """Create multiple port_pairs. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of port_pairs to fake + :return: + A list of dictionaries faking the port_pairs + """ + port_pairs = [] + for _ in range(count): + port_pairs.append(FakeSfcPortPair.create_port_pair(attrs)) + + return port_pairs + + +class FakeSfcPortPairGroup(object): + """Fake port pair group attributes.""" + + @staticmethod + def create_port_pair_group(attrs=None): + """Create a fake port pair group. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A Dictionary with id, name, description, port_pairs, group_id + port_pair_group_parameters, project_id + """ + attrs = attrs or {} + + # Set default attributes. + port_pair_group_attrs = { + 'id': uuidutils.generate_uuid(), + 'name': 'port-pair-group-name', + 'description': 'description', + 'port_pairs': uuidutils.generate_uuid(), + 'port_pair_group_parameters': {"lb_fields": []}, + 'project_id': uuidutils.generate_uuid(), + 'tap_enabled': False + } + + port_pair_group_attrs.update(attrs) + return port_pair_group.SfcPortPairGroup(**port_pair_group_attrs) + + @staticmethod + def create_port_pair_groups(attrs=None, count=1): + """Create multiple port pair groups. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of port_pair_groups to fake + :return: + A list of dictionaries faking the port pair groups + """ + port_pair_groups = [] + for _ in range(count): + port_pair_groups.append( + FakeSfcPortPairGroup.create_port_pair_group(attrs)) + + return port_pair_groups + + +class FakeSfcFlowClassifier(object): + """Fake flow classifier attributes.""" + + @staticmethod + def create_flow_classifier(attrs=None): + """Create a fake flow classifier. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A Dictionary with faking port chain attributes + """ + attrs = attrs or {} + + # Set default attributes. + flow_classifier_attrs = { + 'id': uuidutils.generate_uuid(), + 'destination_ip_prefix': '2.2.2.2/32', + 'destination_port_range_max': '90', + 'destination_port_range_min': '80', + 'ethertype': 'IPv4', + 'logical_destination_port': uuidutils.generate_uuid(), + 'logical_source_port': uuidutils.generate_uuid(), + 'name': 'flow-classifier-name', + 'description': 'fc_description', + 'protocol': 'tcp', + 'source_ip_prefix': '1.1.1.1/32', + 'source_port_range_max': '20', + 'source_port_range_min': '10', + 'project_id': uuidutils.generate_uuid(), + 'l7_parameters': {} + } + flow_classifier_attrs.update(attrs) + return flow_classifier.SfcFlowClassifier(**flow_classifier_attrs) + + @staticmethod + def create_flow_classifiers(attrs=None, count=1): + """Create multiple flow classifiers. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of flow classifiers to fake + :return: + A list of dictionaries faking the flow classifiers + """ + flow_classifiers = [] + for _ in range(count): + flow_classifiers.append( + FakeSfcFlowClassifier.create_flow_classifier(attrs)) + + return flow_classifiers + + +class FakeSfcPortChain(object): + """Fake port chain attributes.""" + + @staticmethod + def create_port_chain(attrs=None): + """Create a fake port chain. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A Dictionary with faking port chain attributes + """ + attrs = attrs or {} + + # Set default attributes. + port_chain_attrs = { + 'id': uuidutils.generate_uuid(), + 'name': 'port-chain-name', + 'description': 'description', + 'port_pair_groups': uuidutils.generate_uuid(), + 'flow_classifiers': uuidutils.generate_uuid(), + 'chain_parameters': {"correlation": "mpls", "symmetric": False}, + 'project_id': uuidutils.generate_uuid(), + } + + port_chain_attrs.update(attrs) + return port_chain.SfcPortChain(**port_chain_attrs) + + @staticmethod + def create_port_chains(attrs=None, count=1): + """Create multiple port chains. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of port chains to fake + :return: + A list of dictionaries faking the port chains. + """ + port_chains = [] + for _ in range(count): + port_chains.append(FakeSfcPortChain.create_port_chain(attrs)) + return port_chains + + +class FakeSfcServiceGraph(object): + """Fake service graph attributes.""" + + @staticmethod + def create_sfc_service_graph(attrs=None): + """Create a fake service graph. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A Dictionary with faking service graph attributes + """ + attrs = attrs or {} + + # Set default attributes. + service_graph_attrs = { + 'id': uuidutils.generate_uuid(), + 'name': 'port-pair-group-name', + 'description': 'description', + 'port_chains': {uuidutils.generate_uuid(): [ + uuidutils.generate_uuid()]}, + 'project_id': uuidutils.generate_uuid(), + } + + service_graph_attrs.update(attrs) + return service_graph.SfcServiceGraph(**service_graph_attrs) + + @staticmethod + def create_sfc_service_graphs(attrs=None, count=1): + """Create multiple service graphs. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of service graphs to fake + :return: + A list of dictionaries faking the service graphs. + """ + service_graphs = [] + for _ in range(count): + service_graphs.append( + FakeSfcServiceGraph.create_sfc_service_graph(attrs)) + return service_graphs diff --git a/neutronclient/tests/unit/osc/v2/sfc/test_flow_classifier.py b/neutronclient/tests/unit/osc/v2/sfc/test_flow_classifier.py new file mode 100755 index 000000000..ea1c4f3ab --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/sfc/test_flow_classifier.py @@ -0,0 +1,385 @@ +# Copyright (c) 2017 Huawei Technologies India Pvt.Limited. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from unittest import mock + +from osc_lib import exceptions +import testtools + +from neutronclient.osc.v2.sfc import sfc_flow_classifier +from neutronclient.tests.unit.osc.v2.sfc import fakes + + +class TestCreateSfcFlowClassifier(fakes.TestNeutronClientOSCV2): + + _fc = fakes.FakeSfcFlowClassifier.create_flow_classifier() + + columns = ('Description', + 'Destination IP', + 'Destination Port Range Max', + 'Destination Port Range Min', + 'Ethertype', + 'ID', + 'L7 Parameters', + 'Logical Destination Port', + 'Logical Source Port', + 'Name', + 'Project', + 'Protocol', + 'Source IP', + 'Source Port Range Max', + 'Source Port Range Min', + 'Summary',) + + def get_data(self): + return ( + self._fc['description'], + self._fc['destination_ip_prefix'], + self._fc['destination_port_range_max'], + self._fc['destination_port_range_min'], + self._fc['ethertype'], + self._fc['id'], + self._fc['l7_parameters'], + self._fc['logical_destination_port'], + self._fc['logical_source_port'], + self._fc['name'], + self._fc['project_id'], + self._fc['protocol'], + self._fc['source_ip_prefix'], + self._fc['source_port_range_max'], + self._fc['source_port_range_min'] + ) + + def setUp(self): + super(TestCreateSfcFlowClassifier, self).setUp() + self.network.create_sfc_flow_classifier = mock.Mock( + return_value=self._fc) + self.data = self.get_data() + + # Get the command object to test + self.cmd = sfc_flow_classifier.CreateSfcFlowClassifier(self.app, + self.namespace) + + def test_create_flow_classifier_default_options(self): + arglist = [ + "--logical-source-port", self._fc['logical_source_port'], + "--ethertype", self._fc['ethertype'], + self._fc['name'], + ] + verifylist = [ + ('logical_source_port', self._fc['logical_source_port']), + ('ethertype', self._fc['ethertype']), + ('name', self._fc['name']), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = (self.cmd.take_action(parsed_args)) + + self.network.create_sfc_flow_classifier.assert_called_once_with( + **{'name': self._fc['name'], + 'logical_source_port': self._fc['logical_source_port'], + 'ethertype': self._fc['ethertype'] + } + ) + self.assertEqual(self.columns, columns) + + def test_create_flow_classifier(self): + arglist = [ + "--description", self._fc['description'], + "--ethertype", self._fc['ethertype'], + "--protocol", self._fc['protocol'], + "--source-ip-prefix", self._fc['source_ip_prefix'], + "--destination-ip-prefix", self._fc['destination_ip_prefix'], + "--logical-source-port", self._fc['logical_source_port'], + "--logical-destination-port", self._fc['logical_destination_port'], + self._fc['name'], + "--l7-parameters", 'url=path', + ] + + param = 'url=path' + + verifylist = [ + ('description', self._fc['description']), + ('name', self._fc['name']), + ('ethertype', self._fc['ethertype']), + ('protocol', self._fc['protocol']), + ('source_ip_prefix', self._fc['source_ip_prefix']), + ('destination_ip_prefix', self._fc['destination_ip_prefix']), + ('logical_source_port', self._fc['logical_source_port']), + ('logical_destination_port', + self._fc['logical_destination_port']), + ('l7_parameters', param) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = (self.cmd.take_action(parsed_args)) + self.network.create_sfc_flow_classifier.assert_called_once_with( + **{ + 'name': self._fc['name'], + 'description': self._fc['description'], + 'ethertype': self._fc['ethertype'], + 'protocol': self._fc['protocol'], + 'source_ip_prefix': self._fc['source_ip_prefix'], + 'destination_ip_prefix': self._fc['destination_ip_prefix'], + 'logical_source_port': self._fc['logical_source_port'], + 'logical_destination_port': + self._fc['logical_destination_port'], + 'l7_parameters': param + } + ) + self.assertEqual(self.columns, columns) + + +class TestDeleteSfcFlowClassifier(fakes.TestNeutronClientOSCV2): + + _flow_classifier = \ + fakes.FakeSfcFlowClassifier.create_flow_classifiers(count=1) + + def setUp(self): + super(TestDeleteSfcFlowClassifier, self).setUp() + self.network.delete_sfc_flow_classifier = mock.Mock( + return_value=None) + self.cmd = sfc_flow_classifier.DeleteSfcFlowClassifier(self.app, + self.namespace) + + def test_delete_flow_classifier(self): + client = self.app.client_manager.network + mock_flow_classifier_delete = client.delete_sfc_flow_classifier + arglist = [ + self._flow_classifier[0]['id'], + ] + verifylist = [ + ('flow_classifier', [self._flow_classifier[0]['id']]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + mock_flow_classifier_delete.assert_called_once_with( + self._flow_classifier[0]['id']) + self.assertIsNone(result) + + def test_delete_multiple_flow_classifiers_with_exception(self): + client = self.app.client_manager.network + target1 = self._flow_classifier[0]['id'] + arglist = [target1] + verifylist = [('flow_classifier', [target1])] + + client.find_sfc_flow_classifier.side_effect = [ + target1, exceptions.CommandError + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + msg = "1 of 2 flow classifier(s) failed to delete." + with testtools.ExpectedException(exceptions.CommandError) as e: + self.cmd.take_action(parsed_args) + self.assertEqual(msg, str(e)) + + +class TestSetSfcFlowClassifier(fakes.TestNeutronClientOSCV2): + _flow_classifier = fakes.FakeSfcFlowClassifier.create_flow_classifier() + _flow_classifier_name = _flow_classifier['name'] + _flow_classifier_id = _flow_classifier['id'] + + def setUp(self): + super(TestSetSfcFlowClassifier, self).setUp() + self.network.update_sfc_flow_classifier = mock.Mock( + return_value=None) + self.cmd = sfc_flow_classifier.SetSfcFlowClassifier(self.app, + self.namespace) + + def test_set_flow_classifier(self): + client = self.app.client_manager.network + mock_flow_classifier_update = client.update_sfc_flow_classifier + arglist = [ + self._flow_classifier_name, + '--name', 'name_updated', + '--description', 'desc_updated' + ] + verifylist = [ + ('flow_classifier', self._flow_classifier_name), + ('name', 'name_updated'), + ('description', 'desc_updated'), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + attrs = { + 'name': 'name_updated', + 'description': 'desc_updated'} + mock_flow_classifier_update.assert_called_once_with( + self._flow_classifier_name, **attrs) + self.assertIsNone(result) + + +class TestShowSfcFlowClassifier(fakes.TestNeutronClientOSCV2): + + _fc = fakes.FakeSfcFlowClassifier.create_flow_classifier() + data = ( + _fc['description'], + _fc['destination_ip_prefix'], + _fc['destination_port_range_max'], + _fc['destination_port_range_min'], + _fc['ethertype'], + _fc['id'], + _fc['l7_parameters'], + _fc['logical_destination_port'], + _fc['logical_source_port'], + _fc['name'], + _fc['project_id'], + _fc['protocol'], + _fc['source_ip_prefix'], + _fc['source_port_range_max'], + _fc['source_port_range_min'] + ) + _flow_classifier = _fc + _flow_classifier_id = _fc['id'] + columns = ('Description', + 'Destination IP', + 'Destination Port Range Max', + 'Destination Port Range Min', + 'Ethertype', + 'ID', + 'L7 Parameters', + 'Logical Destination Port', + 'Logical Source Port', + 'Name', + 'Project', + 'Protocol', + 'Source IP', + 'Source Port Range Max', + 'Source Port Range Min', + 'Summary',) + + def setUp(self): + super(TestShowSfcFlowClassifier, self).setUp() + self.network.get_sfc_flow_classifier = mock.Mock( + return_value=self._flow_classifier + ) + # Get the command object to test + self.cmd = sfc_flow_classifier.ShowSfcFlowClassifier(self.app, + self.namespace) + + def test_show_flow_classifier(self): + client = self.app.client_manager.network + mock_flow_classifier_show = client.get_sfc_flow_classifier + arglist = [ + self._flow_classifier_id, + ] + verifylist = [ + ('flow_classifier', self._flow_classifier_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + mock_flow_classifier_show.assert_called_once_with( + self._flow_classifier_id) + self.assertEqual(self.columns, columns) + + +class TestListSfcFlowClassifier(fakes.TestNeutronClientOSCV2): + + _fc = fakes.FakeSfcFlowClassifier.create_flow_classifiers(count=1) + + columns = ('ID', 'Name', 'Summary') + + columns_long = ('ID', 'Name', 'Protocol', 'Ethertype', 'Source IP', + 'Destination IP', 'Logical Source Port', + 'Logical Destination Port', 'Source Port Range Min', + 'Source Port Range Max', 'Destination Port Range Min', + 'Destination Port Range Max', 'L7 Parameters', + 'Description', 'Project') + _flow_classifier = _fc[0] + data = [ + _flow_classifier['id'], + _flow_classifier['name'], + _flow_classifier['protocol'], + _flow_classifier['source_ip_prefix'], + _flow_classifier['destination_ip_prefix'], + _flow_classifier['logical_source_port'], + _flow_classifier['logical_destination_port'] + ] + data_long = [ + _flow_classifier['id'], + _flow_classifier['name'], + _flow_classifier['protocol'], + _flow_classifier['ethertype'], + _flow_classifier['source_ip_prefix'], + _flow_classifier['destination_ip_prefix'], + _flow_classifier['logical_source_port'], + _flow_classifier['logical_destination_port'], + _flow_classifier['source_port_range_min'], + _flow_classifier['source_port_range_max'], + _flow_classifier['destination_port_range_min'], + _flow_classifier['destination_port_range_max'], + _flow_classifier['l7_parameters'], + _flow_classifier['description'] + ] + + _flow_classifier1 = {'flow_classifiers': _flow_classifier} + _flow_classifier_id = _flow_classifier['id'] + + def setUp(self): + super(TestListSfcFlowClassifier, self).setUp() + self.network.sfc_flow_classifiers = mock.Mock( + return_value=self._fc + ) + # Get the command object to test + self.cmd = sfc_flow_classifier.ListSfcFlowClassifier(self.app, + self.namespace) + + def test_list_flow_classifiers(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns = self.cmd.take_action(parsed_args) + fcs = self.network.sfc_flow_classifiers() + fc = fcs[0] + data = [ + fc['id'], + fc['name'], + fc['protocol'], + fc['source_ip_prefix'], + fc['destination_ip_prefix'], + fc['logical_source_port'], + fc['logical_destination_port'] + ] + self.assertEqual(list(self.columns), columns[0]) + self.assertEqual(self.data, data) + + def test_list_with_long_option(self): + arglist = ['--long'] + verifylist = [('long', True)] + fcs = self.network.sfc_flow_classifiers() + fc = fcs[0] + data = [ + fc['id'], + fc['name'], + fc['protocol'], + fc['ethertype'], + fc['source_ip_prefix'], + fc['destination_ip_prefix'], + fc['logical_source_port'], + fc['logical_destination_port'], + fc['source_port_range_min'], + fc['source_port_range_max'], + fc['destination_port_range_min'], + fc['destination_port_range_max'], + fc['l7_parameters'], + fc['description'] + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns_long = self.cmd.take_action(parsed_args)[0] + self.assertEqual(list(self.columns_long), columns_long) + self.assertEqual(self.data_long, data) diff --git a/neutronclient/tests/unit/osc/v2/sfc/test_port_chain.py b/neutronclient/tests/unit/osc/v2/sfc/test_port_chain.py new file mode 100755 index 000000000..15f7a431e --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/sfc/test_port_chain.py @@ -0,0 +1,499 @@ +# Copyright (c) 2017 Huawei Technologies India Pvt.Limited. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from unittest import mock + +from osc_lib import exceptions +import testtools + +from neutronclient.osc.v2.sfc import sfc_port_chain +from neutronclient.tests.unit.osc.v2.sfc import fakes + + +def _get_id(client, id_or_name, resource): + return id_or_name + + +class TestCreateSfcPortChain(fakes.TestNeutronClientOSCV2): + # The new port_chain created + _port_chain = fakes.FakeSfcPortChain.create_port_chain() + + columns = ('Chain Parameters', + 'Description', + 'Flow Classifiers', + 'ID', + 'Name', + 'Port Pair Groups', + 'Project') + + def get_data(self): + return ( + self._port_chain['chain_parameters'], + self._port_chain['description'], + self._port_chain['flow_classifiers'], + self._port_chain['id'], + self._port_chain['name'], + self._port_chain['port_pair_groups'], + self._port_chain['project_id'], + ) + + def setUp(self): + super(TestCreateSfcPortChain, self).setUp() + self.network.create_sfc_port_chain = mock.Mock( + return_value=self._port_chain) + self.data = self.get_data() + + # Get the command object to test + self.cmd = sfc_port_chain.CreateSfcPortChain(self.app, self.namespace) + + def test_create_port_chain_default_options(self): + arglist = [ + self._port_chain['name'], + "--port-pair-group", self._port_chain['port_pair_groups'] + ] + verifylist = [ + ('name', self._port_chain['name']), + ('port_pair_groups', [self._port_chain['port_pair_groups']]), + ('flow_classifiers', []), + ('chain_parameters', None), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = (self.cmd.take_action(parsed_args)) + + self.network.create_sfc_port_chain.assert_called_once_with( + **{ + 'name': self._port_chain['name'], + 'port_pair_groups': [self._port_chain['port_pair_groups']] + } + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_create_port_chain_all_options(self): + arglist = [ + "--description", self._port_chain['description'], + "--port-pair-group", self._port_chain['port_pair_groups'], + self._port_chain['name'], + "--flow-classifier", self._port_chain['flow_classifiers'], + "--chain-parameters", 'correlation=mpls,symmetric=true', + ] + + cp = {'correlation': 'mpls', 'symmetric': 'true'} + + verifylist = [ + ('port_pair_groups', [self._port_chain['port_pair_groups']]), + ('name', self._port_chain['name']), + ('description', self._port_chain['description']), + ('flow_classifiers', [self._port_chain['flow_classifiers']]), + ('chain_parameters', [cp]) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = (self.cmd.take_action(parsed_args)) + + self.network.create_sfc_port_chain.assert_called_once_with( + **{ + 'name': self._port_chain['name'], + 'port_pair_groups': [self._port_chain['port_pair_groups']], + 'description': self._port_chain['description'], + 'flow_classifiers': [self._port_chain['flow_classifiers']], + 'chain_parameters': cp + } + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestDeleteSfcPortChain(fakes.TestNeutronClientOSCV2): + + _port_chain = fakes.FakeSfcPortChain.create_port_chains(count=1) + + def setUp(self): + super(TestDeleteSfcPortChain, self).setUp() + self.network.delete_sfc_port_chain = mock.Mock(return_value=None) + self.cmd = sfc_port_chain.DeleteSfcPortChain(self.app, self.namespace) + + def test_delete_port_chain(self): + client = self.app.client_manager.network + mock_port_chain_delete = client.delete_sfc_port_chain + arglist = [ + self._port_chain[0]['id'], + ] + verifylist = [ + ('port_chain', [self._port_chain[0]['id']]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + mock_port_chain_delete.assert_called_once_with( + self._port_chain[0]['id']) + self.assertIsNone(result) + + def test_delete_multiple_port_chains_with_exception(self): + client = self.app.client_manager.network + target1 = self._port_chain[0]['id'] + arglist = [target1] + verifylist = [('port_chain', [target1])] + + client.find_sfc_port_chain.side_effect = [ + target1, exceptions.CommandError + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + msg = "1 of 2 port chain(s) failed to delete." + with testtools.ExpectedException(exceptions.CommandError) as e: + self.cmd.take_action(parsed_args) + self.assertEqual(msg, str(e)) + + +class TestListSfcPortChain(fakes.TestNeutronClientOSCV2): + _port_chains = fakes.FakeSfcPortChain.create_port_chains(count=1) + columns = ('ID', 'Name', 'Port Pair Groups', 'Flow Classifiers', + 'Chain Parameters') + columns_long = ('ID', 'Name', 'Port Pair Groups', 'Flow Classifiers', + 'Chain Parameters', 'Description', 'Project') + _port_chain = _port_chains[0] + data = [ + _port_chain['id'], + _port_chain['name'], + _port_chain['port_pair_groups'], + _port_chain['flow_classifiers'], + _port_chain['chain_parameters'], + ] + data_long = [ + _port_chain['id'], + _port_chain['name'], + _port_chain['project_id'], + _port_chain['port_pair_groups'], + _port_chain['flow_classifiers'], + _port_chain['chain_parameters'], + _port_chain['description'] + ] + _port_chain1 = {'port_chains': _port_chain} + _port_chain_id = _port_chain['id'] + + def setUp(self): + super(TestListSfcPortChain, self).setUp() + self.network.sfc_port_chains = mock.Mock( + return_value=self._port_chains + ) + # Get the command object to test + self.cmd = sfc_port_chain.ListSfcPortChain(self.app, self.namespace) + + def test_list_port_chains(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns = self.cmd.take_action(parsed_args)[0] + pcs = self.network.sfc_port_chains() + pc = pcs[0] + data = [ + pc['id'], + pc['name'], + pc['port_pair_groups'], + pc['flow_classifiers'], + pc['chain_parameters'], + ] + self.assertEqual(list(self.columns), columns) + self.assertEqual(self.data, data) + + def test_list_port_chain_with_long_opion(self): + arglist = ['--long'] + verifylist = [('long', True)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns = self.cmd.take_action(parsed_args)[0] + pcs = self.network.sfc_port_chains() + pc = pcs[0] + data = [ + pc['id'], + pc['name'], + pc['project_id'], + pc['port_pair_groups'], + pc['flow_classifiers'], + pc['chain_parameters'], + pc['description'] + ] + self.assertEqual(list(self.columns_long), columns) + self.assertEqual(self.data_long, data) + + +class TestSetSfcPortChain(fakes.TestNeutronClientOSCV2): + _port_chain = fakes.FakeSfcPortChain.create_port_chain() + resource = _port_chain + res = 'port_chain' + _port_chain_name = _port_chain['name'] + _port_chain_id = _port_chain['id'] + pc_ppg = _port_chain['port_pair_groups'] + pc_fc = _port_chain['flow_classifiers'] + + def setUp(self): + super(TestSetSfcPortChain, self).setUp() + self.mocked = self.network.update_sfc_port_chain + self.cmd = sfc_port_chain.SetSfcPortChain(self.app, self.namespace) + + def test_set_port_chain(self): + client = self.app.client_manager.network + mock_port_chain_update = client.update_sfc_port_chain + arglist = [ + self._port_chain_name, + '--name', 'name_updated', + '--description', 'desc_updated', + ] + verifylist = [ + ('port_chain', self._port_chain_name), + ('name', 'name_updated'), + ('description', 'desc_updated'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + attrs = {'name': 'name_updated', 'description': 'desc_updated'} + mock_port_chain_update.assert_called_once_with(self._port_chain_name, + **attrs) + self.assertIsNone(result) + + def test_set_flow_classifiers(self): + target = self.resource['id'] + fc1 = 'flow_classifier1' + fc2 = 'flow_classifier2' + + self.network.find_sfc_port_chain = mock.Mock( + side_effect=lambda name_or_id, ignore_missing=False: + {'id': name_or_id, 'flow_classifiers': [self.pc_fc]} + ) + self.network.find_sfc_flow_classifier.side_effect = \ + lambda name_or_id, ignore_missing=False: {'id': name_or_id} + arglist = [ + target, + '--flow-classifier', fc1, + '--flow-classifier', fc2, + ] + verifylist = [ + (self.res, target), + ('flow_classifiers', [fc1, fc2]) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + expect = {'flow_classifiers': [self.pc_fc, fc1, fc2]} + self.mocked.assert_called_once_with(target, **expect) + self.assertIsNone(result) + + def test_set_no_flow_classifier(self): + client = self.app.client_manager.network + mock_port_chain_update = client.update_sfc_port_chain + arglist = [ + self._port_chain_name, + '--no-flow-classifier', + ] + verifylist = [ + ('port_chain', self._port_chain_name), + ('no_flow_classifier', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + attrs = {'flow_classifiers': []} + mock_port_chain_update.assert_called_once_with(self._port_chain_name, + **attrs) + self.assertIsNone(result) + + def test_set_port_pair_groups(self): + target = self.resource['id'] + existing_ppg = self.pc_ppg + ppg1 = 'port_pair_group1' + ppg2 = 'port_pair_group2' + + self.network.find_sfc_port_chain = mock.Mock( + side_effect=lambda name_or_id, ignore_missing=False: + {'id': name_or_id, 'port_pair_groups': [self.pc_ppg]} + ) + arglist = [ + target, + '--port-pair-group', ppg1, + '--port-pair-group', ppg2, + ] + verifylist = [ + (self.res, target), + ('port_pair_groups', [ppg1, ppg2]) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + expect = {'port_pair_groups': [existing_ppg, ppg1, ppg2]} + self.mocked.assert_called_once_with(target, **expect) + self.assertIsNone(result) + + def test_set_no_port_pair_group(self): + target = self.resource['id'] + ppg1 = 'port_pair_group1' + + arglist = [ + target, + '--no-port-pair-group', + '--port-pair-group', ppg1, + ] + verifylist = [ + (self.res, target), + ('no_port_pair_group', True), + ('port_pair_groups', [ppg1]) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + expect = {'port_pair_groups': [ppg1]} + self.mocked.assert_called_once_with(target, **expect) + self.assertIsNone(result) + + def test_set_only_no_port_pair_group(self): + target = self.resource['id'] + arglist = [ + target, + '--no-port-pair-group', + ] + verifylist = [ + (self.res, target), + ('no_port_pair_group', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args) + + +class TestShowSfcPortChain(fakes.TestNeutronClientOSCV2): + + _pc = fakes.FakeSfcPortChain.create_port_chain() + data = ( + _pc['chain_parameters'], + _pc['description'], + _pc['flow_classifiers'], + _pc['id'], + _pc['name'], + _pc['port_pair_groups'], + _pc['project_id'] + ) + _port_chain = _pc + _port_chain_id = _pc['id'] + columns = ('Chain Parameters', + 'Description', + 'Flow Classifiers', + 'ID', + 'Name', + 'Port Pair Groups', + 'Project') + + def setUp(self): + super(TestShowSfcPortChain, self).setUp() + self.network.get_sfc_port_chain = mock.Mock( + return_value=self._port_chain + ) + # Get the command object to test + self.cmd = sfc_port_chain.ShowSfcPortChain(self.app, self.namespace) + + def test_show_port_chain(self): + client = self.app.client_manager.network + mock_port_chain_show = client.get_sfc_port_chain + arglist = [ + self._port_chain_id, + ] + verifylist = [ + ('port_chain', self._port_chain_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + mock_port_chain_show.assert_called_once_with(self._port_chain_id) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestUnsetSfcPortChain(fakes.TestNeutronClientOSCV2): + _port_chain = fakes.FakeSfcPortChain.create_port_chain() + resource = _port_chain + res = 'port_chain' + _port_chain_name = _port_chain['name'] + _port_chain_id = _port_chain['id'] + pc_ppg = _port_chain['port_pair_groups'] + pc_fc = _port_chain['flow_classifiers'] + + def setUp(self): + super(TestUnsetSfcPortChain, self).setUp() + self.network.update_sfc_port_chain = mock.Mock( + return_value=None) + self.mocked = self.network.update_sfc_port_chain + self.cmd = sfc_port_chain.UnsetSfcPortChain(self.app, self.namespace) + + def test_unset_port_pair_group(self): + target = self.resource['id'] + ppg1 = 'port_pair_group1' + + self.network.find_sfc_port_chain = mock.Mock( + side_effect=lambda name_or_id, ignore_missing=False: + {'id': name_or_id, 'port_pair_groups': [self.pc_ppg]} + ) + self.network.find_sfc_port_pair_group.side_effect = \ + lambda name_or_id, ignore_missing=False: {'id': name_or_id} + + arglist = [ + target, + '--port-pair-group', ppg1, + ] + verifylist = [ + (self.res, target), + ('port_pair_groups', [ppg1]) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + expect = {'port_pair_groups': [self.pc_ppg]} + self.mocked.assert_called_once_with(target, **expect) + self.assertIsNone(result) + + def test_unset_flow_classifier(self): + target = self.resource['id'] + fc1 = 'flow_classifier1' + self.network.find_sfc_port_chain = mock.Mock( + side_effect=lambda name_or_id, ignore_missing=False: + {'id': name_or_id, 'flow_classifiers': [self.pc_fc]} + ) + self.network.find_sfc_flow_classifier.side_effect = \ + lambda name_or_id, ignore_missing=False: {'id': name_or_id} + + arglist = [ + target, + '--flow-classifier', fc1, + ] + verifylist = [ + (self.res, target), + ('flow_classifiers', [fc1]) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + expect = {'flow_classifiers': [self.pc_fc]} + self.mocked.assert_called_once_with(target, **expect) + self.assertIsNone(result) + + def test_unset_all_flow_classifier(self): + client = self.app.client_manager.network + target = self.resource['id'] + mock_port_chain_update = client.update_sfc_port_chain + arglist = [ + target, + '--all-flow-classifier', + ] + verifylist = [ + (self.res, target), + ('all_flow_classifier', True) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + expect = {'flow_classifiers': []} + mock_port_chain_update.assert_called_once_with(target, + **expect) + self.assertIsNone(result) diff --git a/neutronclient/tests/unit/osc/v2/sfc/test_port_pair.py b/neutronclient/tests/unit/osc/v2/sfc/test_port_pair.py new file mode 100755 index 000000000..e0610a360 --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/sfc/test_port_pair.py @@ -0,0 +1,313 @@ +# Copyright (c) 2017 Huawei Technologies India Pvt.Limited. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from unittest import mock + +from osc_lib import exceptions +import testtools + +from neutronclient.osc.v2.sfc import sfc_port_pair +from neutronclient.tests.unit.osc.v2.sfc import fakes + + +class TestCreateSfcPortPair(fakes.TestNeutronClientOSCV2): + # The new port_pair created + _port_pair = fakes.FakeSfcPortPair.create_port_pair() + + columns = ('Description', + 'Egress Logical Port', + 'ID', + 'Ingress Logical Port', + 'Name', + 'Project', + 'Service Function Parameters') + + def get_data(self): + return ( + self._port_pair['description'], + self._port_pair['egress'], + self._port_pair['id'], + self._port_pair['ingress'], + self._port_pair['name'], + self._port_pair['project_id'], + self._port_pair['service_function_parameters'] + ) + + def setUp(self): + super(TestCreateSfcPortPair, self).setUp() + self.network.create_sfc_port_pair = mock.Mock( + return_value=self._port_pair) + self.data = self.get_data() + + # Get the command object to test + self.cmd = sfc_port_pair.CreateSfcPortPair(self.app, self.namespace) + + def test_create_port_pair_default_options(self): + arglist = [ + "--ingress", self._port_pair['ingress'], + "--egress", self._port_pair['egress'], + self._port_pair['name'], + ] + verifylist = [ + ('ingress', self._port_pair['ingress']), + ('egress', self._port_pair['egress']), + ('name', self._port_pair['name']) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = (self.cmd.take_action(parsed_args)) + + self.network.create_sfc_port_pair.assert_called_once_with( + **{'name': self._port_pair['name'], + 'ingress': self._port_pair['ingress'], + 'egress': self._port_pair['egress']} + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def _test_create_port_pair_all_options(self, correlation): + arglist = [ + "--description", self._port_pair['description'], + "--egress", self._port_pair['egress'], + "--ingress", self._port_pair['ingress'], + self._port_pair['name'], + "--service-function-parameters", + 'correlation=%s,weight=1' % correlation, + ] + + verifylist = [ + ('ingress', self._port_pair['ingress']), + ('egress', self._port_pair['egress']), + ('name', self._port_pair['name']), + ('description', self._port_pair['description']), + ('service_function_parameters', + [{'correlation': correlation, 'weight': '1'}]) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = (self.cmd.take_action(parsed_args)) + + if correlation == "None": + correlation_param = None + else: + correlation_param = correlation + self.network.create_sfc_port_pair.assert_called_once_with( + **{'name': self._port_pair['name'], + 'ingress': self._port_pair['ingress'], + 'egress': self._port_pair['egress'], + 'description': self._port_pair['description'], + 'service_function_parameters': + {'correlation': correlation_param, 'weight': '1'}} + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_create_port_pair_all_options(self): + self._test_create_port_pair_all_options('None') + + def test_create_port_pair_all_options_mpls(self): + self._test_create_port_pair_all_options('mpls') + + +class TestDeleteSfcPortPair(fakes.TestNeutronClientOSCV2): + + _port_pair = fakes.FakeSfcPortPair.create_port_pairs(count=1) + + def setUp(self): + super(TestDeleteSfcPortPair, self).setUp() + self.network.delete_sfc_port_pair = mock.Mock(return_value=None) + self.cmd = sfc_port_pair.DeleteSfcPortPair(self.app, self.namespace) + + def test_delete_port_pair(self): + client = self.app.client_manager.network + mock_port_pair_delete = client.delete_sfc_port_pair + arglist = [ + self._port_pair[0]['id'], + ] + verifylist = [ + ('port_pair', [self._port_pair[0]['id']]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + mock_port_pair_delete.assert_called_once_with( + self._port_pair[0]['id']) + self.assertIsNone(result) + + def test_delete_multiple_port_pairs_with_exception(self): + client = self.app.client_manager.network + target1 = self._port_pair[0]['id'] + arglist = [target1] + verifylist = [('port_pair', [target1])] + + client.find_sfc_port_pair.side_effect = [ + target1, exceptions.CommandError + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + msg = "1 of 2 port pair(s) failed to delete." + with testtools.ExpectedException(exceptions.CommandError) as e: + self.cmd.take_action(parsed_args) + self.assertEqual(msg, str(e)) + + +class TestListSfcPortPair(fakes.TestNeutronClientOSCV2): + _port_pairs = fakes.FakeSfcPortPair.create_port_pairs() + columns = ('ID', 'Name', 'Ingress Logical Port', 'Egress Logical Port') + columns_long = ('ID', 'Name', 'Ingress Logical Port', + 'Egress Logical Port', 'Service Function Parameters', + 'Description', 'Project') + _port_pair = _port_pairs[0] + data = [ + _port_pair['id'], + _port_pair['name'], + _port_pair['ingress'], + _port_pair['egress'] + ] + data_long = [ + _port_pair['id'], + _port_pair['name'], + _port_pair['ingress'], + _port_pair['egress'], + _port_pair['service_function_parameters'], + _port_pair['description'] + ] + _port_pair1 = {'port_pairs': _port_pair} + _port_pair_id = _port_pair['id'], + + def setUp(self): + super(TestListSfcPortPair, self).setUp() + self.network.sfc_port_pairs = mock.Mock( + return_value=self._port_pairs) + # Get the command object to test + self.cmd = sfc_port_pair.ListSfcPortPair(self.app, self.namespace) + + def test_list_port_pairs(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns = self.cmd.take_action(parsed_args)[0] + port_pairs = self.network.sfc_port_pairs() + port_pair = port_pairs[0] + data = [ + port_pair['id'], + port_pair['name'], + port_pair['ingress'], + port_pair['egress'] + ] + self.assertEqual(list(self.columns), columns) + self.assertEqual(self.data, data) + + def test_list_with_long_option(self): + arglist = ['--long'] + verifylist = [('long', True)] + port_pairs = self.network.sfc_port_pairs() + port_pair = port_pairs[0] + data = [ + port_pair['id'], + port_pair['name'], + port_pair['ingress'], + port_pair['egress'], + port_pair['service_function_parameters'], + port_pair['description'] + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns_long = self.cmd.take_action(parsed_args)[0] + self.assertEqual(list(self.columns_long), columns_long) + self.assertEqual(self.data_long, data) + + +class TestSetSfcPortPair(fakes.TestNeutronClientOSCV2): + _port_pair = fakes.FakeSfcPortPair.create_port_pair() + _port_pair_name = _port_pair['name'] + _port_pair_id = _port_pair['id'] + + def setUp(self): + super(TestSetSfcPortPair, self).setUp() + self.network.update_sfc_port_pair = mock.Mock(return_value=None) + self.cmd = sfc_port_pair.SetSfcPortPair(self.app, self.namespace) + + def test_set_port_pair(self): + client = self.app.client_manager.network + mock_port_pair_update = client.update_sfc_port_pair + arglist = [ + self._port_pair_name, + '--name', 'name_updated', + '--description', 'desc_updated' + ] + verifylist = [ + ('port_pair', self._port_pair_name), + ('name', 'name_updated'), + ('description', 'desc_updated'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + attrs = { + 'name': 'name_updated', + 'description': 'desc_updated' + } + mock_port_pair_update.assert_called_once_with(self._port_pair_name, + **attrs) + self.assertIsNone(result) + + +class TestShowSfcPortPair(fakes.TestNeutronClientOSCV2): + + _pp = fakes.FakeSfcPortPair.create_port_pair() + + data = ( + _pp['description'], + _pp['egress'], + _pp['id'], + _pp['ingress'], + _pp['name'], + _pp['project_id'], + _pp['service_function_parameters'], + ) + _port_pair = _pp + _port_pair_id = _pp['id'] + columns = ( + 'Description', + 'Egress Logical Port', + 'ID', + 'Ingress Logical Port', + 'Name', + 'Project', + 'Service Function Parameters' + ) + + def setUp(self): + super(TestShowSfcPortPair, self).setUp() + + self.network.get_sfc_port_pair = mock.Mock( + return_value=self._port_pair + ) + + # Get the command object to test + self.cmd = sfc_port_pair.ShowSfcPortPair(self.app, self.namespace) + + def test_show_port_pair(self): + client = self.app.client_manager.network + mock_port_pair_show = client.get_sfc_port_pair + arglist = [ + self._port_pair_id, + ] + verifylist = [ + ('port_pair', self._port_pair_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + mock_port_pair_show.assert_called_once_with(self._port_pair_id) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) diff --git a/neutronclient/tests/unit/osc/v2/sfc/test_port_pair_group.py b/neutronclient/tests/unit/osc/v2/sfc/test_port_pair_group.py new file mode 100755 index 000000000..49f3fcf06 --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/sfc/test_port_pair_group.py @@ -0,0 +1,433 @@ +# Copyright (c) 2017 Huawei Technologies India Pvt.Limited. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from unittest import mock + +from osc_lib import exceptions +import testtools + +from neutronclient.osc.v2.sfc import sfc_port_pair_group +from neutronclient.tests.unit.osc.v2.sfc import fakes + + +class TestCreateSfcPortPairGroup(fakes.TestNeutronClientOSCV2): + + _port_pair_group = fakes.FakeSfcPortPairGroup.create_port_pair_group() + + columns = ('Description', + 'ID', + 'Name', + 'Port Pair', + 'Port Pair Group Parameters', + 'Project', + 'Tap Enabled') + + def get_data(self, ppg): + return ( + ppg['description'], + ppg['id'], + ppg['name'], + ppg['port_pairs'], + ppg['port_pair_group_parameters'], + ppg['project_id'], + ppg['tap_enabled'] + ) + + def setUp(self): + super(TestCreateSfcPortPairGroup, self).setUp() + self.network.create_sfc_port_pair_group = mock.Mock( + return_value=self._port_pair_group) + self.data = self.get_data(self._port_pair_group) + + # Get the command object to test + self.cmd = sfc_port_pair_group.CreateSfcPortPairGroup(self.app, + self.namespace) + + def test_create_port_pair_group_default_options(self): + arglist = [ + "--port-pair", self._port_pair_group['port_pairs'], + self._port_pair_group['name'], + ] + verifylist = [ + ('port_pairs', [self._port_pair_group['port_pairs']]), + ('name', self._port_pair_group['name']), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = (self.cmd.take_action(parsed_args)) + self.network.create_sfc_port_pair_group.assert_called_once_with( + **{'name': self._port_pair_group['name'], + 'port_pairs': [self._port_pair_group['port_pairs']]}) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_create_port_pair_group(self): + arglist = [ + "--description", self._port_pair_group['description'], + "--port-pair", self._port_pair_group['port_pairs'], + self._port_pair_group['name'], + ] + verifylist = [ + ('port_pairs', [self._port_pair_group['port_pairs']]), + ('name', self._port_pair_group['name']), + ('description', self._port_pair_group['description']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = (self.cmd.take_action(parsed_args)) + + self.network.create_sfc_port_pair_group.assert_called_once_with( + **{ + 'name': self._port_pair_group['name'], + 'port_pairs': [self._port_pair_group['port_pairs']], + 'description': self._port_pair_group['description']}) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_create_tap_enabled_port_pair_group(self): + arglist = [ + "--description", self._port_pair_group['description'], + "--port-pair", self._port_pair_group['port_pairs'], + self._port_pair_group['name'], + "--enable-tap" + ] + verifylist = [ + ('port_pairs', [self._port_pair_group['port_pairs']]), + ('name', self._port_pair_group['name']), + ('description', self._port_pair_group['description']), + ('enable_tap', True) + ] + + expected_data = self._update_expected_response_data( + data={ + 'tap_enabled': True + } + ) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = (self.cmd.take_action(parsed_args)) + + self.network.create_sfc_port_pair_group.assert_called_once_with( + **{ + 'name': self._port_pair_group['name'], + 'port_pairs': [self._port_pair_group['port_pairs']], + 'description': self._port_pair_group['description'], + 'tap_enabled': True}) + self.assertEqual(self.columns, columns) + self.assertEqual(expected_data, data) + + def _update_expected_response_data(self, data): + # REVISIT(vks1) - This method can be common for other test functions. + ppg = fakes.FakeSfcPortPairGroup.create_port_pair_group(data) + self.network.create_sfc_port_pair_group.return_value = ppg + return self.get_data(ppg) + + +class TestDeleteSfcPortPairGroup(fakes.TestNeutronClientOSCV2): + + _port_pair_group = (fakes.FakeSfcPortPairGroup.create_port_pair_groups + (count=1)) + + def setUp(self): + super(TestDeleteSfcPortPairGroup, self).setUp() + self.network.delete_sfc_port_pair_group = mock.Mock( + return_value=None) + self.cmd = sfc_port_pair_group.DeleteSfcPortPairGroup(self.app, + self.namespace) + + def test_delete_port_pair_group(self): + client = self.app.client_manager.network + mock_port_pair_group_delete = client.delete_sfc_port_pair_group + arglist = [ + self._port_pair_group[0]['id'], + ] + verifylist = [ + ('port_pair_group', [self._port_pair_group[0]['id']]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + mock_port_pair_group_delete.assert_called_once_with( + self._port_pair_group[0]['id']) + self.assertIsNone(result) + + def test_delete_multiple_port_pair_groups_with_exception(self): + client = self.app.client_manager.network + target1 = 'target' + arglist = [target1] + verifylist = [('port_pair_group', [target1])] + + client.find_sfc_port_pair_group.side_effect = [ + target1, exceptions.CommandError + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + msg = "1 of 2 port pair group(s) failed to delete." + with testtools.ExpectedException(exceptions.CommandError) as e: + self.cmd.take_action(parsed_args) + self.assertEqual(msg, str(e)) + + +class TestListSfcPortPairGroup(fakes.TestNeutronClientOSCV2): + _ppgs = fakes.FakeSfcPortPairGroup.create_port_pair_groups(count=1) + columns = ('ID', 'Name', 'Port Pair', 'Port Pair Group Parameters', + 'Tap Enabled') + columns_long = ('ID', 'Name', 'Port Pair', 'Port Pair Group Parameters', + 'Description', 'Project', 'Tap Enabled') + _port_pair_group = _ppgs[0] + data = [ + _port_pair_group['id'], + _port_pair_group['name'], + _port_pair_group['port_pairs'], + _port_pair_group['port_pair_group_parameters'], + _port_pair_group['tap_enabled'] + ] + data_long = [ + _port_pair_group['id'], + _port_pair_group['name'], + _port_pair_group['port_pairs'], + _port_pair_group['port_pair_group_parameters'], + _port_pair_group['description'], + _port_pair_group['tap_enabled'] + ] + _port_pair_group1 = {'port_pair_groups': _port_pair_group} + _port_pair_id = _port_pair_group['id'] + + def setUp(self): + super(TestListSfcPortPairGroup, self).setUp() + + self.network.sfc_port_pair_groups = mock.Mock( + return_value=self._ppgs + ) + # Get the command object to test + self.cmd = sfc_port_pair_group.ListSfcPortPairGroup(self.app, + self.namespace) + + def test_list_port_pair_groups(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns = self.cmd.take_action(parsed_args)[0] + ppgs = self.network.sfc_port_pair_groups() + ppg = ppgs[0] + data = [ + ppg['id'], + ppg['name'], + ppg['port_pairs'], + ppg['port_pair_group_parameters'], + ppg['tap_enabled'] + ] + self.assertEqual(list(self.columns), columns) + self.assertEqual(self.data, data) + + def test_list_with_long_option(self): + arglist = ['--long'] + verifylist = [('long', True)] + ppgs = self.network.sfc_port_pair_groups() + ppg = ppgs[0] + data = [ + ppg['id'], + ppg['name'], + ppg['port_pairs'], + ppg['port_pair_group_parameters'], + ppg['description'], + ppg['tap_enabled'] + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns_long = self.cmd.take_action(parsed_args)[0] + self.assertEqual(list(self.columns_long), columns_long) + self.assertEqual(self.data_long, data) + + +class TestSetSfcPortPairGroup(fakes.TestNeutronClientOSCV2): + _port_pair_group = fakes.FakeSfcPortPairGroup.create_port_pair_group() + resource = _port_pair_group + res = 'port_pair_group' + _port_pair_group_name = _port_pair_group['name'] + ppg_pp = _port_pair_group['port_pairs'] + _port_pair_group_id = _port_pair_group['id'] + + def setUp(self): + super(TestSetSfcPortPairGroup, self).setUp() + + self.network.update_sfc_port_pair_group = mock.Mock( + return_value=None) + self.mocked = self.network.update_sfc_port_pair_group + self.cmd = sfc_port_pair_group.SetSfcPortPairGroup(self.app, + self.namespace) + + def test_set_port_pair_group(self): + target = self.resource['id'] + port_pair1 = 'additional_port1' + port_pair2 = 'additional_port2' + + self.network.find_sfc_port_pair = mock.Mock( + side_effect=lambda name_or_id, ignore_missing=False: + {'id': name_or_id} + ) + self.network.find_sfc_port_pair_group = mock.Mock( + side_effect=lambda name_or_id, ignore_missing=False: + {'id': name_or_id, 'port_pairs': self.ppg_pp} + ) + + arglist = [ + target, + '--port-pair', port_pair1, + '--port-pair', port_pair2, + ] + verifylist = [ + (self.res, target), + ('port_pairs', [port_pair1, port_pair2]) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + expect = {'port_pairs': sorted([*self.ppg_pp, port_pair1, port_pair2])} + self.mocked.assert_called_once_with(target, **expect) + self.assertIsNone(result) + + def test_set_no_port_pair(self): + client = self.app.client_manager.network + mock_port_pair_group_update = client.update_sfc_port_pair_group + arglist = [ + self._port_pair_group_name, + '--name', 'name_updated', + '--description', 'desc_updated', + '--no-port-pair', + ] + verifylist = [ + ('port_pair_group', self._port_pair_group_name), + ('name', 'name_updated'), + ('description', 'desc_updated'), + ('no_port_pair', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + attrs = {'name': 'name_updated', 'description': 'desc_updated', + 'port_pairs': []} + mock_port_pair_group_update.assert_called_once_with( + self._port_pair_group_name, **attrs) + self.assertIsNone(result) + + +class TestShowSfcPortPairGroup(fakes.TestNeutronClientOSCV2): + + _ppg = fakes.FakeSfcPortPairGroup.create_port_pair_group() + data = ( + _ppg['description'], + _ppg['id'], + _ppg['name'], + _ppg['port_pairs'], + _ppg['port_pair_group_parameters'], + _ppg['project_id'], + _ppg['tap_enabled']) + _port_pair_group = _ppg + _port_pair_group_id = _ppg['id'] + columns = ( + 'Description', + 'ID', + 'Name', + 'Port Pair', + 'Port Pair Group Parameters', + 'Project', + 'Tap Enabled' + ) + + def setUp(self): + super(TestShowSfcPortPairGroup, self).setUp() + + self.network.get_sfc_port_pair_group = mock.Mock( + return_value=self._port_pair_group + ) + self.cmd = sfc_port_pair_group.ShowSfcPortPairGroup(self.app, + self.namespace) + + def test_show_port_pair_group(self): + client = self.app.client_manager.network + mock_port_pair_group_show = client.get_sfc_port_pair_group + arglist = [ + self._port_pair_group_id, + ] + verifylist = [ + ('port_pair_group', self._port_pair_group_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + mock_port_pair_group_show.assert_called_once_with( + self._port_pair_group_id) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestUnsetSfcPortPairGroup(fakes.TestNeutronClientOSCV2): + _port_pair_group = fakes.FakeSfcPortPairGroup.create_port_pair_group() + resource = _port_pair_group + res = 'port_pair_group' + _port_pair_group_name = _port_pair_group['name'] + _port_pair_group_id = _port_pair_group['id'] + ppg_pp = _port_pair_group['port_pairs'] + + def setUp(self): + super(TestUnsetSfcPortPairGroup, self).setUp() + self.network.update_sfc_port_pair_group = mock.Mock( + return_value=None) + self.mocked = self.network.update_sfc_port_pair_group + self.cmd = sfc_port_pair_group.UnsetSfcPortPairGroup( + self.app, self.namespace) + + def test_unset_port_pair(self): + target = self.resource['id'] + port_pair1 = 'additional_port1' + port_pair2 = 'additional_port2' + + self.network.find_sfc_port_pair = mock.Mock( + side_effect=lambda name_or_id, ignore_missing=False: + {'id': name_or_id} + ) + self.network.find_sfc_port_pair_group = mock.Mock( + side_effect=lambda name_or_id, ignore_missing=False: + {'id': name_or_id, 'port_pairs': self.ppg_pp} + ) + arglist = [ + target, + '--port-pair', port_pair1, + '--port-pair', port_pair2, + ] + verifylist = [ + (self.res, target), + ('port_pairs', [port_pair1, port_pair2]) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + expect = {'port_pairs': sorted([*self.ppg_pp])} + self.mocked.assert_called_once_with(target, **expect) + self.assertIsNone(result) + + def test_unset_all_port_pair(self): + client = self.app.client_manager.network + mock_port_pair_group_update = client.update_sfc_port_pair_group + arglist = [ + self._port_pair_group_name, + '--all-port-pair', + ] + verifylist = [ + ('port_pair_group', self._port_pair_group_name), + ('all_port_pair', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + attrs = {'port_pairs': []} + mock_port_pair_group_update.assert_called_once_with( + self._port_pair_group_name, **attrs) + self.assertIsNone(result) diff --git a/neutronclient/tests/unit/osc/v2/sfc/test_service_graph.py b/neutronclient/tests/unit/osc/v2/sfc/test_service_graph.py new file mode 100644 index 000000000..167f1fcf8 --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/sfc/test_service_graph.py @@ -0,0 +1,338 @@ +# Copyright 2017 Intel Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# 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 osc_lib import exceptions +from osc_lib.tests import utils as tests_utils +import testtools + +from neutronclient.osc.v2.sfc import sfc_service_graph +from neutronclient.tests.unit.osc.v2.sfc import fakes + + +class TestListSfcServiceGraph(fakes.TestNeutronClientOSCV2): + _service_graphs = fakes.FakeSfcServiceGraph.create_sfc_service_graphs( + count=1) + columns = ('ID', 'Name', 'Branching Points') + columns_long = ('ID', 'Name', 'Branching Points', 'Description', 'Project') + _service_graph = _service_graphs[0] + data = [ + _service_graph['id'], + _service_graph['name'], + _service_graph['port_chains'] + ] + data_long = [ + _service_graph['id'], + _service_graph['name'], + _service_graph['port_chains'], + _service_graph['description'], + _service_graph['project_id'] + ] + _service_graph1 = {'service_graphs': _service_graph} + _service_graph_id = _service_graph['id'] + + def setUp(self): + super(TestListSfcServiceGraph, self).setUp() + self.network.sfc_service_graphs = mock.Mock( + return_value=self._service_graphs + ) + # Get the command object to test + self.cmd = sfc_service_graph.ListSfcServiceGraph( + self.app, self.namespace) + + def test_list_sfc_service_graphs(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns = self.cmd.take_action(parsed_args)[0] + sgs = self.network.sfc_service_graphs() + sg = sgs[0] + data = [ + sg['id'], + sg['name'], + sg['port_chains'] + ] + self.assertEqual(list(self.columns), columns) + self.assertEqual(self.data, data) + + def test_list_sfc_service_graphs_with_long_option(self): + arglist = ['--long'] + verifylist = [('long', True)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns = self.cmd.take_action(parsed_args)[0] + sgs = self.network.sfc_service_graphs() + sg = sgs[0] + data = [ + sg['id'], + sg['name'], + sg['port_chains'], + sg['description'], + sg['project_id'] + ] + self.assertEqual(list(self.columns_long), columns) + self.assertEqual(self.data_long, data) + + +class TestCreateSfcServiceGraph(fakes.TestNeutronClientOSCV2): + _service_graph = fakes.FakeSfcServiceGraph.create_sfc_service_graph() + + columns = ('ID', 'Name', 'Branching Points') + columns_long = ('Branching Points', 'Description', 'ID', 'Name', 'Project') + + def get_data(self): + return ( + self._service_graph['port_chains'], + self._service_graph['description'], + self._service_graph['id'], + self._service_graph['name'], + self._service_graph['project_id'], + ) + + def setUp(self): + super(TestCreateSfcServiceGraph, self).setUp() + self.network.create_sfc_service_graph = mock.Mock( + return_value=self._service_graph) + self.data = self.get_data() + self.cmd = sfc_service_graph.CreateSfcServiceGraph( + self.app, self.namespace) + + def test_create_sfc_service_graph(self): + arglist = [] + verifylist = [] + + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_create_sfc_service_graph_without_loop(self): + bp1_str = 'pc1:pc2,pc3' + bp2_str = 'pc2:pc4' + self.cmd = sfc_service_graph.CreateSfcServiceGraph( + self.app, self.namespace) + + arglist = [ + "--description", self._service_graph['description'], + "--branching-point", bp1_str, + "--branching-point", bp2_str, + self._service_graph['name']] + + pcs = {'pc1': ['pc2', 'pc3'], 'pc2': ['pc4']} + + verifylist = [ + ("description", self._service_graph['description']), + ("branching_points", [bp1_str, bp2_str]), + ("name", self._service_graph['name']) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = (self.cmd.take_action(parsed_args)) + + self.network.create_sfc_service_graph.assert_called_once_with(**{ + 'description': self._service_graph['description'], + 'name': self._service_graph['name'], + 'port_chains': pcs + }) + self.assertEqual(self.columns_long, columns) + self.assertEqual(self.data, data) + + def test_create_sfc_service_graph_with_loop(self): + bp1_str = 'pc1:pc2,pc3;' + bp2_str = 'pc2:pc1' + self.cmd = sfc_service_graph.CreateSfcServiceGraph( + self.app, self.namespace) + + arglist = [ + "--description", self._service_graph['description'], + "--branching-point", bp1_str, + "--branching-point", bp2_str, + self._service_graph['name']] + + verifylist = [ + ("description", self._service_graph['description']), + ("branching_points", [bp1_str, bp2_str]), + ("name", self._service_graph['name']) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args) + + def test_create_sfc_service_graph_invalid_port_chains(self): + bp1_str = 'pc1:pc2,pc3:' + self.cmd = sfc_service_graph.CreateSfcServiceGraph( + self.app, self.namespace) + + arglist = [ + "--description", self._service_graph['description'], + "--branching-point", bp1_str, + self._service_graph['name']] + + verifylist = [ + ("description", self._service_graph['description']), + ("branching_points", [bp1_str]), + ("name", self._service_graph['name']) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args) + + def test_create_sfc_service_graph_duplicate_src_chains(self): + bp1_str = 'pc1:pc2,pc3;' + bp2_str = 'pc1:pc4' + self.cmd = sfc_service_graph.CreateSfcServiceGraph( + self.app, self.namespace) + + arglist = [ + "--description", self._service_graph['description'], + "--branching-point", bp1_str, + "--branching-point", bp2_str, + self._service_graph['name']] + + verifylist = [ + ("description", self._service_graph['description']), + ("branching_points", [bp1_str, bp2_str]), + ("name", self._service_graph['name']) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args) + + +class TestDeleteSfcServiceGraph(fakes.TestNeutronClientOSCV2): + + _service_graph = fakes.FakeSfcServiceGraph.create_sfc_service_graphs( + count=1) + + def setUp(self): + super(TestDeleteSfcServiceGraph, self).setUp() + self.network.delete_sfc_service_graph = mock.Mock( + return_value=None) + self.cmd = sfc_service_graph.DeleteSfcServiceGraph( + self.app, self.namespace) + + def test_delete_sfc_service_graph(self): + client = self.app.client_manager.network + mock_service_graph_delete = client.delete_sfc_service_graph + arglist = [ + self._service_graph[0]['id'], + ] + verifylist = [ + ('service_graph', [self._service_graph[0]['id']]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + mock_service_graph_delete.assert_called_once_with( + self._service_graph[0]['id']) + self.assertIsNone(result) + + def test_delete_multiple_service_graphs_with_exception(self): + client = self.app.client_manager.network + target = self._service_graph[0]['id'] + arglist = [target] + verifylist = [('service_graph', [target])] + + client.find_sfc_service_graph.side_effect = [ + target, exceptions.CommandError + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + msg = "1 of 2 service graph(s) failed to delete." + with testtools.ExpectedException(exceptions.CommandError) as e: + self.cmd.take_action(parsed_args) + self.assertEqual(msg, str(e)) + + +class TestShowSfcServiceGraph(fakes.TestNeutronClientOSCV2): + + _sg = fakes.FakeSfcServiceGraph.create_sfc_service_graph() + columns = ('ID', 'Name', 'Branching Points') + columns_long = ('Branching Points', 'Description', 'ID', 'Name', 'Project') + data = ( + _sg['id'], + _sg['name'], + _sg['port_chains'] + ) + data_long = ( + _sg['port_chains'], + _sg['description'], + _sg['id'], + _sg['name'], + _sg['project_id'] + ) + + _service_graph = _sg + _service_graph_id = _sg['id'] + + def setUp(self): + super(TestShowSfcServiceGraph, self).setUp() + self.network.get_sfc_service_graph = mock.Mock( + return_value=self._service_graph + ) + # Get the command object to test + self.cmd = sfc_service_graph.ShowSfcServiceGraph( + self.app, self.namespace) + + def test_service_graph_show(self): + client = self.app.client_manager.network + mock_service_graph_show = client.get_sfc_service_graph + arglist = [ + self._service_graph_id, + ] + verifylist = [ + ('service_graph', self._service_graph_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + mock_service_graph_show.assert_called_once_with(self._service_graph_id) + self.assertEqual(self.columns_long, columns) + self.assertEqual(self.data_long, data) + + +class TestSetSfcServiceGraph(fakes.TestNeutronClientOSCV2): + _service_graph = fakes.FakeSfcServiceGraph.create_sfc_service_graph() + _service_graph_name = _service_graph['name'] + _service_graph_id = _service_graph['id'] + + def setUp(self): + super(TestSetSfcServiceGraph, self).setUp() + self.network.update_sfc_service_graph = mock.Mock( + return_value=None) + self.cmd = sfc_service_graph.SetSfcServiceGraph( + self.app, self.namespace) + + def test_set_service_graph(self): + client = self.app.client_manager.network + mock_service_graph_update = client.update_sfc_service_graph + arglist = [ + self._service_graph_name, + '--name', 'name_updated', + '--description', 'desc_updated' + ] + verifylist = [ + ('service_graph', self._service_graph_name), + ('name', 'name_updated'), + ('description', 'desc_updated'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + attrs = { + 'name': 'name_updated', + 'description': 'desc_updated' + } + mock_service_graph_update.assert_called_once_with( + self._service_graph_name, **attrs) + self.assertIsNone(result) diff --git a/neutronclient/tests/unit/flavor/__init__.py b/neutronclient/tests/unit/osc/v2/subnet_onboard/__init__.py similarity index 100% rename from neutronclient/tests/unit/flavor/__init__.py rename to neutronclient/tests/unit/osc/v2/subnet_onboard/__init__.py diff --git a/neutronclient/tests/unit/osc/v2/subnet_onboard/test_network_onboard_subnets.py b/neutronclient/tests/unit/osc/v2/subnet_onboard/test_network_onboard_subnets.py new file mode 100644 index 000000000..c2ca14044 --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/subnet_onboard/test_network_onboard_subnets.py @@ -0,0 +1,54 @@ +# Copyright (c) 2019 SUSE Linux Products GmbH +# All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +from unittest import mock + +from neutronclient.osc.v2.subnet_onboard import subnet_onboard +from neutronclient.tests.unit.osc.v2 import fakes as test_fakes + + +def _get_id(client, id_or_name, resource): + return id_or_name + + +class TestNetworkOnboardSubnets(test_fakes.TestNeutronClientOSCV2): + + def setUp(self): + super(TestNetworkOnboardSubnets, self).setUp() + mock.patch( + 'neutronclient.osc.v2.subnet_onboard.subnet_onboard._get_id', + new=_get_id).start() + + self.network_id = 'my_network_id' + self.subnetpool_id = 'my_subnetpool_id' + + # Get the command object to test + self.cmd = subnet_onboard.NetworkOnboardSubnets(self.app, + self.namespace) + + def test_options(self): + arglist = [ + self.network_id, + self.subnetpool_id + ] + verifylist = [ + ('network', self.network_id), + ('subnetpool', self.subnetpool_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.cmd.take_action(parsed_args) + self.neutronclient.onboard_network_subnets.assert_called_once_with( + self.subnetpool_id, {'network_id': self.network_id}) diff --git a/neutronclient/tests/unit/osc/v2/trunk/__init__.py b/neutronclient/tests/unit/osc/v2/trunk/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutronclient/tests/unit/osc/v2/trunk/fakes.py b/neutronclient/tests/unit/osc/v2/trunk/fakes.py deleted file mode 100644 index 0eb6f9786..000000000 --- a/neutronclient/tests/unit/osc/v2/trunk/fakes.py +++ /dev/null @@ -1,83 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import copy -import mock -import uuid - - -class FakeTrunk(object): - """Fake one or more trunks.""" - @staticmethod - def create_one_trunk(attrs=None): - """Create a fake trunk. - - :param Dictionary attrs: - A dictionary with all attributes - :return: - A Dictionary with id, name, admin_state_up, - port_id, sub_ports, status and project_id - """ - attrs = attrs or {} - - # Set default attributes. - trunk_attrs = { - 'id': 'trunk-id-' + uuid.uuid4().hex, - 'name': 'trunk-name-' + uuid.uuid4().hex, - 'port_id': 'port-' + uuid.uuid4().hex, - 'admin_state_up': True, - 'project_id': 'project-id-' + uuid.uuid4().hex, - 'status': 'ACTIVE', - 'sub_ports': [{'port_id': 'subport-' + uuid.uuid4().hex, - 'segmentation_type': 'vlan', - 'segmentation_id': 100}], - } - - # Overwrite default attributes. - trunk_attrs.update(attrs) - return copy.deepcopy(trunk_attrs) - - @staticmethod - def create_trunks(attrs=None, count=2): - """Create multiple fake trunks. - - :param Dictionary attrs: - A dictionary with all attributes - :param int count: - The number of routers to fake - :return: - A list of dictionaries faking the trunks - """ - trunks = [] - for i in range(0, count): - trunks.append(FakeTrunk.create_one_trunk(attrs)) - - return trunks - - @staticmethod - def get_trunks(trunks=None, count=2): - """Get an iterable MagicMock object with a list of faked trunks. - - If trunks list is provided, then initialize the Mock object with the - list. Otherwise create one. - - :param List trunks: - A list of FakeResource objects faking trunks - :param int count: - The number of trunks to fake - :return: - An iterable Mock object with side_effect set to a list of faked - trunks - """ - if trunks is None: - trunks = FakeTrunk.create_trunks(count) - return mock.MagicMock(side_effect=trunks) diff --git a/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py b/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py deleted file mode 100644 index 5bb5ea214..000000000 --- a/neutronclient/tests/unit/osc/v2/trunk/test_network_trunk.py +++ /dev/null @@ -1,643 +0,0 @@ -# Copyright 2016 ZTE Corporation. -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import mock -from mock import call -import testtools - -from osc_lib import exceptions -from osc_lib.tests import utils as tests_utils -from osc_lib import utils - -from neutronclient.osc.v2.trunk import network_trunk as trunk -from neutronclient.tests.unit.osc.v2 import fakes as test_fakes -from neutronclient.tests.unit.osc.v2.trunk import fakes - - -def _get_id(client, id_or_name, resource): - return id_or_name - - -class TestCreateNetworkTrunk(test_fakes.TestNeutronClientOSCV2): - # The new trunk created - _trunk = fakes.FakeTrunk.create_one_trunk() - - columns = ( - 'admin_state_up', - 'id', - 'name', - 'port_id', - 'project_id', - 'status', - 'sub_ports', - ) - data = ( - trunk._format_admin_state(_trunk['admin_state_up']), - _trunk['id'], - _trunk['name'], - _trunk['port_id'], - _trunk['project_id'], - _trunk['status'], - utils.format_list_of_dicts(_trunk['sub_ports']), - ) - - def setUp(self): - super(TestCreateNetworkTrunk, self).setUp() - mock.patch('neutronclient.osc.v2.trunk.network_trunk._get_id', - new=_get_id).start() - self.neutronclient.create_trunk = mock.Mock( - return_value={trunk.TRUNK: self._trunk}) - - # Get the command object to test - self.cmd = trunk.CreateNetworkTrunk(self.app, self.namespace) - - def test_create_no_options(self): - arglist = [] - verifylist = [] - - self.assertRaises(tests_utils.ParserException, self.check_parser, - self.cmd, arglist, verifylist) - - def test_create_default_options(self): - arglist = [ - "--parent-port", self._trunk['port_id'], - self._trunk['name'], - ] - verifylist = [ - ('parent_port', self._trunk['port_id']), - ('name', self._trunk['name']), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - columns, data = (self.cmd.take_action(parsed_args)) - - self.neutronclient.create_trunk.assert_called_once_with({ - trunk.TRUNK: {'name': self._trunk['name'], - 'admin_state_up': self._trunk['admin_state_up'], - 'port_id': self._trunk['port_id']} - }) - self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) - - def test_create_full_options(self): - subport = self._trunk['sub_ports'][0] - arglist = [ - "--disable", - "--parent-port", self._trunk['port_id'], - "--subport", 'port=%(port)s,segmentation-type=%(seg_type)s,' - 'segmentation-id=%(seg_id)s' % { - 'seg_id': subport['segmentation_id'], - 'seg_type': subport['segmentation_type'], - 'port': subport['port_id']}, - self._trunk['name'], - ] - verifylist = [ - ('name', self._trunk['name']), - ('parent_port', self._trunk['port_id']), - ('add_subports', [{ - 'port': subport['port_id'], - 'segmentation-id': str(subport['segmentation_id']), - 'segmentation-type': subport['segmentation_type']}]), - ('disable', True), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = (self.cmd.take_action(parsed_args)) - - self.neutronclient.create_trunk.assert_called_once_with({ - trunk.TRUNK: {'name': self._trunk['name'], - 'admin_state_up': False, - 'sub_ports': [subport], - 'port_id': self._trunk['port_id']} - }) - self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) - - def test_create_trunk_with_subport_invalid_segmentation_id_fail(self): - subport = self._trunk['sub_ports'][0] - arglist = [ - "--parent-port", self._trunk['port_id'], - "--subport", "port=%(port)s,segmentation-type=%(seg_type)s," - "segmentation-id=boom" % { - 'seg_type': subport['segmentation_type'], - 'port': subport['port_id']}, - self._trunk['name'], - ] - verifylist = [ - ('name', self._trunk['name']), - ('parent_port', self._trunk['port_id']), - ('add_subports', [{ - 'port': subport['port_id'], - 'segmentation-id': 'boom', - 'segmentation-type': subport['segmentation_type']}]), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - with testtools.ExpectedException(exceptions.CommandError) as e: - self.cmd.take_action(parsed_args) - self.assertEqual("Segmentation-id 'boom' is not an integer", - str(e)) - - -class TestDeleteNetworkTrunk(test_fakes.TestNeutronClientOSCV2): - # The trunk to be deleted. - _trunks = fakes.FakeTrunk.create_trunks(count=2) - - def setUp(self): - super(TestDeleteNetworkTrunk, self).setUp() - - mock.patch('neutronclient.osc.v2.trunk.network_trunk._get_id', - new=_get_id).start() - self.neutronclient.delete_trunk = mock.Mock(return_value=None) - - # Get the command object to test - self.cmd = trunk.DeleteNetworkTrunk(self.app, self.namespace) - - def test_delete_trunk(self): - arglist = [ - self._trunks[0]['name'], - ] - verifylist = [ - ('trunk', [self._trunks[0]['name']]), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - self.neutronclient.delete_trunk.assert_called_once_with( - self._trunks[0]['name']) - self.assertIsNone(result) - - def test_delete_trunk_multiple(self): - arglist = [] - verifylist = [] - - for t in self._trunks: - arglist.append(t['name']) - verifylist = [ - ('trunk', arglist), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - calls = [] - for t in self._trunks: - calls.append(call(t['name'])) - self.neutronclient.delete_trunk.assert_has_calls(calls) - self.assertIsNone(result) - - def test_delete_trunk_multiple_with_exception(self): - arglist = [ - self._trunks[0]['name'], - 'unexist_trunk', - ] - verifylist = [ - ('trunk', - [self._trunks[0]['name'], 'unexist_trunk']), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - get_mock_result = [self._trunks[0], exceptions.CommandError] - trunk._get_id = ( - mock.MagicMock(side_effect=get_mock_result) - ) - with testtools.ExpectedException(exceptions.CommandError) as e: - self.cmd.take_action(parsed_args) - self.assertEqual('1 of 2 trunks failed to delete.', str(e)) - self.neutronclient.delete_trunk.assert_called_once_with( - self._trunks[0] - ) - - -class TestShowNetworkTrunk(test_fakes.TestNeutronClientOSCV2): - - # The trunk to set. - _trunk = fakes.FakeTrunk.create_one_trunk() - - columns = ( - 'admin_state_up', - 'id', - 'name', - 'port_id', - 'project_id', - 'status', - 'sub_ports', - ) - data = ( - trunk._format_admin_state(_trunk['admin_state_up']), - _trunk['id'], - _trunk['name'], - _trunk['port_id'], - _trunk['project_id'], - _trunk['status'], - utils.format_list_of_dicts(_trunk['sub_ports']), - ) - - def setUp(self): - super(TestShowNetworkTrunk, self).setUp() - - mock.patch('neutronclient.osc.v2.trunk.network_trunk._get_id', - new=_get_id).start() - self.neutronclient.show_trunk = mock.Mock( - return_value={trunk.TRUNK: self._trunk}) - - # Get the command object to test - self.cmd = trunk.ShowNetworkTrunk(self.app, self.namespace) - - def test_show_no_options(self): - arglist = [] - verifylist = [] - - self.assertRaises(tests_utils.ParserException, self.check_parser, - self.cmd, arglist, verifylist) - - def test_show_all_options(self): - arglist = [ - self._trunk['id'], - ] - verifylist = [ - ('trunk', self._trunk['id']), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.neutronclient.show_trunk.assert_called_once_with( - self._trunk['id']) - self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) - - -class TestListNetworkTrunk(test_fakes.TestNeutronClientOSCV2): - # Create trunks to be listed. - _trunks = fakes.FakeTrunk.create_trunks(count=3) - - columns = ( - 'ID', - 'Name', - 'Parent Port', - ) - columns_long = columns + ( - 'Status', - 'State', - ) - data = [] - for t in _trunks: - data.append(( - t['id'], - t['name'], - t['port_id'], - )) - data_long = [] - for t in _trunks: - data_long.append(( - t['id'], - t['name'], - t['port_id'], - t['status'], - trunk._format_admin_state(t['admin_state_up']), - )) - - def setUp(self): - super(TestListNetworkTrunk, self).setUp() - mock.patch('neutronclient.osc.v2.trunk.network_trunk._get_id', - new=_get_id).start() - self.neutronclient.list_trunks = mock.Mock( - return_value={trunk.TRUNKS: self._trunks}) - - # Get the command object to test - self.cmd = trunk.ListNetworkTrunk(self.app, self.namespace) - - def test_trunk_list_no_option(self): - arglist = [] - verifylist = [] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.neutronclient.list_trunks.assert_called_once_with() - self.assertEqual(self.columns, columns) - self.assertEqual(self.data, list(data)) - - def test_trunk_list_long(self): - arglist = [ - '--long', - ] - verifylist = [ - ('long', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.neutronclient.list_trunks.assert_called_once_with() - self.assertEqual(self.columns_long, columns) - self.assertEqual(self.data_long, list(data)) - - -class TestSetNetworkTrunk(test_fakes.TestNeutronClientOSCV2): - # Create trunks to be listed. - _trunk = fakes.FakeTrunk.create_one_trunk() - - columns = ( - 'admin_state_up', - 'id', - 'name', - 'port_id', - 'project_id', - 'status', - 'sub_ports', - ) - data = ( - trunk._format_admin_state(_trunk['admin_state_up']), - _trunk['id'], - _trunk['name'], - _trunk['port_id'], - _trunk['project_id'], - _trunk['status'], - utils.format_list_of_dicts(_trunk['sub_ports']), - ) - - def setUp(self): - super(TestSetNetworkTrunk, self).setUp() - mock.patch('neutronclient.osc.v2.trunk.network_trunk._get_id', - new=_get_id).start() - self.neutronclient.update_trunk = mock.Mock( - return_value={trunk.TRUNK: self._trunk}) - self.neutronclient.trunk_add_subports = mock.Mock( - return_value=self._trunk) - - # Get the command object to test - self.cmd = trunk.SetNetworkTrunk(self.app, self.namespace) - - def test_set_network_trunk_name(self): - arglist = [ - '--name', 'trunky', - self._trunk['name'], - ] - verifylist = [ - ('name', 'trunky'), - ('trunk', self._trunk['name']), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - attrs = { - 'name': 'trunky', - } - self.neutronclient.update_trunk.assert_called_once_with( - self._trunk['name'], {trunk.TRUNK: attrs}) - self.assertIsNone(result) - - def test_set_network_trunk_admin_state_up_disable(self): - arglist = [ - '--disable', - self._trunk['name'], - ] - verifylist = [ - ('disable', True), - ('trunk', self._trunk['name']), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - attrs = { - 'admin_state_up': False, - } - self.neutronclient.update_trunk.assert_called_once_with( - self._trunk['name'], {trunk.TRUNK: attrs}) - self.assertIsNone(result) - - def test_set_network_trunk_admin_state_up_enable(self): - arglist = [ - '--enable', - self._trunk['name'], - ] - verifylist = [ - ('enable', True), - ('trunk', self._trunk['name']), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - attrs = { - 'admin_state_up': True, - } - self.neutronclient.update_trunk.assert_called_once_with( - self._trunk['name'], {trunk.TRUNK: attrs}) - self.assertIsNone(result) - - def test_set_network_trunk_nothing(self): - arglist = [self._trunk['name'], ] - verifylist = [('trunk', self._trunk['name']), ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - attrs = {} - self.neutronclient.update_trunk.assert_called_once_with( - self._trunk['name'], {trunk.TRUNK: attrs}) - self.assertIsNone(result) - - def test_set_network_trunk_subports(self): - subport = self._trunk['sub_ports'][0] - arglist = [ - '--subport', 'port=%(port)s,segmentation-type=%(seg_type)s,' - 'segmentation-id=%(seg_id)s' % { - 'seg_id': subport['segmentation_id'], - 'seg_type': subport['segmentation_type'], - 'port': subport['port_id']}, - self._trunk['name'], - ] - verifylist = [ - ('trunk', self._trunk['name']), - ('set_subports', [{ - 'port': subport['port_id'], - 'segmentation-id': str(subport['segmentation_id']), - 'segmentation-type': subport['segmentation_type']}]), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.neutronclient.trunk_add_subports.assert_called_once_with( - self._trunk['name'], {'sub_ports': [subport]} - ) - self.assertIsNone(result) - - def test_set_trunk_attrs_with_exception(self): - arglist = [ - '--name', 'reallylongname', - self._trunk['name'], - ] - verifylist = [ - ('trunk', self._trunk['name']), - ('name', 'reallylongname'), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - self.neutronclient.update_trunk = ( - mock.MagicMock(side_effect=exceptions.CommandError) - ) - with testtools.ExpectedException(exceptions.CommandError) as e: - self.cmd.take_action(parsed_args) - self.assertEqual( - "Failed to set trunk '%s': " % self._trunk['name'], - str(e)) - attrs = {'name': 'reallylongname'} - self.neutronclient.update_trunk.assert_called_once_with( - self._trunk['name'], {trunk.TRUNK: attrs}) - self.neutronclient.trunk_add_subports.assert_not_called() - - def test_set_trunk_add_subport_with_exception(self): - arglist = [ - '--subport', 'port=invalid_subport', - self._trunk['name'], - ] - verifylist = [ - ('trunk', self._trunk['name']), - ('set_subports', [{'port': 'invalid_subport'}]), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - self.neutronclient.trunk_add_subports = ( - mock.MagicMock(side_effect=exceptions.CommandError) - ) - with testtools.ExpectedException(exceptions.CommandError) as e: - self.cmd.take_action(parsed_args) - self.assertEqual( - "Failed to add subports to trunk '%s': " % self._trunk['name'], - str(e)) - self.neutronclient.update_trunk.assert_called_once_with( - self._trunk['name'], {trunk.TRUNK: {}}) - self.neutronclient.trunk_add_subports.assert_called_once_with( - self._trunk['name'], - {'sub_ports': [{'port_id': 'invalid_subport'}]} - ) - - -class TestListNetworkSubport(test_fakes.TestNeutronClientOSCV2): - - _trunk = fakes.FakeTrunk.create_one_trunk() - _subports = _trunk['sub_ports'] - - columns = ( - 'Port', - 'Segmentation Type', - 'Segmentation ID', - ) - data = [] - for s in _subports: - data.append(( - s['port_id'], - s['segmentation_type'], - s['segmentation_id'], - )) - - def setUp(self): - super(TestListNetworkSubport, self).setUp() - mock.patch('neutronclient.osc.v2.trunk.network_trunk._get_id', - new=_get_id).start() - self.neutronclient.trunk_get_subports = mock.Mock( - return_value={trunk.SUB_PORTS: self._subports}) - - # Get the command object to test - self.cmd = trunk.ListNetworkSubport(self.app, self.namespace) - - def test_subport_list(self): - arglist = [ - '--trunk', self._trunk['name'], - ] - verifylist = [ - ('trunk', self._trunk['name']), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.neutronclient.trunk_get_subports.assert_called_once_with( - self._trunk['name']) - self.assertEqual(self.columns, columns) - self.assertEqual(self.data, list(data)) - - -class TestUnsetNetworkTrunk(test_fakes.TestNeutronClientOSCV2): - - _trunk = fakes.FakeTrunk.create_one_trunk() - - columns = ( - 'admin_state_up', - 'id', - 'name', - 'port_id', - 'project_id', - 'status', - 'sub_ports', - ) - data = ( - trunk._format_admin_state(_trunk['admin_state_up']), - _trunk['id'], - _trunk['name'], - _trunk['port_id'], - _trunk['project_id'], - _trunk['status'], - utils.format_list_of_dicts(_trunk['sub_ports']), - ) - - def setUp(self): - super(TestUnsetNetworkTrunk, self).setUp() - - mock.patch('neutronclient.osc.v2.trunk.network_trunk._get_id', - new=_get_id).start() - self.neutronclient.trunk_remove_subports = mock.Mock( - return_value=None) - - # Get the command object to test - self.cmd = trunk.UnsetNetworkTrunk(self.app, self.namespace) - - def test_unset_network_trunk_subport(self): - subport = self._trunk['sub_ports'][0] - arglist = [ - "--subport", subport['port_id'], - self._trunk['name'], - ] - verifylist = [ - ('trunk', self._trunk['name']), - ('unset_subports', [subport['port_id']]), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.neutronclient.trunk_remove_subports.assert_called_once_with( - self._trunk['name'], - {trunk.SUB_PORTS: [{'port_id': subport['port_id']}]} - ) - self.assertIsNone(result) - - def test_unset_subport_no_arguments_fail(self): - arglist = [ - self._trunk['name'], - ] - verifylist = [ - ('trunk', self._trunk['name']), - ] - self.assertRaises(tests_utils.ParserException, - self.check_parser, self.cmd, arglist, verifylist) diff --git a/neutronclient/tests/unit/osc/v2/vpnaas/common.py b/neutronclient/tests/unit/osc/v2/vpnaas/common.py new file mode 100644 index 000000000..7aaebdbe3 --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/vpnaas/common.py @@ -0,0 +1,183 @@ +# Copyright 2017 FUJITSU LIMITED +# All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +import testtools + +from osc_lib import exceptions + +from neutronclient.tests.unit.osc.v2 import fakes as test_fakes + + +class TestCreateVPNaaS(test_fakes.TestNeutronClientOSCV2): + pass + + +class TestDeleteVPNaaS(test_fakes.TestNeutronClientOSCV2): + + def test_delete_with_one_resource(self): + target = self.resource['id'] + + def _mock_vpnaas(*args, **kwargs): + return {'id': args[0]} + + self.networkclient.find_vpn_endpoint_group.side_effect = _mock_vpnaas + self.networkclient.find_vpn_ipsec_site_connection.side_effect = \ + _mock_vpnaas + self.networkclient.find_vpn_ike_policy.side_effect = _mock_vpnaas + self.networkclient.find_vpn_ipsec_policy.side_effect = _mock_vpnaas + self.networkclient.find_vpn_service.side_effect = _mock_vpnaas + + arglist = [target] + verifylist = [(self.res, [target])] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with(target) + self.assertIsNone(result) + + def test_delete_with_multiple_resources(self): + + def _mock_vpnaas(*args, **kwargs): + return {'id': args[0]} + + self.networkclient.find_vpn_endpoint_group.side_effect = _mock_vpnaas + self.networkclient.find_vpn_ipsec_site_connection.side_effect = \ + _mock_vpnaas + self.networkclient.find_vpn_ike_policy.side_effect = _mock_vpnaas + self.networkclient.find_vpn_ipsec_policy.side_effect = _mock_vpnaas + self.networkclient.find_vpn_service.side_effect = _mock_vpnaas + + target1 = 'target1' + target2 = 'target2' + arglist = [target1, target2] + verifylist = [(self.res, [target1, target2])] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) + + self.assertEqual(2, self.mocked.call_count) + for idx, reference in enumerate([target1, target2]): + actual = ''.join(self.mocked.call_args_list[idx][0]) + self.assertEqual(reference, actual) + + def test_delete_multiple_with_exception(self): + target1 = 'target' + arglist = [target1] + verifylist = [(self.res, [target1])] + + self.networkclient.find_vpn_ipsec_site_connection.side_effect = [ + target1, exceptions.CommandError + ] + self.networkclient.find_vpn_endpoint_group.side_effect = [ + target1, exceptions.CommandError + ] + self.networkclient.find_vpn_ike_policy.side_effect = [ + target1, exceptions.CommandError + ] + self.networkclient.find_vpn_service.side_effect = [ + target1, exceptions.CommandError + ] + self.networkclient.find_vpn_ipsec_policy.side_effect = [ + target1, exceptions.CommandError + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + resource_name = self.res.replace('_', ' ') + msg = "1 of 2 %s(s) failed to delete." % resource_name + with testtools.ExpectedException(exceptions.CommandError) as e: + self.cmd.take_action(parsed_args) + self.assertEqual(msg, str(e)) + + +class TestListVPNaaS(test_fakes.TestNeutronClientOSCV2): + + def test_list_with_no_option(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.list_headers), headers) + self.assertEqual([self.list_data], list(data)) + + def test_list_with_long_option(self): + arglist = ['--long'] + verifylist = [('long', True)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.headers), headers) + self.assertEqual([self.data], list(data)) + + +class TestSetVPNaaS(test_fakes.TestNeutronClientOSCV2): + + def test_set_name(self): + target = self.resource['id'] + update = 'change' + arglist = [target, '--name', update] + verifylist = [ + (self.res, target), + ('name', update), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, **{'name': update}) + self.assertIsNone(result) + + def test_set_description(self): + target = self.resource['id'] + update = 'change-desc' + arglist = [target, '--description', update] + verifylist = [ + (self.res, target), + ('description', update), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, **{'description': update}) + self.assertIsNone(result) + + +class TestShowVPNaaS(test_fakes.TestNeutronClientOSCV2): + + def test_show_filtered_by_id_or_name(self): + target = self.resource['id'] + + def _mock_vpnaas(*args, **kwargs): + return {'id': args[0]} + + self.networkclient.find_vpn_endpoint_group.side_effect = _mock_vpnaas + self.networkclient.find_vpn_ipsec_site_connection.side_effect = \ + _mock_vpnaas + self.networkclient.find_vpn_ike_policy.side_effect = _mock_vpnaas + self.networkclient.find_vpn_ipsec_policy.side_effect = _mock_vpnaas + self.networkclient.find_vpn_service.side_effect = _mock_vpnaas + + arglist = [target] + verifylist = [(self.res, target)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with(target) + self.assertEqual(self.ordered_headers, headers) + self.assertItemEqual(self.ordered_data, data) diff --git a/neutronclient/tests/unit/osc/v2/vpnaas/fakes.py b/neutronclient/tests/unit/osc/v2/vpnaas/fakes.py new file mode 100644 index 000000000..24e6497fc --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/vpnaas/fakes.py @@ -0,0 +1,191 @@ +# Copyright 2017 FUJITSU LIMITED +# All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +import collections +from unittest import mock +import uuid + +from openstack.network.v2 import vpn_endpoint_group as vpn_epg +from openstack.network.v2 import vpn_ike_policy as vpn_ikep +from openstack.network.v2 import vpn_ipsec_policy as vpn_ipsecp +from openstack.network.v2 import vpn_ipsec_site_connection as vpn_sitec +from openstack.network.v2 import vpn_service + + +class FakeVPNaaS(object): + + def create(self, attrs={}): + """Create a fake vpnaas resources + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A OrderedDict faking the vpnaas resource + """ + self.ordered.update(attrs) + if 'IKEPolicy' == self.__class__.__name__: + return vpn_ikep.VpnIkePolicy(**self.ordered) + if 'IPSecPolicy' == self.__class__.__name__: + return vpn_ipsecp.VpnIpsecPolicy(**self.ordered) + if 'VPNService' == self.__class__.__name__: + return vpn_service.VpnService(**self.ordered) + if 'EndpointGroup' == self.__class__.__name__: + return vpn_epg.VpnEndpointGroup(**self.ordered) + + def bulk_create(self, attrs=None, count=2): + """Create multiple fake vpnaas resources + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of vpnaas resources to fake + :return: + A list of dictionaries faking the vpnaas resources + """ + return [self.create(attrs=attrs) for i in range(0, count)] + + def get(self, attrs=None, count=2): + """Get multiple fake vpnaas resources + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of vpnaas resources to fake + :return: + A list of dictionaries faking the vpnaas resource + """ + if attrs is None: + self.attrs = self.bulk_create(count=count) + return mock.Mock(side_effect=attrs) + + +class IKEPolicy(FakeVPNaaS): + """Fake one or more IKE policies""" + + def __init__(self): + super(IKEPolicy, self).__init__() + self.ordered = collections.OrderedDict(( + ('id', 'ikepolicy-id-' + uuid.uuid4().hex), + ('name', 'my-ikepolicy-' + uuid.uuid4().hex), + ('auth_algorithm', 'sha1'), + ('encryption_algorithm', 'aes-128'), + ('ike_version', 'v1'), + ('pfs', 'group5'), + ('description', 'my-desc-' + uuid.uuid4().hex), + ('phase1_negotiation_mode', 'main'), + ('project_id', 'project-id-' + uuid.uuid4().hex), + ('lifetime', {'units': 'seconds', 'value': 3600}), + )) + + +class IPSecPolicy(FakeVPNaaS): + """Fake one or more IPsec policies""" + + def __init__(self): + super(IPSecPolicy, self).__init__() + self.ordered = collections.OrderedDict(( + ('id', 'ikepolicy-id-' + uuid.uuid4().hex), + ('name', 'my-ikepolicy-' + uuid.uuid4().hex), + ('auth_algorithm', 'sha1'), + ('encapsulation_mode', 'tunnel'), + ('transform_protocol', 'esp'), + ('encryption_algorithm', 'aes-128'), + ('pfs', 'group5'), + ('description', 'my-desc-' + uuid.uuid4().hex), + ('project_id', 'project-id-' + uuid.uuid4().hex), + ('lifetime', {'units': 'seconds', 'value': 3600}), + )) + + +class VPNService(FakeVPNaaS): + """Fake one or more VPN services""" + + def __init__(self): + super(VPNService, self).__init__() + self.ordered = collections.OrderedDict(( + ('id', 'vpnservice-id-' + uuid.uuid4().hex), + ('name', 'my-vpnservice-' + uuid.uuid4().hex), + ('router_id', 'router-id-' + uuid.uuid4().hex), + ('subnet_id', 'subnet-id-' + uuid.uuid4().hex), + ('flavor_id', 'flavor-id-' + uuid.uuid4().hex), + ('admin_state_up', True), + ('status', 'ACTIVE'), + ('description', 'my-desc-' + uuid.uuid4().hex), + ('project_id', 'project-id-' + uuid.uuid4().hex), + ('external_v4_ip', '192.0.2.42'), + ('external_v6_ip', '2001:0db8:207a:4a3a:053b:6fab:7df9:1afd'), + )) + + +class EndpointGroup(FakeVPNaaS): + """Fake one or more Endpoint Groups""" + + def __init__(self): + super(EndpointGroup, self).__init__() + self.ordered = collections.OrderedDict(( + ('id', 'ep-group-id-' + uuid.uuid4().hex), + ('name', 'my-ep-group-' + uuid.uuid4().hex), + ('type', 'cidr'), + ('endpoints', ['10.0.0.0/24', '20.0.0.0/24']), + ('description', 'my-desc-' + uuid.uuid4().hex), + ('project_id', 'project-id-' + uuid.uuid4().hex), + )) + + +class IPsecSiteConnection(object): + """Fake one or more IPsec site connections""" + @staticmethod + def create_conn(attrs=None): + """Create a fake IPsec conn. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A Dictionary with id, name, peer_address, auth_mode, status, + project_id, peer_cidrs, vpnservice_id, ipsecpolicy_id, + ikepolicy_id, mtu, initiator, admin_state_up, description, + psk, route_mode, local_id, peer_id, local_ep_group_id, + peer_ep_group_id + """ + attrs = attrs or {} + + # Set default attributes. + conn_attrs = { + 'id': 'ipsec-site-conn-id-' + uuid.uuid4().hex, + 'name': 'my-ipsec-site-conn-' + uuid.uuid4().hex, + 'peer_address': '192.168.2.10', + 'auth_mode': '', + 'status': '', + 'project_id': 'project-id-' + uuid.uuid4().hex, + 'peer_cidrs': [], + 'vpnservice_id': 'vpnservice-id-' + uuid.uuid4().hex, + 'ipsecpolicy_id': 'ipsecpolicy-id-' + uuid.uuid4().hex, + 'ikepolicy_id': 'ikepolicy-id-' + uuid.uuid4().hex, + 'mtu': 1500, + 'initiator': 'bi-directional', + 'admin_state_up': True, + 'description': 'my-vpn-connection', + 'psk': 'abcd', + 'route_mode': '', + 'local_id': '', + 'peer_id': '192.168.2.10', + 'local_ep_group_id': 'local-ep-group-id-' + uuid.uuid4().hex, + 'peer_ep_group_id': 'peer-ep-group-id-' + uuid.uuid4().hex, + } + + # Overwrite default attributes. + conn_attrs.update(attrs) + return vpn_sitec.VpnIPSecSiteConnection(**conn_attrs) diff --git a/neutronclient/tests/unit/osc/v2/vpnaas/test_endpoint_group.py b/neutronclient/tests/unit/osc/v2/vpnaas/test_endpoint_group.py new file mode 100644 index 000000000..e9da939b1 --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/vpnaas/test_endpoint_group.py @@ -0,0 +1,254 @@ +# Copyright 2017 FUJITSU LIMITED +# All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +from unittest import mock + +from osc_lib.tests import utils as tests_utils + +from neutronclient.osc import utils as osc_utils +from neutronclient.osc.v2.vpnaas import endpoint_group +from neutronclient.tests.unit.osc.v2 import fakes as test_fakes +from neutronclient.tests.unit.osc.v2.vpnaas import common +from neutronclient.tests.unit.osc.v2.vpnaas import fakes + + +_endpoint_group = fakes.EndpointGroup().create() +CONVERT_MAP = { + 'project': 'tenant_id', +} + + +def _generate_data(ordered_dict=None, data=None): + source = ordered_dict if ordered_dict else _endpoint_group + if data: + source.update(data) + return source + + +def _generate_req_and_res(verifylist): + request = dict(verifylist) + response = _endpoint_group + for key, val in verifylist: + converted = CONVERT_MAP.get(key, key) + del request[key] + new_value = val + request[converted] = new_value + response[converted] = new_value + return request, response + + +class TestEndpointGroup(test_fakes.TestNeutronClientOSCV2): + + def check_results(self, headers, data, exp_req, is_list=False): + if is_list: + req_body = {self.res_plural: list(exp_req)} + else: + req_body = exp_req + self.mocked.assert_called_once_with(**req_body) + self.assertEqual(self.ordered_headers, tuple(sorted(headers))) + self.assertEqual(self.ordered_data, data) + + def setUp(self): + super(TestEndpointGroup, self).setUp() + + def _mock_endpoint_group(*args, **kwargs): + self.networkclient.find_vpn_endpoint_group.assert_called_once_with( + self.resource['id'], ignore_missing=False) + return {'id': args[0]} + + self.networkclient.find_vpn_endpoint_group.side_effect = mock.Mock( + side_effect=_mock_endpoint_group) + osc_utils.find_project = mock.Mock() + osc_utils.find_project.id = _endpoint_group['project_id'] + self.res = 'endpoint_group' + self.res_plural = 'endpoint_groups' + self.resource = _endpoint_group + self.headers = ( + 'ID', + 'Name', + 'Type', + 'Endpoints', + 'Description', + 'Project', + ) + self.data = _generate_data() + self.ordered_headers = ( + 'Description', + 'Endpoints', + 'ID', + 'Name', + 'Project', + 'Type', + ) + self.ordered_data = ( + _endpoint_group['description'], + _endpoint_group['endpoints'], + _endpoint_group['id'], + _endpoint_group['name'], + _endpoint_group['project_id'], + _endpoint_group['type'], + ) + self.ordered_columns = ( + 'description', + 'endpoints', + 'id', + 'name', + 'project_id', + 'type', + ) + + +class TestCreateEndpointGroup(TestEndpointGroup, common.TestCreateVPNaaS): + + def setUp(self): + super(TestCreateEndpointGroup, self).setUp() + self.networkclient.create_vpn_endpoint_group = mock.Mock( + return_value=_endpoint_group) + self.mocked = self.networkclient.create_vpn_endpoint_group + self.cmd = endpoint_group.CreateEndpointGroup(self.app, self.namespace) + + def _update_expect_response(self, request, response): + """Set expected request and response + + :param request + A dictionary of request body(dict of verifylist) + :param response + A OrderedDict of request body + """ + # Update response body + self.neutronclient.create_endpoint_group.return_value = \ + {self.res: dict(response)} + osc_utils.find_project.return_value.id = response['tenant_id'] + # Update response(finally returns 'data') + self.data = _generate_data(ordered_dict=response) + self.ordered_data = tuple( + response[column] for column in self.ordered_columns + ) + + def _set_all_params_cidr(self, args={}): + name = args.get('name') or 'my-name' + description = args.get('description') or 'my-desc' + endpoint_type = args.get('type') or 'cidr' + endpoints = args.get('endpoints') or ['10.0.0.0/24', '20.0.0.0/24'] + tenant_id = args.get('project_id') or 'my-tenant' + arglist = [ + '--description', description, + '--type', endpoint_type, + '--value', '10.0.0.0/24', + '--value', '20.0.0.0/24', + '--project', tenant_id, + name, + ] + verifylist = [ + ('description', description), + ('type', endpoint_type), + ('endpoints', endpoints), + ('project', tenant_id), + ('name', name), + ] + return arglist, verifylist + + def _test_create_with_all_params_cidr(self, args={}): + arglist, verifylist = self._set_all_params_cidr(args) + request, response = _generate_req_and_res(verifylist) + self._update_expect_response(request, response) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.check_results(headers, data, request) + + def test_create_with_no_options(self): + arglist = [] + verifylist = [] + + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_create_with_all_params_cidr(self): + self._test_create_with_all_params_cidr() + + +class TestDeleteEndpointGroup(TestEndpointGroup, common.TestDeleteVPNaaS): + + def setUp(self): + super(TestDeleteEndpointGroup, self).setUp() + self.networkclient.delete_vpn_endpoint_group = mock.Mock() + self.mocked = self.networkclient.delete_vpn_endpoint_group + self.cmd = endpoint_group.DeleteEndpointGroup(self.app, self.namespace) + + +class TestListEndpointGroup(TestEndpointGroup): + + def setUp(self): + super(TestListEndpointGroup, self).setUp() + self.cmd = endpoint_group.ListEndpointGroup(self.app, self.namespace) + + self.short_header = ( + 'ID', + 'Name', + 'Type', + 'Endpoints', + ) + + self.short_data = ( + _endpoint_group['id'], + _endpoint_group['name'], + _endpoint_group['type'], + _endpoint_group['endpoints'], + ) + + self.networkclient.vpn_endpoint_groups = mock.Mock( + return_value=[_endpoint_group]) + self.mocked = self.networkclient.vpn_endpoint_groups + + def test_list_with_long_option(self): + arglist = ['--long'] + verifylist = [('long', True)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.headers), headers) + + def test_list_with_no_option(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.short_header), headers) + self.assertEqual([self.short_data], list(data)) + + +class TestSetEndpointGroup(TestEndpointGroup, common.TestSetVPNaaS): + + def setUp(self): + super(TestSetEndpointGroup, self).setUp() + self.networkclient.update_vpn_endpoint_group = mock.Mock( + return_value=_endpoint_group) + self.mocked = self.networkclient.update_vpn_endpoint_group + self.cmd = endpoint_group.SetEndpointGroup(self.app, self.namespace) + + +class TestShowEndpointGroup(TestEndpointGroup, common.TestShowVPNaaS): + + def setUp(self): + super(TestShowEndpointGroup, self).setUp() + self.networkclient.get_vpn_endpoint_group = mock.Mock( + return_value=_endpoint_group) + self.mocked = self.networkclient.get_vpn_endpoint_group + self.cmd = endpoint_group.ShowEndpointGroup(self.app, self.namespace) diff --git a/neutronclient/tests/unit/osc/v2/vpnaas/test_ikepolicy.py b/neutronclient/tests/unit/osc/v2/vpnaas/test_ikepolicy.py new file mode 100644 index 000000000..bf68611f8 --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/vpnaas/test_ikepolicy.py @@ -0,0 +1,319 @@ +# Copyright 2017 FUJITSU LIMITED +# All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +from unittest import mock + +from osc_lib.tests import utils as tests_utils + +from neutronclient.osc import utils as osc_utils +from neutronclient.osc.v2.vpnaas import ikepolicy +from neutronclient.tests.unit.osc.v2 import fakes as test_fakes +from neutronclient.tests.unit.osc.v2.vpnaas import common +from neutronclient.tests.unit.osc.v2.vpnaas import fakes + + +_ikepolicy = fakes.IKEPolicy().create() +CONVERT_MAP = { + 'project': 'project_id', +} + + +def _generate_data(ordered_dict=None, data=None): + source = ordered_dict if ordered_dict else _ikepolicy + if data: + source.update(data) + return source + + +def _generate_req_and_res(verifylist): + request = dict(verifylist) + response = _ikepolicy + for key, val in verifylist: + converted = CONVERT_MAP.get(key, key) + del request[key] + new_value = val + request[converted] = new_value + response[converted] = new_value + return request, response + + +class TestIKEPolicy(test_fakes.TestNeutronClientOSCV2): + + def check_results(self, headers, data, exp_req, is_list=False): + if is_list: + req_body = {self.res_plural: list(exp_req)} + else: + req_body = exp_req + self.mocked.assert_called_once_with(**req_body) + self.assertEqual(self.ordered_headers, tuple(sorted(headers))) + self.assertEqual(self.ordered_data, data) + + def setUp(self): + super(TestIKEPolicy, self).setUp() + + def _mock_ikepolicy(*args, **kwargs): + self.networkclient.find_vpn_ike_policy.assert_called_once_with( + self.resource['id'], ignore_missing=False) + return {'id': args[0]} + + self.networkclient.find_vpn_ike_policy.side_effect = mock.Mock( + side_effect=_mock_ikepolicy) + osc_utils.find_project = mock.Mock() + osc_utils.find_project.id = _ikepolicy['project_id'] + self.res = 'ikepolicy' + self.res_plural = 'ikepolicies' + self.resource = _ikepolicy + self.headers = ( + 'ID', + 'Name', + 'Authentication Algorithm', + 'Encryption Algorithm', + 'IKE Version', + 'Perfect Forward Secrecy (PFS)', + 'Description', + 'Phase1 Negotiation Mode', + 'Project', + 'Lifetime', + ) + self.data = _generate_data() + self.ordered_headers = ( + 'Authentication Algorithm', + 'Description', + 'Encryption Algorithm', + 'ID', + 'IKE Version', + 'Lifetime', + 'Name', + 'Perfect Forward Secrecy (PFS)', + 'Phase1 Negotiation Mode', + 'Project', + ) + self.ordered_data = ( + _ikepolicy['auth_algorithm'], + _ikepolicy['description'], + _ikepolicy['encryption_algorithm'], + _ikepolicy['id'], + _ikepolicy['ike_version'], + _ikepolicy['lifetime'], + _ikepolicy['name'], + _ikepolicy['pfs'], + _ikepolicy['phase1_negotiation_mode'], + _ikepolicy['project_id'], + ) + self.ordered_columns = ( + 'auth_algorithm', + 'description', + 'encryption_algorithm', + 'id', + 'ike_version', + 'lifetime', + 'name', + 'pfs', + 'phase1_negotiation_mode', + 'project_id', + ) + + +class TestCreateIKEPolicy(TestIKEPolicy, common.TestCreateVPNaaS): + + def setUp(self): + super(TestCreateIKEPolicy, self).setUp() + self.networkclient.create_vpn_ike_policy = mock.Mock( + return_value=_ikepolicy) + self.mocked = self.networkclient.create_vpn_ike_policy + self.cmd = ikepolicy.CreateIKEPolicy(self.app, self.namespace) + + def _update_expect_response(self, request, response): + """Set expected request and response + + :param request + A dictionary of request body(dict of verifylist) + :param response + A OrderedDict of request body + """ + # Update response body + self.networkclient.create_vpn_ikepolicy.return_value = response + osc_utils.find_project.return_value.id = response['project_id'] + # Update response(finally returns 'data') + self.data = _generate_data(ordered_dict=response) + self.ordered_data = tuple( + response[column] for column in self.ordered_columns + ) + + def _set_all_params(self, args={}): + name = args.get('name') or 'my-name' + description = args.get('description') or 'my-desc' + auth_algorithm = args.get('auth_algorithm') or 'sha1' + encryption_algorithm = args.get('encryption_algorithm') or 'aes-128' + phase1_negotiation_mode = args.get('phase1_negotiation_mode') or 'main' + ike_version = args.get('ike_version') or 'v1' + pfs = args.get('pfs') or 'group5' + tenant_id = args.get('tenant_id') or 'my-tenant' + arglist = [ + '--description', description, + '--auth-algorithm', auth_algorithm, + '--encryption-algorithm', encryption_algorithm, + '--phase1-negotiation-mode', phase1_negotiation_mode, + '--ike-version', ike_version, + '--pfs', pfs, + '--project', tenant_id, + name, + ] + verifylist = [ + ('description', description), + ('auth_algorithm', auth_algorithm), + ('encryption_algorithm', encryption_algorithm), + ('phase1_negotiation_mode', phase1_negotiation_mode), + ('ike_version', ike_version), + ('pfs', pfs), + ('project', tenant_id), + ('name', name), + ] + return arglist, verifylist + + def _test_create_with_all_params(self, args={}): + arglist, verifylist = self._set_all_params(args) + request, response = _generate_req_and_res(verifylist) + self._update_expect_response(request, response) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.check_results(headers, data, request) + + def test_create_with_no_options(self): + arglist = [] + verifylist = [] + + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_create_with_all_params(self): + self._test_create_with_all_params() + + def test_create_with_all_params_name(self): + self._test_create_with_all_params({'name': 'new_ikepolicy'}) + + def test_create_with_all_params_aggressive_mode(self): + self._test_create_with_all_params( + {'phase1_negotiation_mode': 'aggressive'}) + + +class TestDeleteIKEPolicy(TestIKEPolicy, common.TestDeleteVPNaaS): + + def setUp(self): + super(TestDeleteIKEPolicy, self).setUp() + self.networkclient.delete_vpn_ike_policy = mock.Mock() + self.mocked = self.networkclient.delete_vpn_ike_policy + self.cmd = ikepolicy.DeleteIKEPolicy(self.app, self.namespace) + + +class TestListIKEPolicy(TestIKEPolicy): + + def setUp(self): + super(TestListIKEPolicy, self).setUp() + self.cmd = ikepolicy.ListIKEPolicy(self.app, self.namespace) + + self.short_header = ( + 'ID', + 'Name', + 'Authentication Algorithm', + 'Encryption Algorithm', + 'IKE Version', + 'Perfect Forward Secrecy (PFS)', + ) + + self.short_data = ( + _ikepolicy['id'], + _ikepolicy['name'], + _ikepolicy['auth_algorithm'], + _ikepolicy['encryption_algorithm'], + _ikepolicy['ike_version'], + _ikepolicy['pfs'], + ) + + self.networkclient.vpn_ike_policies = mock.Mock( + return_value=[_ikepolicy]) + self.mocked = self.networkclient.vpn_ike_policies + + def test_list_with_long_option(self): + arglist = ['--long'] + verifylist = [('long', True)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.headers), headers) + + def test_list_with_no_option(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.short_header), headers) + self.assertEqual([self.short_data], list(data)) + + +class TestSetIKEPolicy(TestIKEPolicy, common.TestSetVPNaaS): + + def setUp(self): + super(TestSetIKEPolicy, self).setUp() + self.networkclient.update_vpn_ike_policy = mock.Mock( + return_value=_ikepolicy) + self.mocked = self.networkclient.update_vpn_ike_policy + self.cmd = ikepolicy.SetIKEPolicy(self.app, self.namespace) + + def test_set_auth_algorithm_with_sha256(self): + target = self.resource['id'] + auth_algorithm = 'sha256' + arglist = [target, '--auth-algorithm', auth_algorithm] + verifylist = [ + (self.res, target), + ('auth_algorithm', auth_algorithm), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, **{'auth_algorithm': 'sha256'}) + self.assertIsNone(result) + + def test_set_phase1_negotiation_mode_with_aggressive(self): + target = self.resource['id'] + phase1_negotiation_mode = 'aggressive' + arglist = [target, + '--phase1-negotiation-mode', phase1_negotiation_mode] + verifylist = [ + (self.res, target), + ('phase1_negotiation_mode', phase1_negotiation_mode), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, **{'phase1_negotiation_mode': 'aggressive'}) + self.assertIsNone(result) + + +class TestShowIKEPolicy(TestIKEPolicy, common.TestShowVPNaaS): + + def setUp(self): + super(TestShowIKEPolicy, self).setUp() + self.networkclient.get_vpn_ike_policy = mock.Mock( + return_value=_ikepolicy) + self.mocked = self.networkclient.get_vpn_ike_policy + self.cmd = ikepolicy.ShowIKEPolicy(self.app, self.namespace) diff --git a/neutronclient/tests/unit/osc/v2/vpnaas/test_ipsec_site_connection.py b/neutronclient/tests/unit/osc/v2/vpnaas/test_ipsec_site_connection.py new file mode 100644 index 000000000..4929ea843 --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/vpnaas/test_ipsec_site_connection.py @@ -0,0 +1,372 @@ +# Copyright 2017 FUJITSU LIMITED +# All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +from unittest import mock + +from osc_lib.cli import format_columns +from osc_lib.tests import utils as tests_utils + +from neutronclient.osc import utils as osc_utils +from neutronclient.osc.v2.vpnaas import ipsec_site_connection +from neutronclient.tests.unit.osc.v2 import fakes as test_fakes +from neutronclient.tests.unit.osc.v2.vpnaas import common +from neutronclient.tests.unit.osc.v2.vpnaas import fakes + + +_ipsec_site_conn = fakes.IPsecSiteConnection().create_conn() +CONVERT_MAP = { + 'project': 'project_id', + 'ikepolicy': 'ikepolicy_id', + 'ipsecpolicy': 'ipsecpolicy_id', + 'vpnservice': 'vpnservice_id', + 'peer_endpoint_group': 'peer_ep_group_id', + 'local_endpoint_group': 'local_ep_group_id', +} + + +def _generate_data(ordered_dict=None, data=None): + source = ordered_dict if ordered_dict else _ipsec_site_conn + if data: + source.update(data) + return source + + +def _generate_req_and_res(verifylist): + request = dict(verifylist) + response = _ipsec_site_conn + for key, val in verifylist: + converted = CONVERT_MAP.get(key, key) + del request[key] + new_value = val + request[converted] = new_value + response[converted] = new_value + return request, response + + +class TestIPsecSiteConn(test_fakes.TestNeutronClientOSCV2): + + def check_results(self, headers, data, exp_req, is_list=False): + if is_list: + req_body = {self.res_plural: list(exp_req)} + else: + req_body = exp_req + self.mocked.assert_called_once_with(**req_body) + self.assertEqual(self.ordered_headers, tuple(sorted(headers))) + self.assertItemEqual(self.ordered_data, data) + + def setUp(self): + super(TestIPsecSiteConn, self).setUp() + + def _mock_ipsec_site_conn(*args, **kwargs): + return {'id': args[0]} + + self.networkclient.find_vpn_ipsec_site_connection.side_effect = \ + mock.Mock(side_effect=_mock_ipsec_site_conn) + osc_utils.find_project = mock.Mock() + osc_utils.find_project.id = _ipsec_site_conn['project_id'] + self.res = 'ipsec_site_connection' + self.res_plural = 'ipsec_site_connections' + self.resource = _ipsec_site_conn + self.headers = ( + 'ID', + 'Name', + 'Peer Address', + 'Authentication Algorithm', + 'Status', + 'Project', + 'Peer CIDRs', + 'VPN Service', + 'IPSec Policy', + 'IKE Policy', + 'MTU', + 'Initiator', + 'State', + 'Description', + 'Pre-shared Key', + 'Route Mode', + 'Local ID', + 'Peer ID', + 'Local Endpoint Group ID', + 'Peer Endpoint Group ID', + 'DPD', + ) + self.data = _generate_data() + self.ordered_headers = ( + 'Authentication Algorithm', + 'DPD', + 'Description', + 'ID', + 'IKE Policy', + 'IPSec Policy', + 'Initiator', + 'Local Endpoint Group ID', + 'Local ID', + 'MTU', + 'Name', + 'Peer Address', + 'Peer CIDRs', + 'Peer Endpoint Group ID', + 'Peer ID', + 'Pre-shared Key', + 'Project', + 'Route Mode', + 'State', + 'Status', + 'VPN Service', + ) + self.ordered_data = ( + _ipsec_site_conn['auth_mode'], + _ipsec_site_conn['dpd'], + _ipsec_site_conn['description'], + _ipsec_site_conn['id'], + _ipsec_site_conn['ikepolicy_id'], + _ipsec_site_conn['ipsecpolicy_id'], + _ipsec_site_conn['initiator'], + _ipsec_site_conn['local_ep_group_id'], + _ipsec_site_conn['local_id'], + _ipsec_site_conn['mtu'], + _ipsec_site_conn['name'], + _ipsec_site_conn['peer_address'], + format_columns.ListColumn(_ipsec_site_conn['peer_cidrs']), + _ipsec_site_conn['peer_ep_group_id'], + _ipsec_site_conn['peer_id'], + _ipsec_site_conn['psk'], + _ipsec_site_conn['project_id'], + _ipsec_site_conn['route_mode'], + _ipsec_site_conn['admin_state_up'], + _ipsec_site_conn['status'], + _ipsec_site_conn['vpnservice_id'], + ) + + +class TestCreateIPsecSiteConn(TestIPsecSiteConn, common.TestCreateVPNaaS): + + def setUp(self): + super(TestCreateIPsecSiteConn, self).setUp() + self.networkclient.create_vpn_ipsec_site_connection = mock.Mock( + return_value=_ipsec_site_conn) + self.mocked = self.networkclient.create_vpn_ipsec_site_connection + self.cmd = ipsec_site_connection.CreateIPsecSiteConnection( + self.app, self.namespace) + + def _update_expect_response(self, request, response): + """Set expected request and response + + :param request + A dictionary of request body(dict of verifylist) + :param response + A OrderedDict of request body + """ + # Update response body + self.networkclient.create_vpn_ipsec_site_connection.return_value = \ + response + osc_utils.find_project.return_value.id = response['project_id'] + # Update response(finally returns 'data') + self.data = _generate_data(ordered_dict=response) + self.ordered_data = ( + response['auth_mode'], + response['dpd'], + response['description'], + response['id'], + response['ikepolicy_id'], + response['ipsecpolicy_id'], + response['initiator'], + response['local_ep_group_id'], + response['local_id'], + response['mtu'], + response['name'], + response['peer_address'], + format_columns.ListColumn(response['peer_cidrs']), + response['peer_ep_group_id'], + response['peer_id'], + response['psk'], + response['project_id'], + response['route_mode'], + response['admin_state_up'], + response['status'], + response['vpnservice_id'], + ) + + def _set_all_params(self, args={}): + tenant_id = args.get('tenant_id') or 'my-tenant' + name = args.get('name') or 'connection1' + peer_address = args.get('peer_address') or '192.168.2.10' + peer_id = args.get('peer_id') or '192.168.2.10' + psk = args.get('psk') or 'abcd' + mtu = args.get('mtu') or '1500' + initiator = args.get('initiator') or 'bi-directional' + vpnservice_id = args.get('vpnservice') or 'vpnservice_id' + ikepolicy_id = args.get('ikepolicy') or 'ikepolicy_id' + ipsecpolicy_id = args.get('ipsecpolicy') or 'ipsecpolicy_id' + local_ep_group = args.get('local_ep_group_id') or 'local-epg' + peer_ep_group = args.get('peer_ep_group_id') or 'peer-epg' + description = args.get('description') or 'my-vpn-connection' + + arglist = [ + '--project', tenant_id, + '--peer-address', peer_address, + '--peer-id', peer_id, + '--psk', psk, + '--initiator', initiator, + '--vpnservice', vpnservice_id, + '--ikepolicy', ikepolicy_id, + '--ipsecpolicy', ipsecpolicy_id, + '--mtu', mtu, + '--description', description, + '--local-endpoint-group', local_ep_group, + '--peer-endpoint-group', peer_ep_group, + name, + ] + verifylist = [ + ('project', tenant_id), + ('peer_address', peer_address), + ('peer_id', peer_id), + ('psk', psk), + ('initiator', initiator), + ('vpnservice', vpnservice_id), + ('ikepolicy', ikepolicy_id), + ('ipsecpolicy', ipsecpolicy_id), + ('mtu', mtu), + ('description', description), + ('local_endpoint_group', local_ep_group), + ('peer_endpoint_group', peer_ep_group), + ('name', name), + ] + return arglist, verifylist + + def _test_create_with_all_params(self, args={}): + arglist, verifylist = self._set_all_params(args) + request, response = _generate_req_and_res(verifylist) + + def _mock_endpoint_group(*args, **kwargs): + return {'id': args[0]} + + self.networkclient.find_vpn_endpoint_group.side_effect = mock.Mock( + side_effect=_mock_endpoint_group) + self.networkclient.find_vpn_service.side_effect = mock.Mock( + side_effect=_mock_endpoint_group) + self.networkclient.find_vpn_ike_policy.side_effect = mock.Mock( + side_effect=_mock_endpoint_group) + self.networkclient.find_vpn_ipsec_policy.side_effect = mock.Mock( + side_effect=_mock_endpoint_group) + self._update_expect_response(request, response) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.check_results(headers, data, request) + + def test_create_with_no_options(self): + arglist = [] + verifylist = [] + + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_create_with_all_params(self): + self._test_create_with_all_params() + + +class TestDeleteIPsecSiteConn(TestIPsecSiteConn, common.TestDeleteVPNaaS): + + def setUp(self): + super(TestDeleteIPsecSiteConn, self).setUp() + self.networkclient.delete_vpn_ipsec_site_connection = mock.Mock() + self.mocked = self.networkclient.delete_vpn_ipsec_site_connection + self.cmd = ipsec_site_connection.DeleteIPsecSiteConnection( + self.app, self.namespace) + + +class TestListIPsecSiteConn(TestIPsecSiteConn): + + def setUp(self): + super(TestListIPsecSiteConn, self).setUp() + self.cmd = ipsec_site_connection.ListIPsecSiteConnection( + self.app, self.namespace) + + self.short_header = ( + 'ID', + 'Name', + 'Peer Address', + 'Authentication Algorithm', + 'Status', + ) + + self.short_data = ( + _ipsec_site_conn['id'], + _ipsec_site_conn['name'], + _ipsec_site_conn['peer_address'], + _ipsec_site_conn['auth_mode'], + _ipsec_site_conn['status'], + ) + + self.networkclient.vpn_ipsec_site_connections = mock.Mock( + return_value=[_ipsec_site_conn]) + self.mocked = self.networkclient.vpn_ipsec_site_connections + + def test_list_with_long_option(self): + arglist = ['--long'] + verifylist = [('long', True)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.headers), headers) + + def test_list_with_no_option(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.short_header), headers) + self.assertEqual([self.short_data], list(data)) + + +class TestSetIPsecSiteConn(TestIPsecSiteConn, common.TestSetVPNaaS): + + def setUp(self): + super(TestSetIPsecSiteConn, self).setUp() + self.networkclient.update_vpn_ipsec_site_connection = mock.Mock( + return_value=_ipsec_site_conn) + self.mocked = self.networkclient.update_vpn_ipsec_site_connection + self.cmd = ipsec_site_connection.SetIPsecSiteConnection( + self.app, self.namespace) + + def test_set_ipsec_site_conn_with_peer_id(self): + target = self.resource['id'] + peer_id = '192.168.3.10' + arglist = [target, '--peer-id', peer_id] + verifylist = [ + (self.res, target), + ('peer_id', peer_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with(target, **{'peer_id': peer_id}) + self.assertIsNone(result) + + +class TestShowIPsecSiteConn(TestIPsecSiteConn, common.TestShowVPNaaS): + + def setUp(self): + super(TestShowIPsecSiteConn, self).setUp() + self.networkclient.get_vpn_ipsec_site_connection = mock.Mock( + return_value=_ipsec_site_conn) + self.mocked = self.networkclient.get_vpn_ipsec_site_connection + self.cmd = ipsec_site_connection.ShowIPsecSiteConnection( + self.app, self.namespace) diff --git a/neutronclient/tests/unit/osc/v2/vpnaas/test_ipsecpolicy.py b/neutronclient/tests/unit/osc/v2/vpnaas/test_ipsecpolicy.py new file mode 100644 index 000000000..3729a11fe --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/vpnaas/test_ipsecpolicy.py @@ -0,0 +1,299 @@ +# Copyright 2017 FUJITSU LIMITED +# All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +from unittest import mock + +from osc_lib.tests import utils as tests_utils + +from neutronclient.osc import utils as osc_utils +from neutronclient.osc.v2.vpnaas import ipsecpolicy +from neutronclient.tests.unit.osc.v2 import fakes as test_fakes +from neutronclient.tests.unit.osc.v2.vpnaas import common +from neutronclient.tests.unit.osc.v2.vpnaas import fakes + + +_ipsecpolicy = fakes.IPSecPolicy().create() +CONVERT_MAP = { + 'project': 'project_id', +} + + +def _generate_data(ordered_dict=None, data=None): + source = ordered_dict if ordered_dict else _ipsecpolicy + if data: + source.update(data) + return source + + +def _generate_req_and_res(verifylist): + request = dict(verifylist) + response = _ipsecpolicy + for key, val in verifylist: + converted = CONVERT_MAP.get(key, key) + del request[key] + new_value = val + request[converted] = new_value + response[converted] = new_value + return request, response + + +class TestIPSecPolicy(test_fakes.TestNeutronClientOSCV2): + + def check_results(self, headers, data, exp_req, is_list=False): + if is_list: + req_body = {self.res_plural: list(exp_req)} + else: + req_body = exp_req + self.mocked.assert_called_once_with(**req_body) + self.assertEqual(self.ordered_headers, headers) + self.assertEqual(self.ordered_data, data) + + def setUp(self): + super(TestIPSecPolicy, self).setUp() + + def _mock_ipsecpolicy(*args, **kwargs): + self.networkclient.find_vpn_ipsec_policy.assert_called_once_with( + self.resource['id'], ignore_missing=False) + return {'id': args[0]} + + self.networkclient.find_vpn_ipsec_policy.side_effect = mock.Mock( + side_effect=_mock_ipsecpolicy) + osc_utils.find_project = mock.Mock() + osc_utils.find_project.id = _ipsecpolicy['project_id'] + self.res = 'ipsecpolicy' + self.res_plural = 'ipsecpolicies' + self.resource = _ipsecpolicy + self.headers = ( + 'ID', + 'Name', + 'Authentication Algorithm', + 'Encapsulation Mode', + 'Transform Protocol', + 'Encryption Algorithm', + 'Perfect Forward Secrecy (PFS)', + 'Description', + 'Project', + 'Lifetime', + ) + self.data = _generate_data() + self.ordered_headers = ( + 'Authentication Algorithm', + 'Description', + 'Encapsulation Mode', + 'Encryption Algorithm', + 'ID', + 'Lifetime', + 'Name', + 'Perfect Forward Secrecy (PFS)', + 'Project', + 'Transform Protocol', + ) + self.ordered_data = ( + _ipsecpolicy['auth_algorithm'], + _ipsecpolicy['description'], + _ipsecpolicy['encapsulation_mode'], + _ipsecpolicy['encryption_algorithm'], + _ipsecpolicy['id'], + _ipsecpolicy['lifetime'], + _ipsecpolicy['name'], + _ipsecpolicy['pfs'], + _ipsecpolicy['project_id'], + _ipsecpolicy['transform_protocol'], + ) + self.ordered_columns = ( + 'auth_algorithm', + 'description', + 'encapsulation_mode', + 'encryption_algorithm', + 'id', + 'lifetime', + 'name', + 'pfs', + 'project_id', + 'transform_protocol', + ) + + +class TestCreateIPSecPolicy(TestIPSecPolicy, common.TestCreateVPNaaS): + + def setUp(self): + super(TestCreateIPSecPolicy, self).setUp() + self.networkclient.create_vpn_ipsec_policy = mock.Mock( + return_value=_ipsecpolicy) + self.mocked = self.networkclient.create_vpn_ipsec_policy + self.cmd = ipsecpolicy.CreateIPsecPolicy(self.app, self.namespace) + + def _update_expect_response(self, request, response): + """Set expected request and response + + :param request + A dictionary of request body(dict of verifylist) + :param response + A OrderedDict of request body + """ + # Update response body + self.networkclient.create_vpn_ipsec_policy.return_value = \ + response + osc_utils.find_project.return_value.id = response['project_id'] + # Update response(finally returns 'data') + self.data = _generate_data(ordered_dict=response) + self.ordered_data = tuple( + response[column] for column in self.ordered_columns + ) + + def _set_all_params(self, args={}): + name = args.get('name') or 'my-name' + auth_algorithm = args.get('auth_algorithm') or 'sha1' + encapsulation_mode = args.get('encapsulation_mode') or 'tunnel' + transform_protocol = args.get('transform_protocol') or 'esp' + encryption_algorithm = args.get('encryption_algorithm') or 'aes-128' + pfs = args.get('pfs') or 'group5' + description = args.get('description') or 'my-desc' + project_id = args.get('project_id') or 'my-project' + arglist = [ + name, + '--auth-algorithm', auth_algorithm, + '--encapsulation-mode', encapsulation_mode, + '--transform-protocol', transform_protocol, + '--encryption-algorithm', encryption_algorithm, + '--pfs', pfs, + '--description', description, + '--project', project_id, + ] + verifylist = [ + ('name', name), + ('auth_algorithm', auth_algorithm), + ('encapsulation_mode', encapsulation_mode), + ('transform_protocol', transform_protocol), + ('encryption_algorithm', encryption_algorithm), + ('pfs', pfs), + ('description', description), + ('project', project_id), + ] + return arglist, verifylist + + def _test_create_with_all_params(self, args={}): + arglist, verifylist = self._set_all_params(args) + request, response = _generate_req_and_res(verifylist) + self._update_expect_response(request, response) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + self.check_results(headers, data, request) + + def test_create_with_no_options(self): + arglist = [] + verifylist = [] + + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_create_with_all_params(self): + self._test_create_with_all_params() + + def test_create_with_all_params_name(self): + self._test_create_with_all_params({'name': 'new_ipsecpolicy'}) + + +class TestDeleteIPSecPolicy(TestIPSecPolicy, common.TestDeleteVPNaaS): + + def setUp(self): + super(TestDeleteIPSecPolicy, self).setUp() + self.networkclient.delete_vpn_ipsec_policy = mock.Mock() + self.mocked = self.networkclient.delete_vpn_ipsec_policy + self.cmd = ipsecpolicy.DeleteIPsecPolicy(self.app, self.namespace) + + +class TestListIPSecPolicy(TestIPSecPolicy): + + def setUp(self): + super(TestListIPSecPolicy, self).setUp() + self.cmd = ipsecpolicy.ListIPsecPolicy(self.app, self.namespace) + + self.short_header = ( + 'ID', + 'Name', + 'Authentication Algorithm', + 'Encapsulation Mode', + 'Transform Protocol', + 'Encryption Algorithm', + ) + + self.short_data = ( + _ipsecpolicy['id'], + _ipsecpolicy['name'], + _ipsecpolicy['auth_algorithm'], + _ipsecpolicy['encapsulation_mode'], + _ipsecpolicy['transform_protocol'], + _ipsecpolicy['encryption_algorithm'], + ) + + self.networkclient.vpn_ipsec_policies = mock.Mock( + return_value=[_ipsecpolicy]) + self.mocked = self.networkclient.vpn_ipsec_policies + + def test_list_with_long_option(self): + arglist = ['--long'] + verifylist = [('long', True)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.headers), headers) + + def test_list_with_no_option(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.short_header), headers) + self.assertEqual([self.short_data], list(data)) + + +class TestSetIPSecPolicy(TestIPSecPolicy, common.TestSetVPNaaS): + + def setUp(self): + super(TestSetIPSecPolicy, self).setUp() + self.networkclient.update_vpn_ipsec_policy = mock.Mock( + return_value=_ipsecpolicy) + self.mocked = self.networkclient.update_vpn_ipsec_policy + self.cmd = ipsecpolicy.SetIPsecPolicy(self.app, self.namespace) + + def test_set_auth_algorithm_with_sha256(self): + target = self.resource['id'] + auth_algorithm = 'sha256' + arglist = [target, '--auth-algorithm', auth_algorithm] + verifylist = [ + (self.res, target), + ('auth_algorithm', auth_algorithm), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, **{'auth_algorithm': 'sha256'}) + self.assertIsNone(result) + + +class TestShowIPSecPolicy(TestIPSecPolicy, common.TestShowVPNaaS): + + def setUp(self): + super(TestShowIPSecPolicy, self).setUp() + self.networkclient.get_vpn_ipsec_policy = mock.Mock( + return_value=_ipsecpolicy) + self.mocked = self.networkclient.get_vpn_ipsec_policy + self.cmd = ipsecpolicy.ShowIPsecPolicy(self.app, self.namespace) diff --git a/neutronclient/tests/unit/osc/v2/vpnaas/test_vpnservice.py b/neutronclient/tests/unit/osc/v2/vpnaas/test_vpnservice.py new file mode 100644 index 000000000..521fe741c --- /dev/null +++ b/neutronclient/tests/unit/osc/v2/vpnaas/test_vpnservice.py @@ -0,0 +1,305 @@ +# Copyright 2017 FUJITSU LIMITED +# All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +from unittest import mock +import uuid + + +from neutronclient.osc import utils as osc_utils +from neutronclient.osc.v2.vpnaas import vpnservice +from neutronclient.tests.unit.osc.v2 import fakes as test_fakes +from neutronclient.tests.unit.osc.v2.vpnaas import common +from neutronclient.tests.unit.osc.v2.vpnaas import fakes + + +_vpnservice = fakes.VPNService().create() +CONVERT_MAP = { + 'project': 'project_id', + 'router': 'router_id', + 'subnet': 'subnet_id' +} + + +def _generate_data(ordered_dict=None, data=None): + source = ordered_dict if ordered_dict else _vpnservice + if data: + source.update(data) + return source + + +def _generate_req_and_res(verifylist): + request = dict(verifylist) + response = _vpnservice + for key, val in verifylist: + converted = CONVERT_MAP.get(key, key) + del request[key] + new_value = val + request[converted] = new_value + response[converted] = new_value + return request, response + + +class TestVPNService(test_fakes.TestNeutronClientOSCV2): + + def _check_results(self, headers, data, exp_req, is_list=False): + if is_list: + req_body = {self.res_plural: list(exp_req)} + else: + req_body = exp_req + self.mocked.assert_called_once_with(**req_body) + self.assertEqual(self.ordered_headers, headers) + self.assertEqual(self.ordered_data, data) + + def setUp(self): + super(TestVPNService, self).setUp() + + def _mock_vpnservice(*args, **kwargs): + self.networkclient.find_vpn_service.assert_called_once_with( + self.resource['id'], ignore_missing=False) + return {'id': args[0]} + + self.networkclient.find_router = mock.Mock() + self.networkclient.find_subnet = mock.Mock() + self.fake_router = mock.Mock() + self.fake_subnet = mock.Mock() + self.networkclient.find_router.return_value = self.fake_router + self.networkclient.find_subnet.return_value = self.fake_subnet + self.args = { + 'name': 'my-name', + 'description': 'my-desc', + 'project_id': 'project-id-' + uuid.uuid4().hex, + 'router_id': 'router-id-' + uuid.uuid4().hex, + 'subnet_id': 'subnet-id-' + uuid.uuid4().hex, + + } + self.fake_subnet.id = self.args['subnet_id'] + self.fake_router.id = self.args['router_id'] + + self.networkclient.find_vpn_service.side_effect = mock.Mock( + side_effect=_mock_vpnservice) + osc_utils.find_project = mock.Mock() + osc_utils.find_project.id = _vpnservice['project_id'] + + self.res = 'vpnservice' + self.res_plural = 'vpnservices' + self.resource = _vpnservice + self.headers = ( + 'ID', + 'Name', + 'Router', + 'Subnet', + 'Flavor', + 'State', + 'Status', + 'Description', + 'Project', + 'Ext v4 IP', + 'Ext v6 IP', + ) + self.data = _generate_data() + self.ordered_headers = ( + 'Description', + 'Ext v4 IP', + 'Ext v6 IP', + 'Flavor', + 'ID', + 'Name', + 'Project', + 'Router', + 'State', + 'Status', + 'Subnet', + ) + self.ordered_data = ( + _vpnservice['description'], + _vpnservice['external_v4_ip'], + _vpnservice['external_v6_ip'], + _vpnservice['flavor_id'], + _vpnservice['id'], + _vpnservice['name'], + _vpnservice['project_id'], + _vpnservice['router_id'], + _vpnservice['admin_state_up'], + _vpnservice['status'], + _vpnservice['subnet_id'], + ) + self.ordered_columns = ( + 'description', + 'external_v4_ip', + 'external_v6_ip', + 'flavor_id', + 'id', + 'name', + 'project_id', + 'router_id', + 'admin_state_up', + 'status', + 'subnet_id', + ) + + +class TestCreateVPNService(TestVPNService, common.TestCreateVPNaaS): + + def setUp(self): + super(TestCreateVPNService, self).setUp() + self.networkclient.create_vpn_service = mock.Mock( + return_value=_vpnservice) + self.mocked = self.networkclient.create_vpn_service + self.cmd = vpnservice.CreateVPNService(self.app, self.namespace) + + def _update_expect_response(self, request, response): + """Set expected request and response + + :param request + A dictionary of request body(dict of verifylist) + :param response + A OrderedDict of request body + """ + # Update response body + self.networkclient.create_vpn_service.return_value = response + osc_utils.find_project.return_value.id = response['project_id'] + # Update response(finally returns 'data') + self.data = _generate_data(ordered_dict=response) + self.ordered_data = tuple( + response[column] for column in self.ordered_columns + ) + + def _set_all_params(self): + name = self.args.get('name') + description = self.args.get('description') + router_id = self.args.get('router_id') + subnet_id = self.args.get('subnet_id') + project_id = self.args.get('project_id') + arglist = [ + '--description', description, + '--project', project_id, + '--subnet', subnet_id, + '--router', router_id, + name, + ] + verifylist = [ + ('description', description), + ('project', project_id), + ('subnet', subnet_id), + ('router', router_id), + ('name', name), + ] + return arglist, verifylist + + def _test_create_with_all_params(self): + arglist, verifylist = self._set_all_params() + request, response = _generate_req_and_res(verifylist) + self._update_expect_response(request, response) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self._check_results(headers, data, request) + + def test_create_with_all_params(self): + self._test_create_with_all_params() + + +class TestDeleteVPNService(TestVPNService, common.TestDeleteVPNaaS): + + def setUp(self): + super(TestDeleteVPNService, self).setUp() + self.networkclient.delete_vpn_service = mock.Mock() + self.mocked = self.networkclient.delete_vpn_service + self.cmd = vpnservice.DeleteVPNService(self.app, self.namespace) + + +class TestListVPNService(TestVPNService): + + def setUp(self): + super(TestListVPNService, self).setUp() + self.cmd = vpnservice.ListVPNService(self.app, self.namespace) + + self.short_header = ( + 'ID', + 'Name', + 'Router', + 'Subnet', + 'Flavor', + 'State', + 'Status', + ) + + self.short_data = ( + _vpnservice['id'], + _vpnservice['name'], + _vpnservice['router_id'], + _vpnservice['subnet_id'], + _vpnservice['flavor_id'], + _vpnservice['admin_state_up'], + _vpnservice['status'], + ) + + self.networkclient.vpn_services = mock.Mock(return_value=[_vpnservice]) + self.mocked = self.networkclient.vpn_services + + def test_list_with_long_option(self): + arglist = ['--long'] + verifylist = [('long', True)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.headers), headers) + + def test_list_with_no_option(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + headers, data = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with() + self.assertEqual(list(self.short_header), headers) + self.assertEqual([self.short_data], list(data)) + + +class TestSetVPNService(TestVPNService, common.TestSetVPNaaS): + + def setUp(self): + super(TestSetVPNService, self).setUp() + self.networkclient.update_vpn_service = mock.Mock( + return_value=_vpnservice) + self.mocked = self.networkclient.update_vpn_service + self.cmd = vpnservice.SetVPNSercice(self.app, self.namespace) + + def test_set_name(self): + target = self.resource['id'] + update = 'change' + arglist = [target, '--name', update] + verifylist = [ + (self.res, target), + ('name', update), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.mocked.assert_called_once_with( + target, **{'name': update}) + self.assertIsNone(result) + + +class TestShowVPNService(TestVPNService, common.TestShowVPNaaS): + + def setUp(self): + super(TestShowVPNService, self).setUp() + self.networkclient.get_vpn_service = mock.Mock( + return_value=_vpnservice) + self.mocked = self.networkclient.get_vpn_service + self.cmd = vpnservice.ShowVPNService(self.app, self.namespace) diff --git a/neutronclient/tests/unit/qos/__init__.py b/neutronclient/tests/unit/qos/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutronclient/tests/unit/qos/test_cli20_bandwidth_limit_rule.py b/neutronclient/tests/unit/qos/test_cli20_bandwidth_limit_rule.py deleted file mode 100644 index d1b39c76d..000000000 --- a/neutronclient/tests/unit/qos/test_cli20_bandwidth_limit_rule.py +++ /dev/null @@ -1,137 +0,0 @@ -# Copyright 2015 Huawei Technologies India Pvt Ltd. -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import sys - -from neutronclient.neutron.v2_0.qos import bandwidth_limit_rule as bw_rule -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20QoSBandwidthLimitRuleJSON(test_cli20.CLITestV20Base): - - non_admin_status_resources = ['bandwidth_limit_rule'] - - def setUp(self): - super(CLITestV20QoSBandwidthLimitRuleJSON, self).setUp() - self.res = 'bandwidth_limit_rule' - self.cmd_res = 'qos_bandwidth_limit_rule' - self.ress = self.res + 's' - self.cmd_ress = self.cmd_res + 's' - - def test_create_bandwidth_limit_rule_with_max_kbps(self): - cmd = bw_rule.CreateQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), - None) - my_id = 'my-id' - max_kbps = '1337' - policy_id = 'policy_id' - args = ['--max-kbps', max_kbps, policy_id] - position_names = ['max_kbps'] - position_values = [max_kbps] - self._test_create_resource(self.res, cmd, '', my_id, args, - position_names, position_values, - cmd_resource=self.cmd_res, - parent_id=policy_id) - - def test_create_bandwidth_limit_rule_with_max_burst_kbps(self): - cmd = bw_rule.CreateQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), - None) - my_id = 'my-id' - max_burst_kbps = '1337' - policy_id = 'policy_id' - args = ['--max-burst-kbps', max_burst_kbps, policy_id] - position_names = ['max_burst_kbps'] - position_values = [max_burst_kbps] - self._test_create_resource(self.res, cmd, '', my_id, args, - position_names, position_values, - cmd_resource=self.cmd_res, - parent_id=policy_id) - - def test_create_bandwidth_limit_rule_with_all_params(self): - cmd = bw_rule.CreateQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), - None) - my_id = 'my-id' - max_kbps = '1337' - max_burst_kbps = '1337' - policy_id = 'policy_id' - args = ['--max-kbps', max_kbps, - '--max-burst-kbps', max_burst_kbps, - policy_id] - position_names = ['max_kbps', 'max_burst_kbps'] - position_values = [max_kbps, max_burst_kbps] - self._test_create_resource(self.res, cmd, '', my_id, args, - position_names, position_values, - cmd_resource=self.cmd_res, - parent_id=policy_id) - - def test_update_bandwidth_limit_rule_with_max_kbps(self): - cmd = bw_rule.UpdateQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), - None) - my_id = 'my-id' - max_kbps = '1337' - policy_id = 'policy_id' - args = ['--max-kbps', max_kbps, my_id, policy_id] - self._test_update_resource(self.res, cmd, my_id, args, - {'max_kbps': max_kbps, }, - cmd_resource=self.cmd_res, - parent_id=policy_id) - - def test_update_bandwidth_limit_rule_with_max_burst_kbps(self): - cmd = bw_rule.UpdateQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), - None) - my_id = 'my-id' - max_burst_kbps = '1337' - policy_id = 'policy_id' - args = ['--max-burst-kbps', max_burst_kbps, - my_id, policy_id] - self._test_update_resource(self.res, cmd, my_id, args, - {'max_burst_kbps': max_burst_kbps}, - cmd_resource=self.cmd_res, - parent_id=policy_id) - - def test_update_bandwidth_limit_rule_with_all_params(self): - cmd = bw_rule.UpdateQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), - None) - my_id = 'my-id' - max_kbps = '1337' - max_burst_kbps = '1337' - policy_id = 'policy_id' - args = ['--max-kbps', max_kbps, - '--max-burst-kbps', max_burst_kbps, - my_id, policy_id] - self._test_update_resource(self.res, cmd, my_id, args, - {'max_kbps': max_kbps, - 'max_burst_kbps': max_burst_kbps}, - cmd_resource=self.cmd_res, - parent_id=policy_id) - - def test_delete_bandwidth_limit_rule(self): - cmd = bw_rule.DeleteQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), - None) - my_id = 'my-id' - policy_id = 'policy_id' - args = [my_id, policy_id] - self._test_delete_resource(self.res, cmd, my_id, args, - cmd_resource=self.cmd_res, - parent_id=policy_id) - - def test_show_bandwidth_limit_rule(self): - cmd = bw_rule.ShowQoSBandwidthLimitRule(test_cli20.MyApp(sys.stdout), - None) - policy_id = 'policy_id' - args = [self.test_id, policy_id] - self._test_show_resource(self.res, cmd, self.test_id, args, - [], cmd_resource=self.cmd_res, - parent_id=policy_id) diff --git a/neutronclient/tests/unit/qos/test_cli20_dscp_marking_rule.py b/neutronclient/tests/unit/qos/test_cli20_dscp_marking_rule.py deleted file mode 100644 index 74c941618..000000000 --- a/neutronclient/tests/unit/qos/test_cli20_dscp_marking_rule.py +++ /dev/null @@ -1,91 +0,0 @@ -# Copyright 2016 Comcast Inc. -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import sys - -from neutronclient.common import exceptions -from neutronclient.neutron.v2_0.qos import dscp_marking_rule as dscp_rule -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20QoSDscpMarkingRuleJSON(test_cli20.CLITestV20Base): - - non_admin_status_resources = ['dscp_marking_rule'] - - def setUp(self): - super(CLITestV20QoSDscpMarkingRuleJSON, self).setUp() - self.dscp_res = 'dscp_marking_rule' - self.dscp_cmd_res = 'qos_dscp_marking_rule' - self.dscp_ress = self.dscp_res + 's' - self.dscp_cmd_ress = self.dscp_cmd_res + 's' - - def test_create_dscp_marking_rule_with_dscp_mark(self): - cmd = dscp_rule.CreateQoSDscpMarkingRule(test_cli20.MyApp(sys.stdout), - None) - my_id = 'my-id' - policy_id = 'policy_id' - position_names = ['dscp_mark'] - valid_dscp_marks = ['0', '56'] - invalid_dscp_marks = ['-1', '19', '42', '44', '57', '58'] - for dscp_mark in valid_dscp_marks: - args = ['--dscp-mark', dscp_mark, policy_id] - position_values = [dscp_mark] - self._test_create_resource(self.dscp_res, cmd, '', my_id, args, - position_names, position_values, - cmd_resource=self.dscp_cmd_res, - parent_id=policy_id) - for dscp_mark in invalid_dscp_marks: - args = ['--dscp-mark', dscp_mark, policy_id] - position_values = [dscp_mark] - self._test_create_resource( - self.dscp_res, cmd, '', my_id, args, - position_names, position_values, - cmd_resource=self.dscp_cmd_res, - parent_id=policy_id, - no_api_call=True, - expected_exception=exceptions.CommandError) - - def test_update_dscp_marking_rule_with_dscp_mark(self): - cmd = dscp_rule.UpdateQoSDscpMarkingRule(test_cli20.MyApp(sys.stdout), - None) - my_id = 'my-id' - dscp_mark = '56' - policy_id = 'policy_id' - args = ['--dscp-mark', dscp_mark, - my_id, policy_id] - self._test_update_resource(self.dscp_res, cmd, my_id, args, - {'dscp_mark': dscp_mark}, - cmd_resource=self.dscp_cmd_res, - parent_id=policy_id) - - def test_delete_dscp_marking_rule(self): - cmd = dscp_rule.DeleteQoSDscpMarkingRule(test_cli20.MyApp(sys.stdout), - None) - my_id = 'my-id' - policy_id = 'policy_id' - args = [my_id, policy_id] - self._test_delete_resource(self.dscp_res, cmd, my_id, args, - cmd_resource=self.dscp_cmd_res, - parent_id=policy_id) - - def test_show_dscp_marking_rule(self): - cmd = dscp_rule.ShowQoSDscpMarkingRule(test_cli20.MyApp(sys.stdout), - None) - policy_id = 'policy_id' - args = [self.test_id, policy_id] - self._test_show_resource(self.dscp_res, cmd, self.test_id, args, - [], cmd_resource=self.dscp_cmd_res, - parent_id=policy_id) diff --git a/neutronclient/tests/unit/qos/test_cli20_policy.py b/neutronclient/tests/unit/qos/test_cli20_policy.py deleted file mode 100644 index b1db866e4..000000000 --- a/neutronclient/tests/unit/qos/test_cli20_policy.py +++ /dev/null @@ -1,192 +0,0 @@ -# Copyright 2015 Huawei Technologies India Pvt Ltd. -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import sys - -from neutronclient.neutron.v2_0.qos import policy as policy -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20QoSPolicyJSON(test_cli20.CLITestV20Base): - - non_admin_status_resources = ['policy'] - - def setUp(self): - super(CLITestV20QoSPolicyJSON, self).setUp() - self.res = 'policy' - self.cmd_res = 'qos_policy' - self.ress = "policies" - self.cmd_ress = 'qos_policies' - - def test_create_policy_with_only_keyattributes(self): - # Create qos policy abc. - cmd = policy.CreateQoSPolicy(test_cli20.MyApp(sys.stdout), - None) - myid = 'myid' - name = 'abc' - args = [name] - position_names = ['name'] - position_values = [name] - self._test_create_resource(self.res, cmd, name, myid, args, - position_names, position_values, - cmd_resource=self.cmd_res) - - def test_create_policy_with_description(self): - # Create qos policy xyz --description abc. - cmd = policy.CreateQoSPolicy(test_cli20.MyApp(sys.stdout), - None) - myid = 'myid' - name = 'abc' - description = 'policy_abc' - args = [name, '--description', description] - position_names = ['name', 'description'] - position_values = [name, description] - self._test_create_resource(self.res, cmd, name, myid, args, - position_names, position_values, - cmd_resource=self.cmd_res) - - def test_create_policy_with_shared(self): - # Create qos policy abc shared across tenants - cmd = policy.CreateQoSPolicy(test_cli20.MyApp(sys.stdout), - None) - myid = 'myid' - name = 'abc' - description = 'policy_abc' - args = [name, '--description', description, '--shared'] - position_names = ['name', 'description', 'shared'] - position_values = [name, description, True] - self._test_create_resource(self.res, cmd, name, myid, args, - position_names, position_values, - cmd_resource=self.cmd_res) - - def test_create_policy_with_unicode(self): - # Create qos policy u'\u7f51\u7edc'. - cmd = policy.CreateQoSPolicy(test_cli20.MyApp(sys.stdout), - None) - myid = 'myid' - name = u'\u7f51\u7edc' - description = u'\u7f51\u7edc' - args = [name, '--description', description] - position_names = ['name', 'description'] - position_values = [name, description] - self._test_create_resource(self.res, cmd, name, myid, args, - position_names, position_values, - cmd_resource=self.cmd_res) - - def test_update_policy(self): - # policy-update myid --name newname. - cmd = policy.UpdateQoSPolicy(test_cli20.MyApp(sys.stdout), - None) - self._test_update_resource(self.res, cmd, 'myid', - ['myid', '--name', 'newname'], - {'name': 'newname', }, - cmd_resource=self.cmd_res) - - def test_update_policy_description(self): - # policy-update myid --name newname --description newdesc - cmd = policy.UpdateQoSPolicy(test_cli20.MyApp(sys.stdout), - None) - self._test_update_resource(self.res, cmd, 'myid', - ['myid', '--description', 'newdesc'], - {'description': 'newdesc', }, - cmd_resource=self.cmd_res) - - def test_update_policy_to_shared(self): - # policy-update myid --shared - cmd = policy.UpdateQoSPolicy(test_cli20.MyApp(sys.stdout), - None) - self._test_update_resource(self.res, cmd, 'myid', - ['myid', '--shared'], - {'shared': True, }, - cmd_resource=self.cmd_res) - - def test_update_policy_to_no_shared(self): - # policy-update myid --no-shared - cmd = policy.UpdateQoSPolicy(test_cli20.MyApp(sys.stdout), - None) - self._test_update_resource(self.res, cmd, 'myid', - ['myid', '--no-shared'], - {'shared': False, }, - cmd_resource=self.cmd_res) - - def test_update_policy_to_shared_no_shared_together(self): - # policy-update myid --shared --no-shared - cmd = policy.UpdateQoSPolicy(test_cli20.MyApp(sys.stdout), - None) - self.assertRaises( - SystemExit, - self._test_update_resource, - self.res, cmd, 'myid', - ['myid', '--shared', '--no-shared'], {}, - cmd_resource=self.cmd_res - ) - - def test_list_policies(self): - # qos-policy-list. - cmd = policy.ListQoSPolicy(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(self.ress, cmd, True, - cmd_resources=self.cmd_ress) - - def test_list_policies_pagination(self): - # qos-policy-list for pagination. - cmd = policy.ListQoSPolicy(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources_with_pagination(self.ress, cmd, - cmd_resources=self.cmd_ress) - - def test_list_policies_sort(self): - # sorted list: qos-policy-list --sort-key name --sort-key id - # --sort-key asc --sort-key desc - cmd = policy.ListQoSPolicy(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(self.ress, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"], - cmd_resources=self.cmd_ress) - - def test_list_policies_limit(self): - # size (1000) limited list: qos-policy-list -P. - cmd = policy.ListQoSPolicy(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(self.ress, cmd, page_size=1000, - cmd_resources=self.cmd_ress) - - def test_show_policy_id(self): - # qos-policy-show test_id. - cmd = policy.ShowQoSPolicy(test_cli20.MyApp(sys.stdout), - None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(self.res, cmd, self.test_id, args, - ['id'], cmd_resource=self.cmd_res) - - def test_show_policy_name(self): - # qos-policy-show. - cmd = policy.ShowQoSPolicy(test_cli20.MyApp(sys.stdout), - None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(self.res, cmd, self.test_id, - args, ['id', 'name'], - cmd_resource=self.cmd_res) - - def test_delete_policy(self): - # qos-policy-delete my-id. - cmd = policy.DeleteQoSPolicy(test_cli20.MyApp(sys.stdout), - None) - my_id = 'myid1' - args = [my_id] - self._test_delete_resource(self.res, cmd, my_id, args, - cmd_resource=self.cmd_res) diff --git a/neutronclient/tests/unit/qos/test_cli20_rule.py b/neutronclient/tests/unit/qos/test_cli20_rule.py deleted file mode 100644 index bdbf52df9..000000000 --- a/neutronclient/tests/unit/qos/test_cli20_rule.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright 2015 Huawei Technologies India Pvt Ltd. -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import sys - -from neutronclient.neutron.v2_0.qos import rule as qos_rule -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20QoSRuleJSON(test_cli20.CLITestV20Base): - - non_admin_status_resources = ['bandwidth_limit_rule', 'dscp_marking_rule'] - - def setUp(self): - super(CLITestV20QoSRuleJSON, self).setUp() - - def test_list_qos_rule_types(self): - # qos_rule_types. - resources = 'rule_types' - cmd_resources = 'qos_rule_types' - response_contents = [{'type': 'bandwidth_limit', - 'type': 'dscp_marking'}] - - cmd = qos_rule.ListQoSRuleTypes(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, True, - response_contents=response_contents, - cmd_resources=cmd_resources) diff --git a/neutronclient/tests/unit/test_auto_allocated_topology.py b/neutronclient/tests/unit/test_auto_allocated_topology.py deleted file mode 100644 index af2742b0e..000000000 --- a/neutronclient/tests/unit/test_auto_allocated_topology.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright 2016 IBM -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import sys - -from neutronclient.neutron.v2_0 import auto_allocated_topology as aat -from neutronclient.tests.unit import test_cli20 - - -class TestAutoAllocatedTopologyJSON(test_cli20.CLITestV20Base): - - def test_show_auto_allocated_topology_arg(self): - resource = 'auto_allocated_topology' - cmd = aat.ShowAutoAllocatedTopology(test_cli20.MyApp(sys.stdout), None) - args = ['--tenant-id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args) - - def test_show_auto_allocated_topology_posarg(self): - resource = 'auto_allocated_topology' - cmd = aat.ShowAutoAllocatedTopology(test_cli20.MyApp(sys.stdout), None) - args = ['some-tenant'] - self._test_show_resource(resource, cmd, "some-tenant", args) - - def test_show_auto_allocated_topology_no_arg(self): - resource = 'auto_allocated_topology' - cmd = aat.ShowAutoAllocatedTopology(test_cli20.MyApp(sys.stdout), None) - args = [] - self._test_show_resource(resource, cmd, "None", args) - - def test_show_auto_allocated_topology_dry_run_as_tenant(self): - resource = 'auto_allocated_topology' - cmd = aat.ShowAutoAllocatedTopology(test_cli20.MyApp(sys.stdout), None) - args = ['--dry-run'] - self._test_show_resource(resource, cmd, "None", args, - fields=('dry-run',)) - - def test_show_auto_allocated_topology_dry_run_as_admin(self): - resource = 'auto_allocated_topology' - cmd = aat.ShowAutoAllocatedTopology(test_cli20.MyApp(sys.stdout), None) - args = ['--dry-run', 'some-tenant'] - self._test_show_resource(resource, cmd, "some-tenant", args, - fields=('dry-run',)) diff --git a/neutronclient/tests/unit/test_casual_args.py b/neutronclient/tests/unit/test_casual_args.py index dd87a5399..cbf061617 100644 --- a/neutronclient/tests/unit/test_casual_args.py +++ b/neutronclient/tests/unit/test_casual_args.py @@ -41,6 +41,13 @@ def test_bool_false(self): _mydict = neutronV20.parse_args_to_dict(_specs) self.assertFalse(_mydict['my_bool']) + def test_int_and_str(self): + _specs = ['--my-int', 'type=int', '10', + '--my-str', 'type=str', 'value1'] + _mydict = neutronV20.parse_args_to_dict(_specs) + self.assertEqual(10, _mydict['my_int']) + self.assertEqual('value1', _mydict['my_str']) + def test_nargs(self): _specs = ['--tag', 'x', 'y', '--arg1', 'value1'] _mydict = neutronV20.parse_args_to_dict(_specs) @@ -102,15 +109,25 @@ def test_list_of_dict_arg(self): self.assertEqual('value1', arg1[0]['key1']) self.assertEqual('value2', arg1[0]['key2']) + def test_parse_args_to_dict_bad_type(self): + _specs = ['--badtypearg', 'type=badtype', 'val1'] + ex = self.assertRaises(exceptions.CommandError, + neutronV20.parse_args_to_dict, _specs) + self.assertEqual('Invalid value_specs --badtypearg type=badtype val1: ' + 'type badtype is not supported', + str(ex)) + def test_clear_action(self): _specs = ['--anyarg', 'action=clear'] args = neutronV20.parse_args_to_dict(_specs) self.assertIsNone(args['anyarg']) - def test_bad_values_str(self): + def test_bad_values_str_without_value(self): _specs = ['--strarg', 'type=str'] - self.assertRaises(exceptions.CommandError, - neutronV20.parse_args_to_dict, _specs) + ex = self.assertRaises(exceptions.CommandError, + neutronV20.parse_args_to_dict, _specs) + self.assertEqual('Invalid values_specs --strarg type=str', + str(ex)) def test_bad_values_list(self): _specs = ['--listarg', 'list=true', 'type=str'] diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py deleted file mode 100644 index c8fec064c..000000000 --- a/neutronclient/tests/unit/test_cli20.py +++ /dev/null @@ -1,1030 +0,0 @@ -# Copyright 2012 OpenStack Foundation. -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import contextlib -import itertools -import sys - -import mock -from mox3 import mox -from oslo_utils import encodeutils -from oslotest import base -import requests -import six -import six.moves.urllib.parse as urlparse - -from neutronclient.common import constants -from neutronclient.common import exceptions -from neutronclient.common import utils -from neutronclient.neutron import v2_0 as neutronV2_0 -from neutronclient import shell -from neutronclient.v2_0 import client - -API_VERSION = "2.0" -FORMAT = 'json' -TOKEN = 'testtoken' -ENDURL = 'localurl' -REQUEST_ID = 'test_request_id' - - -@contextlib.contextmanager -def capture_std_streams(): - fake_stdout, fake_stderr = six.StringIO(), six.StringIO() - stdout, stderr = sys.stdout, sys.stderr - try: - sys.stdout, sys.stderr = fake_stdout, fake_stderr - yield fake_stdout, fake_stderr - finally: - sys.stdout, sys.stderr = stdout, stderr - - -class FakeStdout(object): - - def __init__(self): - self.content = [] - - def write(self, text): - self.content.append(text) - - def make_string(self): - result = '' - for line in self.content: - result += encodeutils.safe_decode(line, 'utf-8') - return result - - -class MyRequest(requests.Request): - def __init__(self, method=None): - self.method = method - - -class MyResp(requests.Response): - def __init__(self, status_code, headers=None, reason=None, - request=None, url=None): - self.status_code = status_code - self.headers = headers or {} - self.reason = reason - self.request = request or MyRequest() - self.url = url - - -class MyApp(object): - def __init__(self, _stdout): - self.stdout = _stdout - - -def end_url(path, query=None, format=FORMAT): - _url_str = ENDURL + "/v" + API_VERSION + path + "." + format - return query and _url_str + "?" + query or _url_str - - -class MyUrlComparator(mox.Comparator): - def __init__(self, lhs, client): - self.lhs = lhs - self.client = client - - def equals(self, rhs): - lhsp = urlparse.urlparse(self.lhs) - rhsp = urlparse.urlparse(rhs) - - lhs_qs = urlparse.parse_qsl(lhsp.query) - rhs_qs = urlparse.parse_qsl(rhsp.query) - - return (lhsp.scheme == rhsp.scheme and - lhsp.netloc == rhsp.netloc and - lhsp.path == rhsp.path and - len(lhs_qs) == len(rhs_qs) and - set(lhs_qs) == set(rhs_qs)) - - def __str__(self): - if self.client and self.client.format != FORMAT: - lhs_parts = self.lhs.split("?", 1) - if len(lhs_parts) == 2: - lhs = ("%s.%s?%s" % (lhs_parts[0][:-4], - self.client.format, - lhs_parts[1])) - else: - lhs = ("%s.%s" % (lhs_parts[0][:-4], - self.client.format)) - return lhs - return self.lhs - - def __repr__(self): - return str(self) - - -class MyComparator(mox.Comparator): - def __init__(self, lhs, client): - self.lhs = lhs - self.client = client - - def _com_dict(self, lhs, rhs): - if len(lhs) != len(rhs): - return False - for key, value in six.iteritems(lhs): - if key not in rhs: - return False - rhs_value = rhs[key] - if not self._com(value, rhs_value): - return False - return True - - def _com_list(self, lhs, rhs): - if len(lhs) != len(rhs): - return False - for lhs_value in lhs: - if lhs_value not in rhs: - return False - return True - - def _com(self, lhs, rhs): - if lhs is None: - return rhs is None - if isinstance(lhs, dict): - if not isinstance(rhs, dict): - return False - return self._com_dict(lhs, rhs) - if isinstance(lhs, list): - if not isinstance(rhs, list): - return False - return self._com_list(lhs, rhs) - if isinstance(lhs, tuple): - if not isinstance(rhs, tuple): - return False - return self._com_list(lhs, rhs) - return lhs == rhs - - def equals(self, rhs): - if self.client: - rhs = self.client.deserialize(rhs, 200) - return self._com(self.lhs, rhs) - - def __repr__(self): - if self.client: - return self.client.serialize(self.lhs) - return str(self.lhs) - - -class CLITestV20Base(base.BaseTestCase): - - format = 'json' - test_id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' - id_field = 'id' - - non_admin_status_resources = [] - - def _find_resourceid(self, client, resource, name_or_id, - cmd_resource=None, parent_id=None): - return name_or_id - - def setUp(self, plurals=None): - """Prepare the test environment.""" - super(CLITestV20Base, self).setUp() - client.Client.EXTED_PLURALS.update(constants.PLURALS) - if plurals is not None: - client.Client.EXTED_PLURALS.update(plurals) - self.metadata = {'plurals': client.Client.EXTED_PLURALS} - self.mox = mox.Mox() - self.endurl = ENDURL - self.fake_stdout = FakeStdout() - - self.addCleanup(mock.patch.stopall) - mock.patch('sys.stdout', new=self.fake_stdout).start() - mock.patch('neutronclient.neutron.v2_0.find_resourceid_by_name_or_id', - new=self._find_resourceid).start() - mock.patch('neutronclient.neutron.v2_0.find_resourceid_by_id', - new=self._find_resourceid).start() - - self.client = client.Client(token=TOKEN, endpoint_url=self.endurl) - self.client.format = self.format - - def register_non_admin_status_resource(self, resource_name): - # TODO(amotoki): - # It is recommended to define - # "non_admin_status_resources in each test class rather than - # using register_non_admin_status_resource method. - - # If we change self.non_admin_status_resources like this, - # we need to ensure this should be an instance variable - # to avoid changing the class variable. - if (id(self.non_admin_status_resources) == - id(self.__class__.non_admin_status_resources)): - self.non_admin_status_resources = (self.__class__. - non_admin_status_resources[:]) - self.non_admin_status_resources.append(resource_name) - - def _test_create_resource(self, resource, cmd, name, myid, args, - position_names, position_values, - tenant_id=None, tags=None, admin_state_up=True, - extra_body=None, cmd_resource=None, - parent_id=None, no_api_call=False, - expected_exception=None, - **kwargs): - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) - if not cmd_resource: - cmd_resource = resource - if (resource in self.non_admin_status_resources): - body = {resource: {}, } - else: - body = {resource: {'admin_state_up': admin_state_up, }, } - if tenant_id: - body[resource].update({'tenant_id': tenant_id}) - if tags: - body[resource].update({'tags': tags}) - if extra_body: - body[resource].update(extra_body) - body[resource].update(kwargs) - - for i in range(len(position_names)): - body[resource].update({position_names[i]: position_values[i]}) - ress = {resource: - {self.id_field: myid}, } - if name: - ress[resource].update({'name': name}) - resstr = self.client.serialize(ress) - # url method body - resource_plural = self.client.get_resource_plural(cmd_resource) - path = getattr(self.client, resource_plural + "_path") - if parent_id: - path = path % parent_id - mox_body = MyComparator(body, self.client) - - if not no_api_call: - self.client.httpclient.request( - end_url(path, format=self.format), 'POST', - body=mox_body, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr)) - self.mox.ReplayAll() - cmd_parser = cmd.get_parser('create_' + resource) - if expected_exception: - self.assertRaises(expected_exception, - shell.run_command, cmd, cmd_parser, args) - else: - shell.run_command(cmd, cmd_parser, args) - _str = self.fake_stdout.make_string() - self.assertIn(myid, _str) - if name: - self.assertIn(name, _str) - self.mox.VerifyAll() - self.mox.UnsetStubs() - - def _test_list_columns(self, cmd, resources, - resources_out, args=('-f', 'json'), - cmd_resources=None, parent_id=None): - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) - if not cmd_resources: - cmd_resources = resources - - resstr = self.client.serialize(resources_out) - - path = getattr(self.client, cmd_resources + "_path") - if parent_id: - path = path % parent_id - self.client.httpclient.request( - end_url(path, format=self.format), 'GET', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr)) - self.mox.ReplayAll() - cmd_parser = cmd.get_parser("list_" + cmd_resources) - shell.run_command(cmd, cmd_parser, args) - self.mox.VerifyAll() - self.mox.UnsetStubs() - - def _test_list_resources(self, resources, cmd, detail=False, tags=(), - fields_1=(), fields_2=(), page_size=None, - sort_key=(), sort_dir=(), response_contents=None, - base_args=None, path=None, cmd_resources=None, - parent_id=None, output_format=None, query=""): - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) - if not cmd_resources: - cmd_resources = resources - if response_contents is None: - contents = [{self.id_field: 'myid1', }, - {self.id_field: 'myid2', }, ] - else: - contents = response_contents - reses = {resources: contents} - resstr = self.client.serialize(reses) - # url method body - args = base_args if base_args is not None else [] - if detail: - args.append('-D') - if fields_1: - for field in fields_1: - args.append('--fields') - args.append(field) - - if tags: - args.append('--') - args.append("--tag") - for tag in tags: - args.append(tag) - tag_query = urlparse.urlencode( - {'tag': encodeutils.safe_encode(tag)}) - if query: - query += "&" + tag_query - else: - query = tag_query - if (not tags) and fields_2: - args.append('--') - if fields_2: - args.append("--fields") - for field in fields_2: - args.append(field) - if detail: - query = query and query + '&verbose=True' or 'verbose=True' - for field in itertools.chain(fields_1, fields_2): - if query: - query += "&fields=" + field - else: - query = "fields=" + field - if page_size: - args.append("--page-size") - args.append(str(page_size)) - if query: - query += "&limit=%s" % page_size - else: - query = "limit=%s" % page_size - if sort_key: - for key in sort_key: - args.append('--sort-key') - args.append(key) - if query: - query += '&' - query += 'sort_key=%s' % key - if sort_dir: - len_diff = len(sort_key) - len(sort_dir) - if len_diff > 0: - sort_dir = tuple(sort_dir) + ('asc',) * len_diff - elif len_diff < 0: - sort_dir = sort_dir[:len(sort_key)] - for dir in sort_dir: - args.append('--sort-dir') - args.append(dir) - if query: - query += '&' - query += 'sort_dir=%s' % dir - if path is None: - path = getattr(self.client, cmd_resources + "_path") - if parent_id: - path = path % parent_id - if output_format: - args.append('-f') - args.append(output_format) - self.client.httpclient.request( - MyUrlComparator(end_url(path, query, format=self.format), - self.client), - 'GET', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr)) - self.mox.ReplayAll() - cmd_parser = cmd.get_parser("list_" + cmd_resources) - shell.run_command(cmd, cmd_parser, args) - self.mox.VerifyAll() - self.mox.UnsetStubs() - _str = self.fake_stdout.make_string() - if response_contents is None: - self.assertIn('myid1', _str) - return _str - - def _test_list_resources_with_pagination(self, resources, cmd, - base_args=None, - cmd_resources=None, - parent_id=None, query=""): - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) - if not cmd_resources: - cmd_resources = resources - - path = getattr(self.client, cmd_resources + "_path") - if parent_id: - path = path % parent_id - fake_query = "marker=myid2&limit=2" - reses1 = {resources: [{'id': 'myid1', }, - {'id': 'myid2', }], - '%s_links' % resources: [{'href': end_url(path, fake_query), - 'rel': 'next'}]} - reses2 = {resources: [{'id': 'myid3', }, - {'id': 'myid4', }]} - resstr1 = self.client.serialize(reses1) - resstr2 = self.client.serialize(reses2) - self.client.httpclient.request( - end_url(path, query, format=self.format), 'GET', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr1)) - self.client.httpclient.request( - MyUrlComparator(end_url(path, fake_query, format=self.format), - self.client), 'GET', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr2)) - self.mox.ReplayAll() - cmd_parser = cmd.get_parser("list_" + cmd_resources) - args = base_args if base_args is not None else [] - shell.run_command(cmd, cmd_parser, args) - self.mox.VerifyAll() - self.mox.UnsetStubs() - - def _test_update_resource(self, resource, cmd, myid, args, extrafields, - cmd_resource=None, parent_id=None): - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) - if not cmd_resource: - cmd_resource = resource - - body = {resource: extrafields} - path = getattr(self.client, cmd_resource + "_path") - if parent_id: - path = path % (parent_id, myid) - else: - path = path % myid - mox_body = MyComparator(body, self.client) - - self.client.httpclient.request( - MyUrlComparator(end_url(path, format=self.format), - self.client), - 'PUT', - body=mox_body, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', TOKEN)).AndReturn((MyResp(204), None)) - self.mox.ReplayAll() - cmd_parser = cmd.get_parser("update_" + cmd_resource) - shell.run_command(cmd, cmd_parser, args) - self.mox.VerifyAll() - self.mox.UnsetStubs() - _str = self.fake_stdout.make_string() - self.assertIn(myid, _str) - - def _test_show_resource(self, resource, cmd, myid, args, fields=(), - cmd_resource=None, parent_id=None): - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) - if not cmd_resource: - cmd_resource = resource - - query = "&".join(["fields=%s" % field for field in fields]) - expected_res = {resource: - {self.id_field: myid, - 'name': 'myname', }, } - resstr = self.client.serialize(expected_res) - path = getattr(self.client, cmd_resource + "_path") - if parent_id: - path = path % (parent_id, myid) - else: - path = path % myid - self.client.httpclient.request( - end_url(path, query, format=self.format), 'GET', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr)) - self.mox.ReplayAll() - cmd_parser = cmd.get_parser("show_" + cmd_resource) - shell.run_command(cmd, cmd_parser, args) - self.mox.VerifyAll() - self.mox.UnsetStubs() - _str = self.fake_stdout.make_string() - self.assertIn(myid, _str) - self.assertIn('myname', _str) - - def _test_set_path_and_delete(self, path, parent_id, myid): - if parent_id: - path = path % (parent_id, myid) - else: - path = path % (myid) - self.client.httpclient.request( - end_url(path, format=self.format), 'DELETE', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', TOKEN)).AndReturn((MyResp(204), None)) - - def _test_delete_resource(self, resource, cmd, myid, args, - cmd_resource=None, parent_id=None, - extra_id=None): - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) - if not cmd_resource: - cmd_resource = resource - path = getattr(self.client, cmd_resource + "_path") - self._test_set_path_and_delete(path, parent_id, myid) - # extra_id is used to test for bulk_delete - if extra_id: - self._test_set_path_and_delete(path, parent_id, extra_id) - self.mox.ReplayAll() - cmd_parser = cmd.get_parser("delete_" + cmd_resource) - shell.run_command(cmd, cmd_parser, args) - self.mox.VerifyAll() - self.mox.UnsetStubs() - _str = self.fake_stdout.make_string() - self.assertIn(myid, _str) - if extra_id: - self.assertIn(extra_id, _str) - - def _test_update_resource_action(self, resource, cmd, myid, action, args, - body, retval=None, cmd_resource=None): - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) - if not cmd_resource: - cmd_resource = resource - path = getattr(self.client, cmd_resource + "_path") - path_action = '%s/%s' % (myid, action) - self.client.httpclient.request( - end_url(path % path_action, format=self.format), 'PUT', - body=MyComparator(body, self.client), - headers=mox.ContainsKeyValue( - 'X-Auth-Token', TOKEN)).AndReturn((MyResp(204), retval)) - self.mox.ReplayAll() - cmd_parser = cmd.get_parser("delete_" + cmd_resource) - shell.run_command(cmd, cmd_parser, args) - self.mox.VerifyAll() - self.mox.UnsetStubs() - _str = self.fake_stdout.make_string() - self.assertIn(myid, _str) - - -class TestListCommand(neutronV2_0.ListCommand): - resource = 'test_resource' - filter_attrs = [ - 'name', - 'admin_state_up', - {'name': 'foo', 'help': 'non-boolean attribute foo'}, - {'name': 'bar', 'help': 'boolean attribute bar', - 'boolean': True}, - {'name': 'baz', 'help': 'integer attribute baz', - 'argparse_kwargs': {'choices': ['baz1', 'baz2']}}, - ] - - -class ListCommandTestCase(CLITestV20Base): - - def setUp(self): - super(ListCommandTestCase, self).setUp() - self.client.extend_list('test_resources', '/test_resources', None) - setattr(self.client, 'test_resources_path', '/test_resources') - - def _test_list_resources_filter_params(self, base_args='', query=''): - resources = 'test_resources' - cmd = TestListCommand(MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - base_args=base_args.split(), - query=query) - - def _test_list_resources_with_arg_error(self, base_args=''): - self.addCleanup(self.mox.UnsetStubs) - resources = 'test_resources' - cmd = TestListCommand(MyApp(sys.stdout), None) - # argparse parse error leads to SystemExit - self.assertRaises(SystemExit, - self._test_list_resources, - resources, cmd, - base_args=base_args.split()) - - def test_list_resources_without_filter(self): - self._test_list_resources_filter_params() - - def test_list_resources_use_default_filter(self): - self._test_list_resources_filter_params( - base_args='--name val1 --admin-state-up False', - query='name=val1&admin_state_up=False') - - def test_list_resources_use_custom_filter(self): - self._test_list_resources_filter_params( - base_args='--foo FOO --bar True', - query='foo=FOO&bar=True') - - def test_list_resources_boolean_check_default_filter(self): - self._test_list_resources_filter_params( - base_args='--admin-state-up True', query='admin_state_up=True') - self._test_list_resources_filter_params( - base_args='--admin-state-up False', query='admin_state_up=False') - self._test_list_resources_with_arg_error( - base_args='--admin-state-up non-true-false') - - def test_list_resources_boolean_check_custom_filter(self): - self._test_list_resources_filter_params( - base_args='--bar True', query='bar=True') - self._test_list_resources_filter_params( - base_args='--bar False', query='bar=False') - self._test_list_resources_with_arg_error( - base_args='--bar non-true-false') - - def test_list_resources_argparse_kwargs(self): - self._test_list_resources_filter_params( - base_args='--baz baz1', query='baz=baz1') - self._test_list_resources_filter_params( - base_args='--baz baz2', query='baz=baz2') - self._test_list_resources_with_arg_error( - base_args='--bar non-choice') - - -class ClientV2TestJson(CLITestV20Base): - def test_do_request_unicode(self): - self.mox.StubOutWithMock(self.client.httpclient, "request") - unicode_text = u'\u7f51\u7edc' - # url with unicode - action = u'/test' - expected_action = action - # query string with unicode - params = {'test': unicode_text} - expect_query = urlparse.urlencode(utils.safe_encode_dict(params)) - # request body with unicode - body = params - expect_body = self.client.serialize(body) - self.client.httpclient.auth_token = encodeutils.safe_encode( - unicode_text) - expected_auth_token = encodeutils.safe_encode(unicode_text) - resp_headers = {'x-openstack-request-id': REQUEST_ID} - - self.client.httpclient.request( - end_url(expected_action, query=expect_query, format=self.format), - 'PUT', body=expect_body, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', - expected_auth_token)).AndReturn((MyResp(200, resp_headers), - expect_body)) - - self.mox.ReplayAll() - result = self.client.do_request('PUT', action, body=body, - params=params) - self.mox.VerifyAll() - self.mox.UnsetStubs() - - # test response with unicode - self.assertEqual(body, result) - - def test_do_request_error_without_response_body(self): - self.mox.StubOutWithMock(self.client.httpclient, "request") - params = {'test': 'value'} - expect_query = six.moves.urllib.parse.urlencode(params) - self.client.httpclient.auth_token = 'token' - resp_headers = {'x-openstack-request-id': REQUEST_ID} - - self.client.httpclient.request( - MyUrlComparator(end_url( - '/test', query=expect_query, format=self.format), self.client), - 'PUT', body='', - headers=mox.ContainsKeyValue('X-Auth-Token', 'token') - ).AndReturn((MyResp(400, headers=resp_headers, reason='An error'), '')) - - self.mox.ReplayAll() - error = self.assertRaises(exceptions.NeutronClientException, - self.client.do_request, 'PUT', '/test', - body='', params=params) - expected_error = "An error\nNeutron server returns " \ - "request_ids: %s" % [REQUEST_ID] - self.assertEqual(expected_error, str(error)) - self.mox.VerifyAll() - self.mox.UnsetStubs() - - def test_do_request_with_long_uri_exception(self): - long_string = 'x' * 8200 # 8200 > MAX_URI_LEN:8192 - params = {'id': long_string} - exception = self.assertRaises(exceptions.RequestURITooLong, - self.client.do_request, - 'GET', '/test', body='', params=params) - self.assertNotEqual(0, exception.excess) - - def test_do_request_request_ids(self): - self.mox.StubOutWithMock(self.client.httpclient, "request") - params = {'test': 'value'} - expect_query = six.moves.urllib.parse.urlencode(params) - self.client.httpclient.auth_token = 'token' - body = params - expect_body = self.client.serialize(body) - resp_headers = {'x-openstack-request-id': REQUEST_ID} - self.client.httpclient.request( - MyUrlComparator(end_url( - '/test', query=expect_query, - format=self.format), self.client), - 'PUT', body=expect_body, - headers=mox.ContainsKeyValue('X-Auth-Token', 'token') - ).AndReturn((MyResp(200, resp_headers), expect_body)) - - self.mox.ReplayAll() - result = self.client.do_request('PUT', '/test', body=body, - params=params) - self.mox.VerifyAll() - self.mox.UnsetStubs() - - self.assertEqual(body, result) - self.assertEqual([REQUEST_ID], result.request_ids) - - def test_list_request_ids_with_retrieve_all_true(self): - self.mox.StubOutWithMock(self.client.httpclient, "request") - - path = '/test' - resources = 'tests' - fake_query = "marker=myid2&limit=2" - reses1 = {resources: [{'id': 'myid1', }, - {'id': 'myid2', }], - '%s_links' % resources: [{'href': end_url(path, fake_query), - 'rel': 'next'}]} - reses2 = {resources: [{'id': 'myid3', }, - {'id': 'myid4', }]} - resstr1 = self.client.serialize(reses1) - resstr2 = self.client.serialize(reses2) - resp_headers = {'x-openstack-request-id': REQUEST_ID} - self.client.httpclient.request( - end_url(path, "", format=self.format), 'GET', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200, resp_headers), - resstr1)) - self.client.httpclient.request( - MyUrlComparator(end_url(path, fake_query, format=self.format), - self.client), 'GET', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200, resp_headers), - resstr2)) - self.mox.ReplayAll() - result = self.client.list(resources, path) - - self.mox.VerifyAll() - self.mox.UnsetStubs() - - self.assertEqual([REQUEST_ID, REQUEST_ID], result.request_ids) - - def test_list_request_ids_with_retrieve_all_false(self): - self.mox.StubOutWithMock(self.client.httpclient, "request") - - path = '/test' - resources = 'tests' - fake_query = "marker=myid2&limit=2" - reses1 = {resources: [{'id': 'myid1', }, - {'id': 'myid2', }], - '%s_links' % resources: [{'href': end_url(path, fake_query), - 'rel': 'next'}]} - reses2 = {resources: [{'id': 'myid3', }, - {'id': 'myid4', }]} - resstr1 = self.client.serialize(reses1) - resstr2 = self.client.serialize(reses2) - resp_headers = {'x-openstack-request-id': REQUEST_ID} - self.client.httpclient.request( - end_url(path, "", format=self.format), 'GET', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200, resp_headers), - resstr1)) - self.client.httpclient.request( - MyUrlComparator(end_url(path, fake_query, format=self.format), - self.client), 'GET', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200, resp_headers), - resstr2)) - self.mox.ReplayAll() - result = self.client.list(resources, path, retrieve_all=False) - next(result) - self.assertEqual([REQUEST_ID], result.request_ids) - next(result) - self.assertEqual([REQUEST_ID, REQUEST_ID], result.request_ids) - self.mox.VerifyAll() - self.mox.UnsetStubs() - - -class CLITestV20ExceptionHandler(CLITestV20Base): - - def _test_exception_handler_v20( - self, expected_exception, status_code, expected_msg, - error_type=None, error_msg=None, error_detail=None, - request_id=None, error_content=None): - - resp = MyResp(status_code, {'x-openstack-request-id': request_id}) - if request_id is not None: - expected_msg += "\nNeutron server returns " \ - "request_ids: %s" % [request_id] - if error_content is None: - error_content = {'NeutronError': {'type': error_type, - 'message': error_msg, - 'detail': error_detail}} - expected_content = self.client._convert_into_with_meta(error_content, - resp) - - e = self.assertRaises(expected_exception, - client.exception_handler_v20, - status_code, expected_content) - self.assertEqual(status_code, e.status_code) - self.assertEqual(expected_exception.__name__, - e.__class__.__name__) - - if expected_msg is None: - if error_detail: - expected_msg = '\n'.join([error_msg, error_detail]) - else: - expected_msg = error_msg - self.assertEqual(expected_msg, e.message) - - def test_exception_handler_v20_ip_address_in_use(self): - err_msg = ('Unable to complete operation for network ' - 'fake-network-uuid. The IP address fake-ip is in use.') - self._test_exception_handler_v20( - exceptions.IpAddressInUseClient, 409, err_msg, - 'IpAddressInUse', err_msg, '', REQUEST_ID) - - def test_exception_handler_v20_neutron_known_error(self): - known_error_map = [ - ('NetworkNotFound', exceptions.NetworkNotFoundClient, 404), - ('PortNotFound', exceptions.PortNotFoundClient, 404), - ('NetworkInUse', exceptions.NetworkInUseClient, 409), - ('PortInUse', exceptions.PortInUseClient, 409), - ('StateInvalid', exceptions.StateInvalidClient, 400), - ('IpAddressInUse', exceptions.IpAddressInUseClient, 409), - ('IpAddressGenerationFailure', - exceptions.IpAddressGenerationFailureClient, 409), - ('MacAddressInUse', exceptions.MacAddressInUseClient, 409), - ('ExternalIpAddressExhausted', - exceptions.ExternalIpAddressExhaustedClient, 400), - ('OverQuota', exceptions.OverQuotaClient, 409), - ('InvalidIpForNetwork', exceptions.InvalidIpForNetworkClient, 400), - ('InvalidIpForSubnet', exceptions.InvalidIpForSubnetClient, 400), - ] - - error_msg = 'dummy exception message' - error_detail = 'sample detail' - for server_exc, client_exc, status_code in known_error_map: - self._test_exception_handler_v20( - client_exc, status_code, - error_msg + '\n' + error_detail, - server_exc, error_msg, error_detail, REQUEST_ID) - - def test_exception_handler_v20_neutron_known_error_without_detail(self): - error_msg = 'Network not found' - error_detail = '' - self._test_exception_handler_v20( - exceptions.NetworkNotFoundClient, 404, - error_msg, - 'NetworkNotFound', error_msg, error_detail, REQUEST_ID) - - def test_exception_handler_v20_unknown_error_to_per_code_exception(self): - for status_code, client_exc in exceptions.HTTP_EXCEPTION_MAP.items(): - error_msg = 'Unknown error' - error_detail = 'This is detail' - self._test_exception_handler_v20( - client_exc, status_code, - error_msg + '\n' + error_detail, - 'UnknownError', error_msg, error_detail, [REQUEST_ID]) - - def test_exception_handler_v20_neutron_unknown_status_code(self): - error_msg = 'Unknown error' - error_detail = 'This is detail' - self._test_exception_handler_v20( - exceptions.NeutronClientException, 501, - error_msg + '\n' + error_detail, - 'UnknownError', error_msg, error_detail, REQUEST_ID) - - def test_exception_handler_v20_bad_neutron_error(self): - for status_code, client_exc in exceptions.HTTP_EXCEPTION_MAP.items(): - error_content = {'NeutronError': {'unknown_key': 'UNKNOWN'}} - self._test_exception_handler_v20( - client_exc, status_code, - expected_msg="{'unknown_key': 'UNKNOWN'}", - error_content=error_content, - request_id=REQUEST_ID) - - def test_exception_handler_v20_error_dict_contains_message(self): - error_content = {'message': 'This is an error message'} - for status_code, client_exc in exceptions.HTTP_EXCEPTION_MAP.items(): - self._test_exception_handler_v20( - client_exc, status_code, - expected_msg='This is an error message', - error_content=error_content, - request_id=REQUEST_ID) - - def test_exception_handler_v20_error_dict_not_contain_message(self): - # 599 is not contained in HTTP_EXCEPTION_MAP. - error_content = {'error': 'This is an error message'} - expected_msg = '%s-%s' % (599, error_content) - self._test_exception_handler_v20( - exceptions.NeutronClientException, 599, - expected_msg=expected_msg, - request_id=None, - error_content=error_content) - - def test_exception_handler_v20_default_fallback(self): - # 599 is not contained in HTTP_EXCEPTION_MAP. - error_content = 'This is an error message' - expected_msg = '%s-%s' % (599, error_content) - self._test_exception_handler_v20( - exceptions.NeutronClientException, 599, - expected_msg=expected_msg, - request_id=None, - error_content=error_content) - - def test_exception_status(self): - e = exceptions.BadRequest() - self.assertEqual(400, e.status_code) - - e = exceptions.BadRequest(status_code=499) - self.assertEqual(499, e.status_code) - - # SslCertificateValidationError has no explicit status_code, - # but should have a 'safe' defined fallback. - e = exceptions.SslCertificateValidationError() - self.assertIsNotNone(e.status_code) - - e = exceptions.SslCertificateValidationError(status_code=599) - self.assertEqual(599, e.status_code) - - def test_connection_failed(self): - self.mox.StubOutWithMock(self.client.httpclient, 'request') - self.client.httpclient.auth_token = 'token' - - self.client.httpclient.request( - end_url('/test'), 'GET', - headers=mox.ContainsKeyValue('X-Auth-Token', 'token') - ).AndRaise(requests.exceptions.ConnectionError('Connection refused')) - - self.mox.ReplayAll() - - error = self.assertRaises(exceptions.ConnectionFailed, - self.client.get, '/test') - # NB: ConnectionFailed has no explicit status_code, so this - # tests that there is a fallback defined. - self.assertIsNotNone(error.status_code) - self.mox.VerifyAll() - self.mox.UnsetStubs() - - -class DictWithMetaTest(base.BaseTestCase): - - def test_dict_with_meta(self): - body = {'test': 'value'} - resp = MyResp(200, {'x-openstack-request-id': REQUEST_ID}) - obj = client._DictWithMeta(body, resp) - self.assertEqual(body, obj) - - # Check request_ids attribute is added to obj - self.assertTrue(hasattr(obj, 'request_ids')) - self.assertEqual([REQUEST_ID], obj.request_ids) - - -class TupleWithMetaTest(base.BaseTestCase): - - def test_tuple_with_meta(self): - body = ('test', 'value') - resp = MyResp(200, {'x-openstack-request-id': REQUEST_ID}) - obj = client._TupleWithMeta(body, resp) - self.assertEqual(body, obj) - - # Check request_ids attribute is added to obj - self.assertTrue(hasattr(obj, 'request_ids')) - self.assertEqual([REQUEST_ID], obj.request_ids) - - -class StrWithMetaTest(base.BaseTestCase): - - def test_str_with_meta(self): - body = "test_string" - resp = MyResp(200, {'x-openstack-request-id': REQUEST_ID}) - obj = client._StrWithMeta(body, resp) - self.assertEqual(body, obj) - - # Check request_ids attribute is added to obj - self.assertTrue(hasattr(obj, 'request_ids')) - self.assertEqual([REQUEST_ID], obj.request_ids) - - -class GeneratorWithMetaTest(base.BaseTestCase): - - body = {'test': 'value'} - resp = MyResp(200, {'x-openstack-request-id': REQUEST_ID}) - - def _pagination(self, collection, path, **params): - obj = client._DictWithMeta(self.body, self.resp) - yield obj - - def test_generator(self): - obj = client._GeneratorWithMeta(self._pagination, 'test_collection', - 'test_path', test_args='test_args') - self.assertEqual(self.body, next(obj)) - - self.assertTrue(hasattr(obj, 'request_ids')) - self.assertEqual([REQUEST_ID], obj.request_ids) diff --git a/neutronclient/tests/unit/test_cli20_address_scope.py b/neutronclient/tests/unit/test_cli20_address_scope.py deleted file mode 100644 index e770de3b7..000000000 --- a/neutronclient/tests/unit/test_cli20_address_scope.py +++ /dev/null @@ -1,178 +0,0 @@ -# Copyright 2015 Huawei Technologies India Pvt. Ltd. -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import sys - -from mox3 import mox - -from neutronclient.common import exceptions -from neutronclient.neutron.v2_0 import address_scope -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20AddressScopeJSON(test_cli20.CLITestV20Base): - - non_admin_status_resources = ['address_scope'] - - def setUp(self): - super(CLITestV20AddressScopeJSON, self).setUp(plurals={'tags': 'tag'}) - - def test_create_address_scope_with_minimum_option_ipv4(self): - """Create address_scope: foo-address-scope with minimum option.""" - resource = 'address_scope' - cmd = address_scope.CreateAddressScope( - test_cli20.MyApp(sys.stdout), None) - name = 'foo-address-scope' - myid = 'myid' - args = [name, '4'] - position_names = ['name', 'ip_version'] - position_values = [name, 4] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_address_scope_with_minimum_option_ipv6(self): - """Create address_scope: foo-address-scope with minimum option.""" - resource = 'address_scope' - cmd = address_scope.CreateAddressScope( - test_cli20.MyApp(sys.stdout), None) - name = 'foo-address-scope' - myid = 'myid' - args = [name, '6'] - position_names = ['name', 'ip_version'] - position_values = [name, 6] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_address_scope_with_minimum_option_bad_ip_version(self): - """Create address_scope: foo-address-scope with minimum option.""" - resource = 'address_scope' - cmd = address_scope.CreateAddressScope( - test_cli20.MyApp(sys.stdout), None) - name = 'foo-address-scope' - myid = 'myid' - args = [name, '5'] - position_names = ['name', 'ip_version'] - position_values = [name, 5] - self.assertRaises(SystemExit, self._test_create_resource, - resource, cmd, name, myid, args, position_names, - position_values) - - def test_create_address_scope_with_all_option(self): - # Create address_scope: foo-address-scope with all options. - resource = 'address_scope' - cmd = address_scope.CreateAddressScope( - test_cli20.MyApp(sys.stdout), None) - name = 'foo-address-scope' - myid = 'myid' - args = [name, '4', '--shared'] - position_names = ['name', 'ip_version', 'shared'] - position_values = [name, 4, True] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_address_scope_with_unicode(self): - # Create address_scope: u'\u7f51\u7edc'. - resource = 'address_scope' - cmd = address_scope.CreateAddressScope( - test_cli20.MyApp(sys.stdout), None) - name = u'\u7f51\u7edc' - ip_version = u'4' - myid = 'myid' - args = [name, ip_version] - position_names = ['name', 'ip_version'] - position_values = [name, 4] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_update_address_scope_exception(self): - # Update address_scope (Negative) : myid. - resource = 'address_scope' - cmd = address_scope.UpdateAddressScope( - test_cli20.MyApp(sys.stdout), None) - self.assertRaises(exceptions.CommandError, self._test_update_resource, - resource, cmd, 'myid', ['myid'], {}) - - def test_update_address_scope(self): - # Update address_scope: myid --name newname-address-scope. - resource = 'address_scope' - cmd = address_scope.UpdateAddressScope( - test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'newname-address-scope'], - {'name': 'newname-address-scope'} - ) - # Update address_scope: myid --shared - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--shared', 'True'], - {'shared': "True"} - ) - - def test_list_address_scope(self): - # address_scope-list. - resources = "address_scopes" - cmd = address_scope.ListAddressScope(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, True) - - def test_list_address_scope_pagination(self): - # address_scope-list. - cmd = address_scope.ListAddressScope(test_cli20.MyApp(sys.stdout), - None) - self.mox.StubOutWithMock(address_scope.ListAddressScope, - "extend_list") - address_scope.ListAddressScope.extend_list(mox.IsA(list), - mox.IgnoreArg()) - self._test_list_resources_with_pagination("address_scopes", - cmd) - self.mox.VerifyAll() - self.mox.UnsetStubs() - - def test_list_address_scope_sort(self): - # sorted list: - # address_scope-list --sort-key name --sort-key id --sort-key asc - # --sort-key desc - resources = "address_scopes" - cmd = address_scope.ListAddressScope(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id", "ip_version"], - sort_dir=["asc", "desc"]) - - def test_list_address_scope_limit(self): - # size (1000) limited list: address_scope-list -P. - resources = "address_scopes" - cmd = address_scope.ListAddressScope(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_show_address_scope(self): - # Show address_scope: --fields id --fields name myid. - resource = 'address_scope' - cmd = address_scope.ShowAddressScope( - test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id, - '--fields', 'ip_version', '6'] - self._test_show_resource(resource, cmd, self.test_id, args, - ['id', 'name', 'ip_version']) - - def test_delete_address_scope(self): - # Delete address_scope: address_scope_id. - resource = 'address_scope' - cmd = address_scope.DeleteAddressScope( - test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = [myid] - self._test_delete_resource(resource, cmd, myid, args) diff --git a/neutronclient/tests/unit/test_cli20_agents.py b/neutronclient/tests/unit/test_cli20_agents.py deleted file mode 100644 index 7641d2741..000000000 --- a/neutronclient/tests/unit/test_cli20_agents.py +++ /dev/null @@ -1,97 +0,0 @@ -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import sys - -from oslo_serialization import jsonutils - -from neutronclient.neutron.v2_0 import agent -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20Agent(test_cli20.CLITestV20Base): - def test_list_agents(self): - contents = {'agents': [{'id': 'myname', 'agent_type': 'mytype', - 'alive': True}]} - args = ['-f', 'json'] - resources = "agents" - - cmd = agent.ListAgent(test_cli20.MyApp(sys.stdout), None) - self._test_list_columns(cmd, resources, contents, args) - _str = self.fake_stdout.make_string() - - returned_agents = jsonutils.loads(_str) - self.assertEqual(1, len(returned_agents)) - ag = returned_agents[0] - self.assertEqual(3, len(ag)) - self.assertIn("alive", ag.keys()) - - def test_list_agents_field(self): - contents = {'agents': [{'alive': True}]} - args = ['-f', 'json'] - resources = "agents" - smile = ':-)' - - cmd = agent.ListAgent(test_cli20.MyApp(sys.stdout), None) - self._test_list_columns(cmd, resources, contents, args) - _str = self.fake_stdout.make_string() - - returned_agents = jsonutils.loads(_str) - self.assertEqual(1, len(returned_agents)) - ag = returned_agents[0] - self.assertEqual(1, len(ag)) - self.assertIn("alive", ag.keys()) - self.assertIn(smile, ag.values()) - - def test_list_agents_zone_field(self): - contents = {'agents': [{'availability_zone': 'myzone'}]} - args = ['-f', 'json'] - resources = "agents" - - cmd = agent.ListAgent(test_cli20.MyApp(sys.stdout), None) - self._test_list_columns(cmd, resources, contents, args) - _str = self.fake_stdout.make_string() - - returned_agents = jsonutils.loads(_str) - self.assertEqual(1, len(returned_agents)) - ag = returned_agents[0] - self.assertEqual(1, len(ag)) - self.assertIn("availability_zone", ag.keys()) - self.assertIn('myzone', ag.values()) - - def test_update_agent(self): - # agent-update myid --admin-state-down --description mydescr. - resource = 'agent' - cmd = agent.UpdateAgent(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource( - resource, cmd, 'myid', - ['myid', '--admin-state-down', '--description', 'mydescr'], - {'description': 'mydescr', 'admin_state_up': False} - ) - - def test_show_agent(self): - # Show agent: --field id --field binary myid. - resource = 'agent' - cmd = agent.ShowAgent(test_cli20.MyApp(sys.stdout), None) - args = ['--field', 'id', '--field', 'binary', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'binary']) - - def test_delete_agent(self): - # Delete agent: myid. - resource = 'agent' - cmd = agent.DeleteAgent(test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = [myid] - self._test_delete_resource(resource, cmd, myid, args) diff --git a/neutronclient/tests/unit/test_cli20_agentschedulers.py b/neutronclient/tests/unit/test_cli20_agentschedulers.py deleted file mode 100644 index d9c1f0033..000000000 --- a/neutronclient/tests/unit/test_cli20_agentschedulers.py +++ /dev/null @@ -1,208 +0,0 @@ -# Copyright 2013 Mirantis Inc. -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import sys - -from mox3 import mox - -from neutronclient.neutron.v2_0 import agentscheduler -from neutronclient.neutron.v2_0 import network -from neutronclient.tests.unit import test_cli20 - - -AGENT_ID = 'agent_id1' -NETWORK_ID = 'net_id1' -ROUTER_ID = 'router_id1' - - -class CLITestV20AgentScheduler(test_cli20.CLITestV20Base): - def _test_add_to_agent(self, resource, cmd, cmd_args, destination, - body, result): - path = ((self.client.agent_path + destination) % - cmd_args[0]) - - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) - result_str = self.client.serialize(result) - return_tup = (test_cli20.MyResp(200), result_str) - - self.client.httpclient.request( - test_cli20.end_url(path), 'POST', - body=test_cli20.MyComparator(body, self.client), - headers=mox.ContainsKeyValue( - 'X-Auth-Token', test_cli20.TOKEN)).AndReturn(return_tup) - self.mox.ReplayAll() - cmd_parser = cmd.get_parser('test_' + resource) - parsed_args = cmd_parser.parse_args(cmd_args) - cmd.run(parsed_args) - self.mox.VerifyAll() - self.mox.UnsetStubs() - - def _test_remove_from_agent(self, resource, cmd, cmd_args, destination): - path = ((self.client.agent_path + destination + '/%s') % - cmd_args) - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) - - return_tup = (test_cli20.MyResp(204), None) - self.client.httpclient.request( - test_cli20.end_url(path), 'DELETE', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', test_cli20.TOKEN)).AndReturn(return_tup) - self.mox.ReplayAll() - cmd_parser = cmd.get_parser('test_' + resource) - parsed_args = cmd_parser.parse_args(cmd_args) - cmd.run(parsed_args) - self.mox.VerifyAll() - self.mox.UnsetStubs() - - -class CLITestV20DHCPAgentScheduler(CLITestV20AgentScheduler): - - def test_add_network_to_agent(self): - resource = 'agent' - cmd = agentscheduler.AddNetworkToDhcpAgent( - test_cli20.MyApp(sys.stdout), None) - args = (AGENT_ID, NETWORK_ID) - body = {'network_id': NETWORK_ID} - result = {'network_id': 'net_id', } - self._test_add_to_agent(resource, cmd, args, self.client.DHCP_NETS, - body, result) - - def test_remove_network_from_agent(self): - resource = 'agent' - cmd = agentscheduler.RemoveNetworkFromDhcpAgent( - test_cli20.MyApp(sys.stdout), None) - args = (AGENT_ID, NETWORK_ID) - self._test_remove_from_agent(resource, cmd, args, - self.client.DHCP_NETS) - - def test_list_networks_on_agent(self): - resources = 'networks' - cmd = agentscheduler.ListNetworksOnDhcpAgent( - test_cli20.MyApp(sys.stdout), None) - agent_id = 'agent_id1' - path = ((self.client.agent_path + self.client.DHCP_NETS) % - agent_id) - self.mox.StubOutWithMock(network.ListNetwork, "extend_list") - network.ListNetwork.extend_list(mox.IsA(list), mox.IgnoreArg()) - self._test_list_resources(resources, cmd, base_args=[agent_id], - path=path) - - def test_list_agents_hosting_network(self): - resources = 'agent' - cmd = agentscheduler.ListDhcpAgentsHostingNetwork( - test_cli20.MyApp(sys.stdout), None) - agent_id = 'agent_id1' - path = ((self.client.network_path + self.client.DHCP_AGENTS) % - agent_id) - contents = {self.id_field: 'myid1', 'alive': True} - self._test_list_resources(resources, cmd, base_args=[agent_id], - path=path, response_contents=contents) - - -class CLITestV20L3AgentScheduler(CLITestV20AgentScheduler): - - def test_add_router_to_agent(self): - resource = 'agent' - cmd = agentscheduler.AddRouterToL3Agent( - test_cli20.MyApp(sys.stdout), None) - args = (AGENT_ID, ROUTER_ID) - body = {'router_id': ROUTER_ID} - result = {'network_id': 'net_id', } - self._test_add_to_agent(resource, cmd, args, self.client.L3_ROUTERS, - body, result) - - def test_remove_router_from_agent(self): - resource = 'agent' - cmd = agentscheduler.RemoveRouterFromL3Agent( - test_cli20.MyApp(sys.stdout), None) - args = (AGENT_ID, ROUTER_ID) - self._test_remove_from_agent(resource, cmd, args, - self.client.L3_ROUTERS) - - def test_list_routers_on_agent(self): - resources = 'router' - cmd = agentscheduler.ListRoutersOnL3Agent( - test_cli20.MyApp(sys.stdout), None) - agent_id = 'agent_id1' - path = ((self.client.agent_path + self.client.L3_ROUTERS) % - agent_id) - contents = {self.id_field: 'myid1', 'name': 'my_name'} - self._test_list_resources(resources, cmd, base_args=[agent_id], - path=path, response_contents=contents) - - def test_list_agents_hosting_router(self): - resources = 'agent' - cmd = agentscheduler.ListL3AgentsHostingRouter( - test_cli20.MyApp(sys.stdout), None) - agent_id = 'agent_id1' - path = ((self.client.router_path + self.client.L3_AGENTS) % - agent_id) - contents = {self.id_field: 'myid1', 'alive': True} - self._test_list_resources(resources, cmd, base_args=[agent_id], - path=path, response_contents=contents) - - -class CLITestV20LBaaSAgentScheduler(test_cli20.CLITestV20Base): - - def test_list_pools_on_agent(self): - resources = 'pools' - cmd = agentscheduler.ListPoolsOnLbaasAgent( - test_cli20.MyApp(sys.stdout), None) - agent_id = 'agent_id1' - path = ((self.client.agent_path + self.client.LOADBALANCER_POOLS) % - agent_id) - self._test_list_resources(resources, cmd, base_args=[agent_id], - path=path) - - def test_get_lbaas_agent_hosting_pool(self): - resources = 'agent' - cmd = agentscheduler.GetLbaasAgentHostingPool( - test_cli20.MyApp(sys.stdout), None) - pool_id = 'pool_id1' - path = ((self.client.pool_path + self.client.LOADBALANCER_AGENT) % - pool_id) - contents = {self.id_field: 'myid1', 'alive': True} - self._test_list_resources(resources, cmd, base_args=[pool_id], - path=path, response_contents=contents) - - -class CLITestV20LBaaSV2AgentScheduler(test_cli20.CLITestV20Base): - - def test_list_loadbalancers_on_agent(self): - resources = 'loadbalancers' - cmd = agentscheduler.ListLoadBalancersOnLbaasAgent( - test_cli20.MyApp(sys.stdout), None) - agent_id = 'agent_id1' - path = ((self.client.agent_path + self.client.AGENT_LOADBALANCERS) % - agent_id) - self._test_list_resources(resources, cmd, base_args=[agent_id], - path=path) - - def test_get_lbaas_agent_hosting_pool(self): - resources = 'agent' - cmd = agentscheduler.GetLbaasAgentHostingLoadBalancer( - test_cli20.MyApp(sys.stdout), None) - lb_id = 'lb_id1' - path = ((self.client.lbaas_loadbalancer_path + - self.client.LOADBALANCER_HOSTING_AGENT) % lb_id) - contents = {self.id_field: 'myid1', 'alive': True} - self._test_list_resources(resources, cmd, base_args=[lb_id], - path=path, response_contents=contents) diff --git a/neutronclient/tests/unit/test_cli20_az.py b/neutronclient/tests/unit/test_cli20_az.py deleted file mode 100644 index cb127c529..000000000 --- a/neutronclient/tests/unit/test_cli20_az.py +++ /dev/null @@ -1,31 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import sys - -from neutronclient.neutron.v2_0 import availability_zone as az -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20Agent(test_cli20.CLITestV20Base): - def test_list_agents(self): - contents = {'availability_zones': [{'name': 'zone1', - 'resource': 'network', - 'state': 'available'}, - {'name': 'zone2', - 'resource': 'router', - 'state': 'unavailable'}]} - args = ['-f', 'json'] - resources = "availability_zones" - - cmd = az.ListAvailabilityZone(test_cli20.MyApp(sys.stdout), None) - self._test_list_columns(cmd, resources, contents, args) diff --git a/neutronclient/tests/unit/test_cli20_extensions.py b/neutronclient/tests/unit/test_cli20_extensions.py deleted file mode 100644 index 9e7d30a34..000000000 --- a/neutronclient/tests/unit/test_cli20_extensions.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright 2013 NEC Corporation -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import sys - -from neutronclient.neutron.v2_0.extension import ListExt -from neutronclient.neutron.v2_0.extension import ShowExt -from neutronclient.tests.unit.test_cli20 import CLITestV20Base -from neutronclient.tests.unit.test_cli20 import MyApp - - -class CLITestV20Extension(CLITestV20Base): - id_field = 'alias' - - def test_list_extensions(self): - resources = 'extensions' - cmd = ListExt(MyApp(sys.stdout), None) - contents = [{'alias': 'ext1', 'name': 'name1', 'other': 'other1'}, - {'alias': 'ext2', 'name': 'name2', 'other': 'other2'}] - ret = self._test_list_resources(resources, cmd, - response_contents=contents) - ret_words = set(ret.split()) - # Check only the default columns are shown. - self.assertIn('name', ret_words) - self.assertIn('alias', ret_words) - self.assertNotIn('other', ret_words) - - def test_show_extension(self): - # -F option does not work for ext-show at the moment, so -F option - # is not passed in the commandline args as other tests do. - resource = 'extension' - cmd = ShowExt(MyApp(sys.stdout), None) - args = [self.test_id] - ext_alias = self.test_id - self._test_show_resource(resource, cmd, ext_alias, args, fields=[]) diff --git a/neutronclient/tests/unit/test_cli20_floatingips.py b/neutronclient/tests/unit/test_cli20_floatingips.py deleted file mode 100644 index 9a1c39863..000000000 --- a/neutronclient/tests/unit/test_cli20_floatingips.py +++ /dev/null @@ -1,194 +0,0 @@ -#!/usr/bin/env python -# Copyright 2012 Red Hat -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import sys - -from neutronclient.neutron.v2_0 import floatingip as fip -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20FloatingIpsJSON(test_cli20.CLITestV20Base): - - non_admin_status_resources = ['floatingip'] - - def test_create_floatingip(self): - # Create floatingip: fip1. - resource = 'floatingip' - cmd = fip.CreateFloatingIP(test_cli20.MyApp(sys.stdout), None) - name = 'fip1' - myid = 'myid' - args = [name, '--description', 'floats like a butterfly'] - position_names = ['floating_network_id'] - position_values = [name] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - description='floats like a butterfly') - - def test_create_floatingip_and_port(self): - # Create floatingip: fip1. - resource = 'floatingip' - cmd = fip.CreateFloatingIP(test_cli20.MyApp(sys.stdout), None) - name = 'fip1' - myid = 'myid' - pid = 'mypid' - args = [name, '--port_id', pid] - position_names = ['floating_network_id', 'port_id'] - position_values = [name, pid] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - # Test dashed options - args = [name, '--port-id', pid] - position_names = ['floating_network_id', 'port_id'] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_floatingip_and_port_and_address(self): - # Create floatingip: fip1 with a given port and address. - resource = 'floatingip' - cmd = fip.CreateFloatingIP(test_cli20.MyApp(sys.stdout), None) - name = 'fip1' - myid = 'myid' - pid = 'mypid' - addr = '10.0.0.99' - args = [name, '--port_id', pid, '--fixed_ip_address', addr] - position_names = ['floating_network_id', 'port_id', 'fixed_ip_address'] - position_values = [name, pid, addr] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - # Test dashed options - args = [name, '--port-id', pid, '--fixed-ip-address', addr] - position_names = ['floating_network_id', 'port_id', 'fixed_ip_address'] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_floatingip_with_ip_address_of_floating_ip(self): - # Create floatingip: fip1 with a given IP address of floating IP. - resource = 'floatingip' - cmd = fip.CreateFloatingIP(test_cli20.MyApp(sys.stdout), None) - name = 'fip1' - myid = 'myid' - addr = '10.0.0.99' - - args = [name, '--floating-ip-address', addr] - position_values = [name, addr] - position_names = ['floating_network_id', 'floating_ip_address'] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_floatingip_with_subnet_id(self): - # Create floatingip: fip1 on a given subnet id. - resource = 'floatingip' - cmd = fip.CreateFloatingIP(test_cli20.MyApp(sys.stdout), None) - name = 'fip1' - myid = 'myid' - subnet_id = 'mysubnetid' - - args = [name, '--subnet', subnet_id] - position_values = [name, subnet_id] - position_names = ['floating_network_id', 'subnet_id'] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_floatingip_with_subnet_id_and_port(self): - # Create floatingip: fip1 on a given subnet id and port. - resource = 'floatingip' - cmd = fip.CreateFloatingIP(test_cli20.MyApp(sys.stdout), None) - name = 'fip1' - myid = 'myid' - pid = 'mypid' - subnet_id = 'mysubnetid' - - args = [name, '--subnet', subnet_id, '--port-id', pid] - position_values = [name, subnet_id, pid] - position_names = ['floating_network_id', 'subnet_id', 'port_id'] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_floatingip_with_dns_name_and_dns_domain(self): - # Create floatingip: fip1 with dns name and dns domain. - resource = 'floatingip' - cmd = fip.CreateFloatingIP(test_cli20.MyApp(sys.stdout), None) - name = 'fip1' - myid = 'myid' - dns_name_name = 'my-floatingip' - dns_domain_name = 'my-domain.org.' - args = [name, '--dns-name', dns_name_name, '--dns-domain', - dns_domain_name] - position_names = ['floating_network_id', 'dns_name', 'dns_domain'] - position_values = [name, dns_name_name, dns_domain_name] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_list_floatingips(self): - # list floatingips: -D. - resources = 'floatingips' - cmd = fip.ListFloatingIP(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True) - - def test_list_floatingips_pagination(self): - resources = 'floatingips' - cmd = fip.ListFloatingIP(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_floatingips_sort(self): - # list floatingips: - # --sort-key name --sort-key id --sort-key asc --sort-key desc - resources = 'floatingips' - cmd = fip.ListFloatingIP(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_floatingips_limit(self): - # list floatingips: -P. - resources = 'floatingips' - cmd = fip.ListFloatingIP(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_delete_floatingip(self): - # Delete floatingip: fip1. - resource = 'floatingip' - cmd = fip.DeleteFloatingIP(test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = [myid] - self._test_delete_resource(resource, cmd, myid, args) - - def test_show_floatingip(self): - # Show floatingip: --fields id. - resource = 'floatingip' - cmd = fip.ShowFloatingIP(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id']) - - def test_disassociate_ip(self): - # Disassociate floating IP: myid. - resource = 'floatingip' - cmd = fip.DisassociateFloatingIP(test_cli20.MyApp(sys.stdout), None) - args = ['myid'] - self._test_update_resource(resource, cmd, 'myid', - args, {"port_id": None} - ) - - def test_associate_ip(self): - # Associate floating IP: myid portid. - resource = 'floatingip' - cmd = fip.AssociateFloatingIP(test_cli20.MyApp(sys.stdout), None) - args = ['myid', 'portid'] - self._test_update_resource(resource, cmd, 'myid', - args, {"port_id": "portid"} - ) diff --git a/neutronclient/tests/unit/test_cli20_metering.py b/neutronclient/tests/unit/test_cli20_metering.py deleted file mode 100644 index 9ecf04734..000000000 --- a/neutronclient/tests/unit/test_cli20_metering.py +++ /dev/null @@ -1,99 +0,0 @@ -# Copyright (C) 2013 eNovance SAS -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# 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 sys - -from neutronclient.neutron.v2_0 import metering -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20MeteringJSON(test_cli20.CLITestV20Base): - - non_admin_status_resources = ['metering_label', 'metering_label_rule'] - - def test_create_metering_label(self): - # Create a metering label. - resource = 'metering_label' - cmd = metering.CreateMeteringLabel( - test_cli20.MyApp(sys.stdout), None) - name = 'my label' - myid = 'myid' - description = 'my description' - args = [name, '--description', description, '--shared'] - position_names = ['name', 'description', 'shared'] - position_values = [name, description, True] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_list_metering_labels(self): - resources = "metering_labels" - cmd = metering.ListMeteringLabel( - test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd) - - def test_delete_metering_label(self): - # Delete a metering label. - resource = 'metering_label' - cmd = metering.DeleteMeteringLabel( - test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = [myid] - self._test_delete_resource(resource, cmd, myid, args) - - def test_show_metering_label(self): - resource = 'metering_label' - cmd = metering.ShowMeteringLabel( - test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id']) - - def test_create_metering_label_rule(self): - resource = 'metering_label_rule' - cmd = metering.CreateMeteringLabelRule( - test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - metering_label_id = 'aaa' - remote_ip_prefix = '10.0.0.0/24' - direction = 'ingress' - args = [metering_label_id, remote_ip_prefix, '--direction', direction, - '--excluded'] - position_names = ['metering_label_id', 'remote_ip_prefix', 'direction', - 'excluded'] - position_values = [metering_label_id, remote_ip_prefix, - direction, True] - self._test_create_resource(resource, cmd, metering_label_id, - myid, args, position_names, position_values) - - def test_list_metering_label_rules(self): - resources = "metering_label_rules" - cmd = metering.ListMeteringLabelRule( - test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd) - - def test_delete_metering_label_rule(self): - resource = 'metering_label_rule' - cmd = metering.DeleteMeteringLabelRule( - test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = [myid] - self._test_delete_resource(resource, cmd, myid, args) - - def test_show_metering_label_rule(self): - resource = 'metering_label_rule' - cmd = metering.ShowMeteringLabelRule( - test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id']) diff --git a/neutronclient/tests/unit/test_cli20_network.py b/neutronclient/tests/unit/test_cli20_network.py deleted file mode 100644 index 5c728b05a..000000000 --- a/neutronclient/tests/unit/test_cli20_network.py +++ /dev/null @@ -1,674 +0,0 @@ -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import itertools -import sys - -from mox3 import mox -from oslo_serialization import jsonutils - -from neutronclient.common import exceptions -from neutronclient.neutron.v2_0 import network -from neutronclient import shell -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20NetworkJSON(test_cli20.CLITestV20Base): - def setUp(self): - super(CLITestV20NetworkJSON, self).setUp(plurals={'tags': 'tag'}) - - def test_create_network(self): - # Create net: myname. - resource = 'network' - cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - args = [name, ] - position_names = ['name', ] - position_values = [name, ] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_network_with_unicode(self): - # Create net: u'\u7f51\u7edc'. - resource = 'network' - cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) - name = u'\u7f51\u7edc' - myid = 'myid' - args = [name, ] - position_names = ['name', ] - position_values = [name, ] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_network_description(self): - # Create net: --tenant_id tenantid myname. - resource = 'network' - cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - args = ['--description', 'Nice network', name] - position_names = ['name', ] - position_values = [name, ] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - description='Nice network') - - def test_create_network_tenant(self): - # Create net: --tenant_id tenantid myname. - resource = 'network' - cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - args = ['--tenant_id', 'tenantid', name] - position_names = ['name', ] - position_values = [name, ] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') - - # Test dashed options - args = ['--tenant-id', 'tenantid', name] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') - - def test_create_network_provider_args(self): - # Create net: with --provider arguments. - resource = 'network' - cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - - # Test --provider attributes before network name - args = ['--provider:network_type', 'vlan', - '--provider:physical_network', 'physnet1', - '--provider:segmentation_id', '400', name] - position_names = ['provider:network_type', - 'provider:physical_network', - 'provider:segmentation_id', 'name'] - position_values = ['vlan', 'physnet1', '400', name] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_network_tags(self): - # Create net: myname --tags a b. - resource = 'network' - cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - args = [name, '--tags', 'a', 'b'] - position_names = ['name', ] - position_values = [name, ] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tags=['a', 'b']) - - def test_create_network_state(self): - # Create net: --admin_state_down myname. - resource = 'network' - cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - args = ['--admin_state_down', name, ] - position_names = ['name', ] - position_values = [name, ] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - admin_state_up=False) - - # Test dashed options - args = ['--admin-state-down', name, ] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - admin_state_up=False) - - def test_create_network_vlan_transparent(self): - # Create net: myname --vlan-transparent True. - resource = 'network' - cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - args = ['--vlan-transparent', 'True', name] - vlantrans = {'vlan_transparent': 'True'} - position_names = ['name', ] - position_values = [name, ] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - **vlantrans) - - def test_create_network_with_qos_policy(self): - # Create net: --qos-policy mypolicy. - resource = 'network' - cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - qos_policy_name = 'mypolicy' - args = [name, '--qos-policy', qos_policy_name] - position_names = ['name', 'qos_policy_id'] - position_values = [name, qos_policy_name] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_network_with_az_hint(self): - # Create net: --availability-zone-hint zone1 - # --availability-zone-hint zone2. - resource = 'network' - cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - args = ['--availability-zone-hint', 'zone1', - '--availability-zone-hint', 'zone2', name] - position_names = ['availability_zone_hints', 'name'] - position_values = [['zone1', 'zone2'], name] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_network_with_dns_domain(self): - # Create net: --dns-domain my-domain.org. - resource = 'network' - cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - dns_domain_name = 'my-domain.org.' - args = [name, '--dns-domain', dns_domain_name] - position_names = ['name', 'dns_domain'] - position_values = [name, dns_domain_name] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_list_nets_empty_with_column(self): - resources = "networks" - cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - self.mox.StubOutWithMock(network.ListNetwork, "extend_list") - network.ListNetwork.extend_list(mox.IsA(list), mox.IgnoreArg()) - cmd.get_client().MultipleTimes().AndReturn(self.client) - reses = {resources: []} - resstr = self.client.serialize(reses) - # url method body - query = "id=myfakeid" - args = ['-c', 'id', '--', '--id', 'myfakeid'] - path = getattr(self.client, resources + "_path") - self.client.httpclient.request( - test_cli20.MyUrlComparator(test_cli20.end_url(path, query), - self.client), - 'GET', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', - test_cli20.TOKEN)).AndReturn( - (test_cli20.MyResp(200), resstr)) - self.mox.ReplayAll() - cmd_parser = cmd.get_parser("list_" + resources) - shell.run_command(cmd, cmd_parser, args) - self.mox.VerifyAll() - self.mox.UnsetStubs() - _str = self.fake_stdout.make_string() - self.assertEqual('\n', _str) - - def _test_list_networks(self, cmd, detail=False, tags=(), - fields_1=(), fields_2=(), page_size=None, - sort_key=(), sort_dir=(), base_args=None, - query=''): - resources = "networks" - self.mox.StubOutWithMock(network.ListNetwork, "extend_list") - network.ListNetwork.extend_list(mox.IsA(list), mox.IgnoreArg()) - self._test_list_resources(resources, cmd, detail, tags, - fields_1, fields_2, page_size=page_size, - sort_key=sort_key, sort_dir=sort_dir, - base_args=base_args, query=query) - - def test_list_nets_pagination(self): - cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - self.mox.StubOutWithMock(network.ListNetwork, "extend_list") - network.ListNetwork.extend_list(mox.IsA(list), mox.IgnoreArg()) - self._test_list_resources_with_pagination("networks", cmd) - - def test_list_nets_sort(self): - # list nets: - # --sort-key name --sort-key id --sort-dir asc --sort-dir desc - cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_list_networks(cmd, sort_key=['name', 'id'], - sort_dir=['asc', 'desc']) - - def test_list_nets_sort_with_keys_more_than_dirs(self): - # list nets: --sort-key name --sort-key id --sort-dir desc - cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_list_networks(cmd, sort_key=['name', 'id'], - sort_dir=['desc']) - - def test_list_nets_sort_with_dirs_more_than_keys(self): - # list nets: --sort-key name --sort-dir desc --sort-dir asc - cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_list_networks(cmd, sort_key=['name'], - sort_dir=['desc', 'asc']) - - def test_list_nets_limit(self): - # list nets: -P. - cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_list_networks(cmd, page_size=1000) - - def test_list_nets_detail(self): - # list nets: -D. - cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_list_networks(cmd, True) - - def test_list_nets_tags(self): - # List nets: -- --tags a b. - cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_list_networks(cmd, tags=['a', 'b']) - - def test_list_nets_tags_with_unicode(self): - # List nets: -- --tags u'\u7f51\u7edc'. - cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_list_networks(cmd, tags=[u'\u7f51\u7edc']) - - def test_list_nets_detail_tags(self): - # List nets: -D -- --tags a b. - cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_list_networks(cmd, detail=True, tags=['a', 'b']) - - def _test_list_nets_extend_subnets(self, data, expected): - def setup_list_stub(resources, data, query): - reses = {resources: data} - resstr = self.client.serialize(reses) - resp = (test_cli20.MyResp(200), resstr) - path = getattr(self.client, resources + '_path') - self.client.httpclient.request( - test_cli20.MyUrlComparator( - test_cli20.end_url(path, query), self.client), - 'GET', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', test_cli20.TOKEN)).AndReturn(resp) - - cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - self.mox.StubOutWithMock(cmd, 'get_client') - self.mox.StubOutWithMock(self.client.httpclient, 'request') - cmd.get_client().AndReturn(self.client) - setup_list_stub('networks', data, '') - cmd.get_client().AndReturn(self.client) - filters = '' - for n in data: - for s in n['subnets']: - filters = filters + "&id=%s" % s - setup_list_stub('subnets', - [{'id': 'mysubid1', 'cidr': '192.168.1.0/24'}, - {'id': 'mysubid2', 'cidr': '172.16.0.0/24'}, - {'id': 'mysubid3', 'cidr': '10.1.1.0/24'}], - query='fields=id&fields=cidr' + filters) - self.mox.ReplayAll() - - args = [] - cmd_parser = cmd.get_parser('list_networks') - parsed_args = cmd_parser.parse_args(args) - result = cmd.take_action(parsed_args) - self.mox.VerifyAll() - self.mox.UnsetStubs() - _result = [x for x in result[1]] - self.assertEqual(len(expected), len(_result)) - for res, exp in zip(_result, expected): - self.assertEqual(len(exp), len(res)) - for obsrvd, expctd in zip(res, exp): - self.assertEqual(expctd, obsrvd) - - def test_list_nets_extend_subnets(self): - data = [{'id': 'netid1', 'name': 'net1', 'subnets': ['mysubid1']}, - {'id': 'netid2', 'name': 'net2', 'subnets': ['mysubid2', - 'mysubid3']}] - # id, name, subnets - expected = [('netid1', 'net1', 'mysubid1 192.168.1.0/24'), - ('netid2', 'net2', - 'mysubid2 172.16.0.0/24\nmysubid3 10.1.1.0/24')] - self._test_list_nets_extend_subnets(data, expected) - - def test_list_nets_extend_subnets_no_subnet(self): - data = [{'id': 'netid1', 'name': 'net1', 'subnets': ['mysubid1']}, - {'id': 'netid2', 'name': 'net2', 'subnets': ['mysubid4']}] - # id, name, subnets - expected = [('netid1', 'net1', 'mysubid1 192.168.1.0/24'), - ('netid2', 'net2', 'mysubid4 ')] - self._test_list_nets_extend_subnets(data, expected) - - def test_list_nets_fields(self): - # List nets: --fields a --fields b -- --fields c d. - cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_list_networks(cmd, - fields_1=['a', 'b'], fields_2=['c', 'd']) - - def _test_list_nets_columns(self, cmd, returned_body, - args=('-f', 'json')): - resources = 'networks' - self.mox.StubOutWithMock(network.ListNetwork, "extend_list") - network.ListNetwork.extend_list(mox.IsA(list), mox.IgnoreArg()) - self._test_list_columns(cmd, resources, returned_body, args=args) - - def test_list_nets_defined_column(self): - cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - returned_body = {"networks": [{"name": "buildname3", - "id": "id3", - "tenant_id": "tenant_3", - "subnets": []}]} - self._test_list_nets_columns(cmd, returned_body, - args=['-f', 'json', '-c', 'id']) - _str = self.fake_stdout.make_string() - returned_networks = jsonutils.loads(_str) - self.assertEqual(1, len(returned_networks)) - net = returned_networks[0] - self.assertEqual(1, len(net)) - self.assertIn("id", net.keys()) - - def test_list_nets_with_default_column(self): - cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - returned_body = {"networks": [{"name": "buildname3", - "id": "id3", - "tenant_id": "tenant_3", - "subnets": []}]} - self._test_list_nets_columns(cmd, returned_body) - _str = self.fake_stdout.make_string() - returned_networks = jsonutils.loads(_str) - self.assertEqual(1, len(returned_networks)) - net = returned_networks[0] - self.assertEqual(3, len(net)) - self.assertEqual(0, len(set(net) ^ set(cmd.list_columns))) - - def test_list_external_nets_empty_with_column(self): - resources = "networks" - cmd = network.ListExternalNetwork(test_cli20.MyApp(sys.stdout), None) - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - self.mox.StubOutWithMock(network.ListNetwork, "extend_list") - network.ListNetwork.extend_list(mox.IsA(list), mox.IgnoreArg()) - cmd.get_client().MultipleTimes().AndReturn(self.client) - reses = {resources: []} - resstr = self.client.serialize(reses) - # url method body - query = "router%3Aexternal=True&id=myfakeid" - args = ['-c', 'id', '--', '--id', 'myfakeid'] - path = getattr(self.client, resources + "_path") - self.client.httpclient.request( - test_cli20.MyUrlComparator( - test_cli20.end_url(path, query), self.client), - 'GET', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', - test_cli20.TOKEN)).AndReturn( - (test_cli20.MyResp(200), resstr)) - self.mox.ReplayAll() - cmd_parser = cmd.get_parser("list_" + resources) - shell.run_command(cmd, cmd_parser, args) - self.mox.VerifyAll() - self.mox.UnsetStubs() - _str = self.fake_stdout.make_string() - self.assertEqual('\n', _str) - - def _test_list_external_nets(self, resources, cmd, - detail=False, tags=(), - fields_1=(), fields_2=()): - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - self.mox.StubOutWithMock(network.ListNetwork, "extend_list") - network.ListNetwork.extend_list(mox.IsA(list), mox.IgnoreArg()) - cmd.get_client().MultipleTimes().AndReturn(self.client) - reses = {resources: [{'id': 'myid1', }, - {'id': 'myid2', }, ], } - - resstr = self.client.serialize(reses) - - # url method body - query = "" - args = detail and ['-D', ] or [] - if fields_1: - for field in fields_1: - args.append('--fields') - args.append(field) - if tags: - args.append('--') - args.append("--tag") - for tag in tags: - args.append(tag) - if (not tags) and fields_2: - args.append('--') - if fields_2: - args.append("--fields") - for field in fields_2: - args.append(field) - for field in itertools.chain(fields_1, fields_2): - if query: - query += "&fields=" + field - else: - query = "fields=" + field - if query: - query += '&router%3Aexternal=True' - else: - query += 'router%3Aexternal=True' - for tag in tags: - if query: - query += "&tag=" + tag - else: - query = "tag=" + tag - if detail: - query = query and query + '&verbose=True' or 'verbose=True' - path = getattr(self.client, resources + "_path") - - self.client.httpclient.request( - test_cli20.MyUrlComparator( - test_cli20.end_url(path, query), self.client), - 'GET', - body=None, - headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN) - ).AndReturn((test_cli20.MyResp(200), resstr)) - self.mox.ReplayAll() - cmd_parser = cmd.get_parser("list_" + resources) - shell.run_command(cmd, cmd_parser, args) - self.mox.VerifyAll() - self.mox.UnsetStubs() - _str = self.fake_stdout.make_string() - - self.assertIn('myid1', _str) - - def test_list_external_nets_detail(self): - # list external nets: -D. - resources = "networks" - cmd = network.ListExternalNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_list_external_nets(resources, cmd, True) - - def test_list_external_nets_tags(self): - # List external nets: -- --tags a b. - resources = "networks" - cmd = network.ListExternalNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_list_external_nets(resources, - cmd, tags=['a', 'b']) - - def test_list_external_nets_detail_tags(self): - # List external nets: -D -- --tags a b. - resources = "networks" - cmd = network.ListExternalNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_list_external_nets(resources, cmd, - detail=True, tags=['a', 'b']) - - def test_list_externel_nets_fields(self): - # List external nets: --fields a --fields b -- --fields c d. - resources = "networks" - cmd = network.ListExternalNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_list_external_nets(resources, cmd, - fields_1=['a', 'b'], - fields_2=['c', 'd']) - - def test_update_network_exception(self): - # Update net: myid. - resource = 'network' - cmd = network.UpdateNetwork(test_cli20.MyApp(sys.stdout), None) - self.assertRaises(exceptions.CommandError, self._test_update_resource, - resource, cmd, 'myid', ['myid'], {}) - - def test_update_network(self): - # Update net: myid --name myname --tags a b. - resource = 'network' - cmd = network.UpdateNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'myname', - '--tags', 'a', 'b', '--description', - 'This network takes the scenic route'], - {'name': 'myname', 'tags': ['a', 'b'], - 'description': 'This network takes the ' - 'scenic route'}) - - def test_update_network_with_unicode(self): - # Update net: myid --name u'\u7f51\u7edc' --tags a b. - resource = 'network' - cmd = network.UpdateNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', u'\u7f51\u7edc', - '--tags', 'a', 'b'], - {'name': u'\u7f51\u7edc', - 'tags': ['a', 'b'], } - ) - - def test_update_network_with_qos_policy(self): - # Update net: myid --qos-policy mypolicy. - resource = 'network' - cmd = network.UpdateNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--qos-policy', 'mypolicy'], - {'qos_policy_id': 'mypolicy', }) - - def test_update_network_with_no_qos_policy(self): - # Update net: myid --no-qos-policy. - resource = 'network' - cmd = network.UpdateNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--no-qos-policy'], - {'qos_policy_id': None, }) - - def test_update_network_with_dns_domain(self): - # Update net: myid --dns-domain my-domain.org. - resource = 'network' - cmd = network.UpdateNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--dns-domain', 'my-domain.org.'], - {'dns_domain': 'my-domain.org.', }) - - def test_update_network_with_no_dns_domain(self): - # Update net: myid --no-dns-domain - resource = 'network' - cmd = network.UpdateNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--no-dns-domain'], - {'dns_domain': "", }) - - def test_show_network(self): - # Show net: --fields id --fields name myid. - resource = 'network' - cmd = network.ShowNetwork(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, - ['id', 'name']) - - def test_delete_network(self): - # Delete net: myid. - resource = 'network' - cmd = network.DeleteNetwork(test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = [myid] - self._test_delete_resource(resource, cmd, myid, args) - - def test_bulk_delete_network(self): - # Delete net: myid1 myid2. - resource = 'network' - cmd = network.DeleteNetwork(test_cli20.MyApp(sys.stdout), None) - myid1 = 'myid1' - myid2 = 'myid2' - args = [myid1, myid2] - self._test_delete_resource(resource, cmd, myid1, args, extra_id=myid2) - - def _test_extend_list(self, mox_calls): - data = [{'id': 'netid%d' % i, 'name': 'net%d' % i, - 'subnets': ['mysubid%d' % i]} - for i in range(10)] - self.mox.StubOutWithMock(self.client.httpclient, "request") - path = getattr(self.client, 'subnets_path') - cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - self.mox.StubOutWithMock(cmd, "get_client") - cmd.get_client().MultipleTimes().AndReturn(self.client) - mox_calls(path, data) - self.mox.ReplayAll() - known_args, _vs = cmd.get_parser('create_subnets').parse_known_args() - cmd.extend_list(data, known_args) - self.mox.VerifyAll() - - def _build_test_data(self, data): - subnet_ids = [] - response = [] - filters = "" - for n in data: - if 'subnets' in n: - subnet_ids.extend(n['subnets']) - for subnet_id in n['subnets']: - filters = "%s&id=%s" % (filters, subnet_id) - response.append({'id': subnet_id, - 'cidr': '192.168.0.0/16'}) - resp_str = self.client.serialize({'subnets': response}) - resp = (test_cli20.MyResp(200), resp_str) - return filters, resp - - def test_extend_list(self): - def mox_calls(path, data): - filters, response = self._build_test_data(data) - self.client.httpclient.request( - test_cli20.MyUrlComparator(test_cli20.end_url( - path, 'fields=id&fields=cidr' + filters), self.client), - 'GET', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', test_cli20.TOKEN)).AndReturn(response) - - self._test_extend_list(mox_calls) - - def test_extend_list_exceed_max_uri_len(self): - def mox_calls(path, data): - sub_data_lists = [data[:len(data) - 1], data[len(data) - 1:]] - filters, response = self._build_test_data(data) - - # 1 char of extra URI len will cause a split in 2 requests - self.mox.StubOutWithMock(self.client.httpclient, - "_check_uri_length") - self.client.httpclient._check_uri_length(mox.IgnoreArg()).AndRaise( - exceptions.RequestURITooLong(excess=1)) - - for data in sub_data_lists: - filters, response = self._build_test_data(data) - self.client.httpclient._check_uri_length( - mox.IgnoreArg()).AndReturn(None) - self.client.httpclient.request( - test_cli20.MyUrlComparator( - test_cli20.end_url( - path, 'fields=id&fields=cidr%s' % filters), - self.client), - 'GET', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', test_cli20.TOKEN)).AndReturn(response) - - self._test_extend_list(mox_calls) - - def test_list_shared_networks(self): - # list nets : --shared False - cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - self._test_list_networks(cmd, base_args='--shared False'.split(), - query='shared=False') diff --git a/neutronclient/tests/unit/test_cli20_network_ip_availability.py b/neutronclient/tests/unit/test_cli20_network_ip_availability.py deleted file mode 100644 index eb325a8f6..000000000 --- a/neutronclient/tests/unit/test_cli20_network_ip_availability.py +++ /dev/null @@ -1,54 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import sys - -from neutronclient.neutron.v2_0 import network_ip_availability -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20NetworkIPAvailability(test_cli20.CLITestV20Base): - - id_field = 'network_id' - - def _test_list_network_ip_availability(self, args, query): - resources = "network_ip_availabilities" - cmd = network_ip_availability.ListIpAvailability(test_cli20.MyApp - (sys.stdout), None) - self._test_list_resources(resources, cmd, - base_args=args, - query=query) - - def test_list_network_ip_availability(self): - self._test_list_network_ip_availability(args=None, - query='ip_version=4') - - def test_list_network_ip_availability_ipv6(self): - self._test_list_network_ip_availability( - args=['--ip-version', '6'], query='ip_version=6') - - def test_list_network_ip_availability_net_id_and_ipv4(self): - self._test_list_network_ip_availability( - args=['--ip-version', '4', '--network-id', 'myid'], - query='ip_version=4&network_id=myid') - - def test_list_network_ip_availability_net_name_and_tenant_id(self): - self._test_list_network_ip_availability( - args=['--network-name', 'foo', '--tenant-id', 'mytenant'], - query='network_name=foo&tenant_id=mytenant&ip_version=4') - - def test_show_network_ip_availability(self): - resource = "network_ip_availability" - cmd = network_ip_availability.ShowIpAvailability( - test_cli20.MyApp(sys.stdout), None) - self._test_show_resource(resource, cmd, self.test_id, - args=[self.test_id]) diff --git a/neutronclient/tests/unit/test_cli20_port.py b/neutronclient/tests/unit/test_cli20_port.py deleted file mode 100644 index 50eaffbdb..000000000 --- a/neutronclient/tests/unit/test_cli20_port.py +++ /dev/null @@ -1,774 +0,0 @@ -# Copyright 2012 OpenStack Foundation. -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import itertools -import sys - -from mox3 import mox - -from neutronclient.neutron.v2_0 import port -from neutronclient import shell -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20PortJSON(test_cli20.CLITestV20Base): - def setUp(self): - super(CLITestV20PortJSON, self).setUp(plurals={'tags': 'tag'}) - - def test_create_port(self): - # Create port: netid. - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - args = [netid, '--description', 'DESC'] - position_names = ['network_id'] - position_values = [] - position_values.extend([netid]) - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - description='DESC') - - def test_create_port_extra_dhcp_opts_args(self): - # Create port: netid --extra_dhcp_opt. - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - extra_dhcp_opts = [{'opt_name': 'bootfile-name', - 'opt_value': 'pxelinux.0'}, - {'opt_name': 'tftp-server', - 'opt_value': '123.123.123.123'}, - {'opt_name': 'server-ip-address', - 'opt_value': '123.123.123.45'}] - args = [netid] - for dhcp_opt in extra_dhcp_opts: - args += ['--extra-dhcp-opt', - ('opt_name=%(opt_name)s,opt_value=%(opt_value)s' % - dhcp_opt)] - position_names = ['network_id', 'extra_dhcp_opts'] - position_values = [netid, extra_dhcp_opts] - position_values.extend([netid]) - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_port_extra_dhcp_opts_args_ip_version(self): - # Create port: netid --extra_dhcp_opt. - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - extra_dhcp_opts = [{'opt_name': 'bootfile-name', - 'opt_value': 'pxelinux.0', - 'ip_version': "4"}, - {'opt_name': 'tftp-server', - 'opt_value': '2001:192:168::1', - 'ip_version': "6"}, - {'opt_name': 'server-ip-address', - 'opt_value': '123.123.123.45', - 'ip_version': "4"}] - args = [netid] - for dhcp_opt in extra_dhcp_opts: - args += ['--extra-dhcp-opt', - ('opt_name=%(opt_name)s,opt_value=%(opt_value)s,' - 'ip_version=%(ip_version)s' % - dhcp_opt)] - position_names = ['network_id', 'extra_dhcp_opts'] - position_values = [netid, extra_dhcp_opts] - position_values.extend([netid]) - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_port_full(self): - # Create port: --mac_address mac --device_id deviceid netid. - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - args = ['--mac_address', 'mac', '--device_id', 'deviceid', netid] - position_names = ['network_id', 'mac_address', 'device_id'] - position_values = [netid, 'mac', 'deviceid'] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - # Test dashed options - args = ['--mac-address', 'mac', '--device-id', 'deviceid', netid] - position_names = ['network_id', 'mac_address', 'device_id'] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_port_vnic_type_normal(self): - # Create port: --vnic_type normal netid. - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - args = ['--vnic_type', 'normal', netid] - position_names = ['binding:vnic_type', 'network_id'] - position_values = ['normal', netid] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - # Test dashed options - args = ['--vnic-type', 'normal', netid] - position_names = ['binding:vnic_type', 'network_id'] - position_values = ['normal', netid] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_port_vnic_type_direct(self): - # Create port: --vnic_type direct netid. - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - args = ['--vnic_type', 'direct', netid] - position_names = ['binding:vnic_type', 'network_id'] - position_values = ['direct', netid] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - # Test dashed options - args = ['--vnic-type', 'direct', netid] - position_names = ['binding:vnic_type', 'network_id'] - position_values = ['direct', netid] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_port_vnic_type_direct_physical(self): - # Create port: --vnic_type direct-physical netid. - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - args = ['--vnic_type', 'direct-physical', netid] - position_names = ['binding:vnic_type', 'network_id'] - position_values = ['direct-physical', netid] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - # Test dashed options - args = ['--vnic-type', 'direct-physical', netid] - position_names = ['binding:vnic_type', 'network_id'] - position_values = ['direct-physical', netid] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_port_vnic_type_macvtap(self): - # Create port: --vnic_type macvtap netid. - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - args = ['--vnic_type', 'macvtap', netid] - position_names = ['binding:vnic_type', 'network_id'] - position_values = ['macvtap', netid] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - # Test dashed options - args = ['--vnic-type', 'macvtap', netid] - position_names = ['binding:vnic_type', 'network_id'] - position_values = ['macvtap', netid] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_port_vnic_type_baremetal(self): - # Create port: --vnic_type baremetal netid. - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - args = ['--vnic_type', 'baremetal', netid] - position_names = ['binding:vnic_type', 'network_id'] - position_values = ['baremetal', netid] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - # Test dashed options - args = ['--vnic-type', 'baremetal', netid] - position_names = ['binding:vnic_type', 'network_id'] - position_values = ['baremetal', netid] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_port_with_binding_profile(self): - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - args = ['--binding_profile', '{"foo":"bar"}', netid] - position_names = ['binding:profile', 'network_id'] - position_values = [{'foo': 'bar'}, netid] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - # Test dashed options - args = ['--binding-profile', '{"foo":"bar"}', netid] - position_names = ['binding:profile', 'network_id'] - position_values = [{'foo': 'bar'}, netid] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_port_tenant(self): - # Create port: --tenant_id tenantid netid. - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - args = ['--tenant_id', 'tenantid', netid, ] - position_names = ['network_id'] - position_values = [] - position_values.extend([netid]) - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') - - # Test dashed options - args = ['--tenant-id', 'tenantid', netid, ] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') - - def test_create_port_tags(self): - # Create port: netid mac_address device_id --tags a b. - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - args = [netid, '--tags', 'a', 'b'] - position_names = ['network_id'] - position_values = [] - position_values.extend([netid]) - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tags=['a', 'b']) - - def test_create_port_secgroup(self): - # Create port: --security-group sg1_id netid. - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - args = ['--security-group', 'sg1_id', netid] - position_names = ['network_id', 'security_groups'] - position_values = [netid, ['sg1_id']] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_port_secgroups(self): - # Create port: netid - # The are --security-group sg1_id - # --security-group sg2_id - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - args = ['--security-group', 'sg1_id', - '--security-group', 'sg2_id', - netid] - position_names = ['network_id', 'security_groups'] - position_values = [netid, ['sg1_id', 'sg2_id']] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_port_secgroup_off(self): - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - args = ['--no-security-groups', netid] - position_names = ['network_id', 'security_groups'] - position_values = [netid, []] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_port_secgroups_list(self): - # Create port: netid - # The are --security-groups list=true sg_id1 sg_id2 - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - args = [netid, '--security-groups', 'list=true', 'sg_id1', 'sg_id2'] - position_names = ['network_id', 'security_groups'] - position_values = [netid, ['sg_id1', 'sg_id2']] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_port_with_qos_policy(self): - # Create port: --qos-policy mypolicy. - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - qos_policy_name = 'mypolicy' - args = [netid, '--qos-policy', qos_policy_name] - position_names = ['network_id', 'qos_policy_id'] - position_values = [netid, qos_policy_name] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_port_with_dns_name(self): - # Create port: --dns-name my-port. - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - dns_name_name = 'my-port' - args = [netid, '--dns-name', dns_name_name] - position_names = ['network_id', 'dns_name'] - position_values = [netid, dns_name_name] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_port_with_allowed_address_pair_ipaddr(self): - # Create port: - # --allowed-address-pair ip_address=addr0 - # --allowed-address-pair ip_address=addr1 - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - pairs = [{'ip_address': '123.123.123.123'}, - {'ip_address': '123.123.123.45'}] - args = [netid, - '--allowed-address-pair', - 'ip_address=123.123.123.123', - '--allowed-address-pair', - 'ip_address=123.123.123.45'] - position_names = ['network_id', 'allowed_address_pairs'] - position_values = [netid, pairs] - position_values.extend([netid]) - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_port_with_allowed_address_pair(self): - # Create port: - # --allowed-address-pair ip_address=addr0,mac_address=mac0 - # --allowed-address-pair ip_address=addr1,mac_address=mac1 - resource = 'port' - cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - pairs = [{'ip_address': '123.123.123.123', - 'mac_address': '10:00:00:00:00:00'}, - {'ip_address': '123.123.123.45', - 'mac_address': '10:00:00:00:00:01'}] - args = [netid, - '--allowed-address-pair', - 'ip_address=123.123.123.123,mac_address=10:00:00:00:00:00', - '--allowed-address-pair', - 'ip_address=123.123.123.45,mac_address=10:00:00:00:00:01'] - position_names = ['network_id', 'allowed_address_pairs'] - position_values = [netid, pairs] - position_values.extend([netid]) - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_list_ports(self): - # List ports: -D. - resources = "ports" - cmd = port.ListPort(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True) - - def test_list_ports_pagination(self): - resources = "ports" - cmd = port.ListPort(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_ports_sort(self): - # list ports: - # --sort-key name --sort-key id --sort-key asc --sort-key desc - resources = "ports" - cmd = port.ListPort(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_ports_limit(self): - # list ports: -P. - resources = "ports" - cmd = port.ListPort(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_list_ports_tags(self): - # List ports: -- --tags a b. - resources = "ports" - cmd = port.ListPort(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, tags=['a', 'b']) - - def test_list_ports_detail_tags(self): - # List ports: -D -- --tags a b. - resources = "ports" - cmd = port.ListPort(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, detail=True, tags=['a', 'b']) - - def test_list_ports_fields(self): - # List ports: --fields a --fields b -- --fields c d. - resources = "ports" - cmd = port.ListPort(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - fields_1=['a', 'b'], fields_2=['c', 'd']) - - def test_list_ports_with_fixed_ips_in_csv(self): - # List ports: -f csv. - resources = "ports" - cmd = port.ListPort(test_cli20.MyApp(sys.stdout), None) - fixed_ips = [{"subnet_id": "30422057-d6df-4c90-8314-aefb5e326666", - "ip_address": "10.0.0.12"}, - {"subnet_id": "30422057-d6df-4c90-8314-aefb5e326666", - "ip_address": "10.0.0.4"}] - contents = [{'name': 'name1', 'fixed_ips': fixed_ips}] - self._test_list_resources(resources, cmd, True, - response_contents=contents, - output_format='csv') - - def _test_list_router_port(self, resources, cmd, - myid, detail=False, tags=(), - fields_1=(), fields_2=()): - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) - reses = {resources: [{'id': 'myid1', }, - {'id': 'myid2', }, ], } - - resstr = self.client.serialize(reses) - - # url method body - query = "" - args = detail and ['-D', ] or [] - - if fields_1: - for field in fields_1: - args.append('--fields') - args.append(field) - args.append(myid) - if tags: - args.append('--') - args.append("--tag") - for tag in tags: - args.append(tag) - if (not tags) and fields_2: - args.append('--') - if fields_2: - args.append("--fields") - for field in fields_2: - args.append(field) - for field in itertools.chain(fields_1, fields_2): - if query: - query += "&fields=" + field - else: - query = "fields=" + field - - for tag in tags: - if query: - query += "&tag=" + tag - else: - query = "tag=" + tag - if detail: - query = query and query + '&verbose=True' or 'verbose=True' - query = query and query + '&device_id=%s' or 'device_id=%s' - path = getattr(self.client, resources + "_path") - self.client.httpclient.request( - test_cli20.MyUrlComparator( - test_cli20.end_url(path, query % myid), - self.client), - 'GET', - body=None, - headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN) - ).AndReturn((test_cli20.MyResp(200), resstr)) - self.mox.ReplayAll() - cmd_parser = cmd.get_parser("list_" + resources) - shell.run_command(cmd, cmd_parser, args) - self.mox.VerifyAll() - self.mox.UnsetStubs() - _str = self.fake_stdout.make_string() - - self.assertIn('myid1', _str) - - def test_list_router_ports(self): - # List router ports: -D. - resources = "ports" - cmd = port.ListRouterPort(test_cli20.MyApp(sys.stdout), None) - self._test_list_router_port(resources, cmd, - self.test_id, True) - - def test_list_router_ports_tags(self): - # List router ports: -- --tags a b. - resources = "ports" - cmd = port.ListRouterPort(test_cli20.MyApp(sys.stdout), None) - self._test_list_router_port(resources, cmd, - self.test_id, tags=['a', 'b']) - - def test_list_router_ports_detail_tags(self): - # List router ports: -D -- --tags a b. - resources = "ports" - cmd = port.ListRouterPort(test_cli20.MyApp(sys.stdout), None) - self._test_list_router_port(resources, cmd, self.test_id, - detail=True, tags=['a', 'b']) - - def test_list_router_ports_fields(self): - # List ports: --fields a --fields b -- --fields c d. - resources = "ports" - cmd = port.ListRouterPort(test_cli20.MyApp(sys.stdout), None) - self._test_list_router_port(resources, cmd, self.test_id, - fields_1=['a', 'b'], - fields_2=['c', 'd']) - - def test_update_port(self): - # Update port: myid --name myname --admin-state-up False --tags a b. - resource = 'port' - cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'myname', - '--admin-state-up', 'False', - '--description', 'garbage', - '--tags', 'a', 'b'], - {'name': 'myname', - 'admin_state_up': 'False', - 'description': 'garbage', - 'tags': ['a', 'b'], }) - - def test_update_port_secgroup(self): - resource = 'port' - cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = ['--security-group', 'sg1_id', myid] - updatefields = {'security_groups': ['sg1_id']} - self._test_update_resource(resource, cmd, myid, args, updatefields) - - def test_update_port_secgroups(self): - resource = 'port' - cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = ['--security-group', 'sg1_id', - '--security-group', 'sg2_id', - myid] - updatefields = {'security_groups': ['sg1_id', 'sg2_id']} - self._test_update_resource(resource, cmd, myid, args, updatefields) - - def test_update_port_extra_dhcp_opts(self): - # Update port: myid --extra_dhcp_opt. - resource = 'port' - myid = 'myid' - args = [myid, - '--extra-dhcp-opt', - "opt_name=bootfile-name,opt_value=pxelinux.0", - '--extra-dhcp-opt', - "opt_name=tftp-server,opt_value=123.123.123.123", - '--extra-dhcp-opt', - "opt_name=server-ip-address,opt_value=123.123.123.45" - ] - updatedfields = {'extra_dhcp_opts': [{'opt_name': 'bootfile-name', - 'opt_value': 'pxelinux.0'}, - {'opt_name': 'tftp-server', - 'opt_value': '123.123.123.123'}, - {'opt_name': 'server-ip-address', - 'opt_value': '123.123.123.45'}]} - cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, myid, args, updatedfields) - - def test_update_port_fixed_ip(self): - resource = 'port' - cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - subnet_id = 'subnet_id' - ip_addr = '123.123.123.123' - args = [myid, - '--fixed-ip', - "subnet_id=%(subnet_id)s,ip_address=%(ip_addr)s" % - {'subnet_id': subnet_id, - 'ip_addr': ip_addr}] - updated_fields = {"fixed_ips": [{'subnet_id': subnet_id, - 'ip_address': ip_addr}]} - self._test_update_resource(resource, cmd, myid, args, updated_fields) - - def test_update_port_device_id_device_owner(self): - resource = 'port' - cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = ['--device-id', 'dev_id', '--device-owner', 'fake', myid] - updatefields = {'device_id': 'dev_id', - 'device_owner': 'fake'} - self._test_update_resource(resource, cmd, myid, args, updatefields) - - def test_update_port_extra_dhcp_opts_ip_version(self): - # Update port: myid --extra_dhcp_opt. - resource = 'port' - myid = 'myid' - args = [myid, - '--extra-dhcp-opt', - "opt_name=bootfile-name,opt_value=pxelinux.0,ip_version=4", - '--extra-dhcp-opt', - "opt_name=tftp-server,opt_value=2001:192:168::1,ip_version=6", - '--extra-dhcp-opt', - "opt_name=server-ip-address,opt_value=null,ip_version=4" - ] - updatedfields = {'extra_dhcp_opts': [{'opt_name': 'bootfile-name', - 'opt_value': 'pxelinux.0', - 'ip_version': '4'}, - {'opt_name': 'tftp-server', - 'opt_value': '2001:192:168::1', - 'ip_version': '6'}, - {'opt_name': 'server-ip-address', - 'opt_value': None, - 'ip_version': '4'}]} - cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, myid, args, updatedfields) - - def test_update_port_with_qos_policy(self): - # Update port: myid --qos-policy mypolicy. - resource = 'port' - cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--qos-policy', 'mypolicy'], - {'qos_policy_id': 'mypolicy', }) - - def test_update_port_with_no_qos_policy(self): - # Update port: myid --no-qos-policy. - resource = 'port' - cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--no-qos-policy'], - {'qos_policy_id': None, }) - - def test_update_port_with_dns_name(self): - # Update port: myid --dns-name my-port. - resource = 'port' - cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--dns-name', 'my-port'], - {'dns_name': 'my-port', }) - - def test_update_port_with_no_dns_name(self): - # Update port: myid --no-dns-name - resource = 'port' - cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--no-dns-name'], - {'dns_name': "", }) - - def test_delete_extra_dhcp_opts_from_port(self): - resource = 'port' - myid = 'myid' - args = [myid, - '--extra-dhcp-opt', - "opt_name=bootfile-name,opt_value=null", - '--extra-dhcp-opt', - "opt_name=tftp-server,opt_value=123.123.123.123", - '--extra-dhcp-opt', - "opt_name=server-ip-address,opt_value=123.123.123.45" - ] - # the client code will change the null to None and send to server, - # where its interpreted as delete the DHCP option on the port. - updatedfields = {'extra_dhcp_opts': [{'opt_name': 'bootfile-name', - 'opt_value': None}, - {'opt_name': 'tftp-server', - 'opt_value': '123.123.123.123'}, - {'opt_name': 'server-ip-address', - 'opt_value': '123.123.123.45'}]} - cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, myid, args, updatedfields) - - def test_update_port_security_group_off(self): - # Update port: --no-security-groups myid. - resource = 'port' - cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['--no-security-groups', 'myid'], - {'security_groups': []}) - - def test_update_port_allowed_address_pair_ipaddr(self): - # Update port(ip_address only): - # --allowed-address-pairs ip_address=addr0 - # --allowed-address-pairs ip_address=addr1 - import sys - resource = 'port' - cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - pairs = [{'ip_address': '123.123.123.123'}, - {'ip_address': '123.123.123.45'}] - args = [myid, - '--allowed-address-pair', - 'ip_address=123.123.123.123', - '--allowed-address-pair', - 'ip_address=123.123.123.45'] - updatefields = {'allowed_address_pairs': pairs} - self._test_update_resource(resource, cmd, myid, args, updatefields) - - def test_update_port_allowed_address_pair(self): - # Update port: - # --allowed-address-pair ip_address=addr0,mac_address=mac0 - # --allowed-address-pair ip_address_addr1,mac_address=mac1 - resource = 'port' - cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - pairs = [{'ip_address': '123.123.123.123', - 'mac_address': '10:00:00:00:00:00'}, - {'ip_address': '123.123.123.45', - 'mac_address': '10:00:00:00:00:01'}] - args = [myid, - '--allowed-address-pair', - 'ip_address=123.123.123.123,mac_address=10:00:00:00:00:00', - '--allowed-address-pair', - 'ip_address=123.123.123.45,mac_address=10:00:00:00:00:01'] - updatefields = {'allowed_address_pairs': pairs} - self._test_update_resource(resource, cmd, myid, args, updatefields) - - def test_update_port_allowed_address_pairs_off(self): - # Update port: --no-allowed-address-pairs. - resource = 'port' - cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['--no-allowed-address-pairs', 'myid'], - {'allowed_address_pairs': []}) - - def test_show_port(self): - # Show port: --fields id --fields name myid. - resource = 'port' - cmd = port.ShowPort(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name']) - - def test_delete_port(self): - # Delete port: myid. - resource = 'port' - cmd = port.DeletePort(test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = [myid] - self._test_delete_resource(resource, cmd, myid, args) diff --git a/neutronclient/tests/unit/test_cli20_purge.py b/neutronclient/tests/unit/test_cli20_purge.py deleted file mode 100644 index 9bfd91c53..000000000 --- a/neutronclient/tests/unit/test_cli20_purge.py +++ /dev/null @@ -1,100 +0,0 @@ -# Copyright 2016 Cisco Systems -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import sys - -from neutronclient.neutron.v2_0 import purge -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20Purge(test_cli20.CLITestV20Base): - - def setUp(self): - super(CLITestV20Purge, self).setUp() - self.resource_types = ['floatingip', 'port', 'router', - 'network', 'security_group'] - - def _generate_resources_dict(self, value=0): - resources_dict = {} - resources_dict['true'] = value - for resource_type in self.resource_types: - resources_dict[resource_type] = value - return resources_dict - - def _verify_suffix(self, resources, message): - for resource, value in resources.items(): - if value > 0: - suffix = list('%(value)d %(resource)s' % - {'value': value, 'resource': resource}) - if value != 1: - suffix.append('s') - suffix = ''.join(suffix) - self.assertIn(suffix, message) - else: - self.assertNotIn(resource, message) - - def _verify_message(self, message, deleted, failed): - message = message.split('.') - success_prefix = "Deleted " - failure_prefix = "The following resources could not be deleted: " - if not deleted['true']: - for msg in message: - self.assertNotIn(success_prefix, msg) - message = message[0] - if not failed['true']: - expected = 'Tenant has no supported resources' - self.assertEqual(expected, message) - else: - self.assertIn(failure_prefix, message) - self._verify_suffix(failed, message) - else: - resources_deleted = message[0] - self.assertIn(success_prefix, resources_deleted) - self._verify_suffix(deleted, resources_deleted) - if failed['true']: - resources_failed = message[1] - self.assertIn(failure_prefix, resources_failed) - self._verify_suffix(failed, resources_failed) - else: - for msg in message: - self.assertNotIn(failure_prefix, msg) - - def _verify_result(self, my_purge, deleted, failed): - message = my_purge._build_message(deleted, failed, failed['true']) - self._verify_message(message, deleted, failed) - - def test_build_message(self): - my_purge = purge.Purge(test_cli20.MyApp(sys.stdout), None) - - # Verify message when tenant has no supported resources - deleted = self._generate_resources_dict() - failed = self._generate_resources_dict() - self._verify_result(my_purge, deleted, failed) - - # Verify message when tenant has supported resources, - # and they are all deleteable - deleted = self._generate_resources_dict(1) - self._verify_result(my_purge, deleted, failed) - - # Verify message when tenant has supported resources, - # and some are not deleteable - failed = self._generate_resources_dict(1) - self._verify_result(my_purge, deleted, failed) - - # Verify message when tenant has supported resources, - # and all are not deleteable - deleted = self._generate_resources_dict() - self._verify_result(my_purge, deleted, failed) diff --git a/neutronclient/tests/unit/test_cli20_rbac.py b/neutronclient/tests/unit/test_cli20_rbac.py deleted file mode 100644 index ca7ea46d2..000000000 --- a/neutronclient/tests/unit/test_cli20_rbac.py +++ /dev/null @@ -1,134 +0,0 @@ -# Copyright 2015 Huawei Technologies India Pvt Ltd. -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import sys - -import testscenarios - -from neutronclient.neutron.v2_0 import rbac -from neutronclient.tests.unit import test_cli20 - -load_tests = testscenarios.load_tests_apply_scenarios - - -class CLITestV20RBACBaseJSON(test_cli20.CLITestV20Base): - non_admin_status_resources = ['rbac_policy'] - - scenarios = [ - ('network rbac objects', - {'object_type_name': 'network', 'object_type_val': 'network'}), - ('qos policy rbac objects', - {'object_type_name': 'qos-policy', 'object_type_val': 'qos_policy'}), - ] - - def test_create_rbac_policy_with_mandatory_params(self): - # Create rbac: rbac_object --type --action - # access_as_shared - resource = 'rbac_policy' - cmd = rbac.CreateRBACPolicy(test_cli20.MyApp(sys.stdout), None) - name = 'rbac_object' - myid = 'myid' - args = [name, '--type', self.object_type_name, - '--action', 'access_as_shared'] - position_names = ['object_id', 'object_type', - 'target_tenant', 'action'] - position_values = [name, self.object_type_val, None, - 'access_as_shared'] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_rbac_policy_with_all_params(self): - # Create rbac: rbac_object --type - # --target-tenant tenant_id --action access_as_external - resource = 'rbac_policy' - cmd = rbac.CreateRBACPolicy(test_cli20.MyApp(sys.stdout), None) - name = 'rbac_object' - myid = 'myid' - args = [name, '--type', self.object_type_name, - '--target-tenant', 'tenant_id', - '--action', 'access_as_external'] - position_names = ['object_id', 'object_type', - 'target_tenant', 'action'] - position_values = [name, self.object_type_val, 'tenant_id', - 'access_as_external'] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_rbac_policy_with_unicode(self): - # Create rbac policy u'\u7f51\u7edc'. - resource = 'rbac_policy' - cmd = rbac.CreateRBACPolicy(test_cli20.MyApp(sys.stdout), None) - name = u'\u7f51\u7edc' - myid = 'myid' - args = [name, '--type', self.object_type_name, - '--target-tenant', 'tenant_id', - '--action', 'access_as_external'] - position_names = ['object_id', 'object_type', - 'target_tenant', 'action'] - position_values = [name, self.object_type_val, 'tenant_id', - 'access_as_external'] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_update_rbac_policy(self): - # rbac-update --target-tenant . - resource = 'rbac_policy' - cmd = rbac.UpdateRBACPolicy(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--target-tenant', 'tenant_id'], - {'target_tenant': 'tenant_id', }) - - def test_delete_rbac_policy(self): - # rbac-delete my-id. - resource = 'rbac_policy' - cmd = rbac.DeleteRBACPolicy(test_cli20.MyApp(sys.stdout), None) - my_id = 'myid1' - args = [my_id] - self._test_delete_resource(resource, cmd, my_id, args) - - def test_list_rbac_policies(self): - # rbac-list. - resources = "rbac_policies" - cmd = rbac.ListRBACPolicy(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True) - - def test_list_rbac_policies_pagination(self): - # rbac-list with pagination. - resources = "rbac_policies" - cmd = rbac.ListRBACPolicy(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_rbac_policies_sort(self): - # sorted list: - # rbac-list --sort-key name --sort-key id --sort-key asc - # --sort-key desc - resources = "rbac_policies" - cmd = rbac.ListRBACPolicy(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_rbac_policies_limit(self): - # size (1000) limited list: rbac-list -P. - resources = "rbac_policies" - cmd = rbac.ListRBACPolicy(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_show_rbac_policy(self): - # rbac-show test_id. - resource = 'rbac_policy' - cmd = rbac.ShowRBACPolicy(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id']) diff --git a/neutronclient/tests/unit/test_cli20_router.py b/neutronclient/tests/unit/test_cli20_router.py deleted file mode 100644 index cd7ed0310..000000000 --- a/neutronclient/tests/unit/test_cli20_router.py +++ /dev/null @@ -1,406 +0,0 @@ -# Copyright 2012 VMware, Inc -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import sys - -from neutronclient.common import exceptions -from neutronclient.neutron.v2_0 import router -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20RouterJSON(test_cli20.CLITestV20Base): - def test_create_router(self): - # Create router: router1. - resource = 'router' - cmd = router.CreateRouter(test_cli20.MyApp(sys.stdout), None) - name = 'router1' - myid = 'myid' - args = [name, '--description', 'rooter'] - position_names = ['name', ] - position_values = [name, ] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - description='rooter') - - def test_create_router_tenant(self): - # Create router: --tenant_id tenantid myname. - resource = 'router' - cmd = router.CreateRouter(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - args = ['--tenant_id', 'tenantid', name] - position_names = ['name', ] - position_values = [name, ] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') - - def test_create_router_admin_state(self): - # Create router: --admin_state_down myname. - resource = 'router' - cmd = router.CreateRouter(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - args = ['--admin_state_down', name, ] - position_names = ['name', ] - position_values = [name, ] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - admin_state_up=False) - - def _create_router_distributed_or_ha(self, distributed=None, ha=None): - # Create router: --distributed distributed --ha ha myname. - resource = 'router' - cmd = router.CreateRouter(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - args = [] - if distributed is not None: - args += ['--distributed', str(distributed)] - if ha is not None: - args += ['--ha', str(ha)] - args.append(name) - position_names = ['name', ] - position_values = [name, ] - expected = {} - if distributed is not None: - expected['distributed'] = str(distributed) - if ha is not None: - expected['ha'] = str(ha) - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - **expected) - - def test_create_router_distributed_True(self): - # Create router: --distributed=True. - self._create_router_distributed_or_ha(distributed='True') - - def test_create_router_ha_with_True(self): - self._create_router_distributed_or_ha(ha='True') - - def test_create_router_ha_with_true(self): - self._create_router_distributed_or_ha(ha='true') - - def test_create_router_ha_with_False(self): - self._create_router_distributed_or_ha(ha='False') - - def test_create_router_ha_with_false(self): - self._create_router_distributed_or_ha(ha='false') - - def test_create_router_distributed_False(self): - # Create router: --distributed=False. - self._create_router_distributed_or_ha(distributed='False') - - def test_create_router_distributed_true(self): - # Create router: --distributed=true. - self._create_router_distributed_or_ha(distributed='true') - - def test_create_router_distributed_false(self): - # Create router: --distributed=false. - self._create_router_distributed_or_ha(distributed='false') - - def test_create_router_with_az_hint(self): - # Create router: --availability-zone-hint zone1 - # --availability-zone-hint zone2. - resource = 'router' - cmd = router.CreateRouter(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - args = ['--availability-zone-hint', 'zone1', - '--availability-zone-hint', 'zone2', name] - position_names = ['availability_zone_hints', 'name'] - position_values = [['zone1', 'zone2'], name] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_list_routers_detail(self): - # list routers: -D. - resources = "routers" - cmd = router.ListRouter(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True) - - def test_list_routers_pagination(self): - resources = "routers" - cmd = router.ListRouter(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_routers_sort(self): - # list routers: - # --sort-key name --sort-key id --sort-key asc --sort-key desc - resources = "routers" - cmd = router.ListRouter(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_routers_limit(self): - # list routers: -P. - resources = "routers" - cmd = router.ListRouter(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_update_router_exception(self): - # Update router: myid. - resource = 'router' - cmd = router.UpdateRouter(test_cli20.MyApp(sys.stdout), None) - self.assertRaises(exceptions.CommandError, self._test_update_resource, - resource, cmd, 'myid', ['myid'], {}) - - def test_update_router(self): - # Update router: myid --name myname --tags a b. - resource = 'router' - cmd = router.UpdateRouter(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'myname', - '--description', ':D'], - {'name': 'myname', 'description': ':D'}) - - def test_update_router_admin_state(self): - # Update router: myid --admin-state-up . - resource = 'router' - cmd = router.UpdateRouter(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--admin-state-up', 'True'], - {'admin_state_up': 'True'} - ) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--admin-state-up', 'true'], - {'admin_state_up': 'true'} - ) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--admin-state-up', 'False'], - {'admin_state_up': 'False'} - ) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--admin-state-up', 'false'], - {'admin_state_up': 'false'} - ) - - def test_update_router_distributed(self): - # Update router: myid --distributed . - resource = 'router' - cmd = router.UpdateRouter(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--distributed', 'True'], - {'distributed': 'True'} - ) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--distributed', 'true'], - {'distributed': 'true'} - ) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--distributed', 'False'], - {'distributed': 'False'} - ) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--distributed', 'false'], - {'distributed': 'false'} - ) - - def test_update_router_no_routes(self): - # Update router: myid --no-routes - resource = 'router' - cmd = router.UpdateRouter(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--no-routes'], - {'routes': None}) - - def test_update_router_add_route(self): - # Update router: myid --route destination=10.0.3.0/24,nexthop=10.0.0.10 - resource = 'router' - cmd = router.UpdateRouter(test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = [myid, - '--route', - 'destination=10.0.3.0/24,nexthop=10.0.0.10'] - routes = [{'destination': '10.0.3.0/24', - 'nexthop': '10.0.0.10'}] - updatefields = {'routes': routes} - self._test_update_resource(resource, cmd, myid, args, updatefields) - - def test_update_router_add_routes(self): - # Update router: myid --route destination=10.0.3.0/24,nexthop=10.0.0.10 - # --route destination=fd7a:1d63:2063::/64, - # nexthop=fd7a:1d63:2063:0:f816:3eff:fe0e:a697 - resource = 'router' - cmd = router.UpdateRouter(test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = [myid, - '--route', - 'destination=10.0.3.0/24,nexthop=10.0.0.10', - '--route', - 'destination=fd7a:1d63:2063::/64,' - 'nexthop=fd7a:1d63:2063:0:f816:3eff:fe0e:a697'] - routes = [{'destination': '10.0.3.0/24', - 'nexthop': '10.0.0.10'}, - {'destination': 'fd7a:1d63:2063::/64', - 'nexthop': 'fd7a:1d63:2063:0:f816:3eff:fe0e:a697'}] - updatefields = {'routes': routes} - self._test_update_resource(resource, cmd, myid, args, updatefields) - - def test_update_router_no_routes_with_add_route(self): - # Update router: --no-routes with --route - resource = 'router' - cmd = router.UpdateRouter(test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = [myid, - '--no-routes', - '--route', - 'destination=10.0.3.0/24,nexthop=10.0.0.10'] - exception = self.assertRaises(SystemExit, - self._test_update_resource, - resource, cmd, myid, args, None) - self.assertEqual(2, exception.code) - - def test_delete_router(self): - # Delete router: myid. - resource = 'router' - cmd = router.DeleteRouter(test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = [myid] - self._test_delete_resource(resource, cmd, myid, args) - - def test_show_router(self): - # Show router: myid. - resource = 'router' - cmd = router.ShowRouter(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, - ['id', 'name']) - - def _test_add_remove_interface(self, action, mode, cmd, args): - resource = 'router' - subcmd = '%s_router_interface' % action - if mode == 'port': - body = {'port_id': 'portid'} - else: - body = {'subnet_id': 'subnetid'} - if action == 'add': - retval = {'subnet_id': 'subnetid', 'port_id': 'portid'} - else: - retval = None - self._test_update_resource_action(resource, cmd, 'myid', - subcmd, args, - body, retval) - - def test_add_interface_compat(self): - # Add interface to router: myid subnetid. - cmd = router.AddInterfaceRouter(test_cli20.MyApp(sys.stdout), None) - args = ['myid', 'subnetid'] - self._test_add_remove_interface('add', 'subnet', cmd, args) - - def test_add_interface_by_subnet(self): - # Add interface to router: myid subnet=subnetid. - cmd = router.AddInterfaceRouter(test_cli20.MyApp(sys.stdout), None) - args = ['myid', 'subnet=subnetid'] - self._test_add_remove_interface('add', 'subnet', cmd, args) - - def test_add_interface_by_port(self): - # Add interface to router: myid port=portid. - cmd = router.AddInterfaceRouter(test_cli20.MyApp(sys.stdout), None) - args = ['myid', 'port=portid'] - self._test_add_remove_interface('add', 'port', cmd, args) - - def test_del_interface_compat(self): - # Delete interface from router: myid subnetid. - cmd = router.RemoveInterfaceRouter(test_cli20.MyApp(sys.stdout), None) - args = ['myid', 'subnetid'] - self._test_add_remove_interface('remove', 'subnet', cmd, args) - - def test_del_interface_by_subnet(self): - # Delete interface from router: myid subnet=subnetid. - cmd = router.RemoveInterfaceRouter(test_cli20.MyApp(sys.stdout), None) - args = ['myid', 'subnet=subnetid'] - self._test_add_remove_interface('remove', 'subnet', cmd, args) - - def test_del_interface_by_port(self): - # Delete interface from router: myid port=portid. - cmd = router.RemoveInterfaceRouter(test_cli20.MyApp(sys.stdout), None) - args = ['myid', 'port=portid'] - self._test_add_remove_interface('remove', 'port', cmd, args) - - def test_set_gateway(self): - # Set external gateway for router: myid externalid. - resource = 'router' - cmd = router.SetGatewayRouter(test_cli20.MyApp(sys.stdout), None) - args = ['myid', 'externalid'] - self._test_update_resource(resource, cmd, 'myid', - args, - {"external_gateway_info": - {"network_id": "externalid"}} - ) - - def test_set_gateway_disable_snat(self): - # set external gateway for router: myid externalid. - resource = 'router' - cmd = router.SetGatewayRouter(test_cli20.MyApp(sys.stdout), None) - args = ['myid', 'externalid', '--disable-snat'] - self._test_update_resource(resource, cmd, 'myid', - args, - {"external_gateway_info": - {"network_id": "externalid", - "enable_snat": False}} - ) - - def test_set_gateway_external_ip(self): - # set external gateway for router: myid externalid --fixed-ip ... - resource = 'router' - cmd = router.SetGatewayRouter(test_cli20.MyApp(sys.stdout), None) - args = ['myid', 'externalid', '--fixed-ip', 'ip_address=10.0.0.2'] - self._test_update_resource(resource, cmd, 'myid', - args, - {"external_gateway_info": - {"network_id": "externalid", - "external_fixed_ips": [ - {"ip_address": "10.0.0.2"}]}} - ) - - def test_set_gateway_external_subnet(self): - # set external gateway for router: myid externalid --fixed-ip ... - resource = 'router' - cmd = router.SetGatewayRouter(test_cli20.MyApp(sys.stdout), None) - args = ['myid', 'externalid', '--fixed-ip', 'subnet_id=mysubnet'] - self._test_update_resource(resource, cmd, 'myid', - args, - {"external_gateway_info": - {"network_id": "externalid", - "external_fixed_ips": [ - {"subnet_id": "mysubnet"}]}} - ) - - def test_set_gateway_external_ip_and_subnet(self): - # set external gateway for router: myid externalid --fixed-ip ... - resource = 'router' - cmd = router.SetGatewayRouter(test_cli20.MyApp(sys.stdout), None) - args = ['myid', 'externalid', '--fixed-ip', - 'ip_address=10.0.0.2,subnet_id=mysubnet'] - self._test_update_resource(resource, cmd, 'myid', - args, - {"external_gateway_info": - {"network_id": "externalid", - "external_fixed_ips": [ - {"subnet_id": "mysubnet", - "ip_address": "10.0.0.2"}]}} - ) - - def test_remove_gateway(self): - # Remove external gateway from router: externalid. - resource = 'router' - cmd = router.RemoveGatewayRouter(test_cli20.MyApp(sys.stdout), None) - args = ['externalid'] - self._test_update_resource(resource, cmd, 'externalid', - args, {"external_gateway_info": {}} - ) diff --git a/neutronclient/tests/unit/test_cli20_securitygroup.py b/neutronclient/tests/unit/test_cli20_securitygroup.py deleted file mode 100644 index 1af88ff79..000000000 --- a/neutronclient/tests/unit/test_cli20_securitygroup.py +++ /dev/null @@ -1,640 +0,0 @@ -#!/usr/bin/env python -# Copyright 2012 Red Hat -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import sys -import uuid - -from mox3 import mox -import six - -from neutronclient.common import exceptions -from neutronclient.common import utils -from neutronclient.neutron.v2_0 import securitygroup -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20SecurityGroupsJSON(test_cli20.CLITestV20Base): - - non_admin_status_resources = ['security_group', 'security_group_rule'] - - def test_create_security_group(self): - # Create security group: webservers. - resource = 'security_group' - cmd = securitygroup.CreateSecurityGroup( - test_cli20.MyApp(sys.stdout), None) - name = 'webservers' - myid = 'myid' - args = [name, ] - position_names = ['name'] - position_values = [name] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_security_group_tenant(self): - # Create security group: webservers. - resource = 'security_group' - cmd = securitygroup.CreateSecurityGroup( - test_cli20.MyApp(sys.stdout), None) - name = 'webservers' - description = 'my webservers' - myid = 'myid' - args = ['--tenant_id', 'tenant_id', '--description', description, name] - position_names = ['name', 'description'] - position_values = [name, description] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenant_id') - - def test_create_security_group_with_description(self): - # Create security group: webservers. - resource = 'security_group' - cmd = securitygroup.CreateSecurityGroup( - test_cli20.MyApp(sys.stdout), None) - name = 'webservers' - description = 'my webservers' - myid = 'myid' - args = [name, '--description', description] - position_names = ['name', 'description'] - position_values = [name, description] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_list_security_groups(self): - resources = "security_groups" - cmd = securitygroup.ListSecurityGroup( - test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True) - - def test_list_security_groups_pagination(self): - resources = "security_groups" - cmd = securitygroup.ListSecurityGroup( - test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_security_groups_sort(self): - resources = "security_groups" - cmd = securitygroup.ListSecurityGroup( - test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_security_groups_limit(self): - resources = "security_groups" - cmd = securitygroup.ListSecurityGroup( - test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_show_security_group_id(self): - resource = 'security_group' - cmd = securitygroup.ShowSecurityGroup( - test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id']) - - def test_show_security_group_id_name(self): - resource = 'security_group' - cmd = securitygroup.ShowSecurityGroup( - test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name']) - - def test_delete_security_group(self): - # Delete security group: myid. - resource = 'security_group' - cmd = securitygroup.DeleteSecurityGroup( - test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = [myid] - self._test_delete_resource(resource, cmd, myid, args) - - def test_update_security_group(self): - # Update security group: myid --name myname --description desc. - resource = 'security_group' - cmd = securitygroup.UpdateSecurityGroup( - test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'myname', - '--description', 'mydescription'], - {'name': 'myname', - 'description': 'mydescription'} - ) - - def test_update_security_group_with_unicode(self): - resource = 'security_group' - cmd = securitygroup.UpdateSecurityGroup( - test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', u'\u7f51\u7edc', - '--description', u'\u7f51\u7edc'], - {'name': u'\u7f51\u7edc', - 'description': u'\u7f51\u7edc'} - ) - - def test_create_security_group_rule_full(self): - # Create security group rule. - resource = 'security_group_rule' - cmd = securitygroup.CreateSecurityGroupRule( - test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - direction = 'ingress' - ethertype = 'IPv4' - protocol = 'tcp' - port_range_min = '22' - port_range_max = '22' - remote_ip_prefix = '10.0.0.0/24' - security_group_id = '1' - remote_group_id = '1' - args = ['--remote_ip_prefix', remote_ip_prefix, '--direction', - direction, '--ethertype', ethertype, '--protocol', protocol, - '--port_range_min', port_range_min, '--port_range_max', - port_range_max, '--remote_group_id', remote_group_id, - security_group_id, '--description', 'PCI policy 1421912'] - position_names = ['remote_ip_prefix', 'direction', 'ethertype', - 'protocol', 'port_range_min', 'port_range_max', - 'remote_group_id', 'security_group_id'] - position_values = [remote_ip_prefix, direction, ethertype, protocol, - port_range_min, port_range_max, remote_group_id, - security_group_id] - self._test_create_resource(resource, cmd, None, myid, args, - position_names, position_values, - description='PCI policy 1421912') - - def test_create_security_group_rule_with_integer_protocol_value(self): - resource = 'security_group_rule' - cmd = securitygroup.CreateSecurityGroupRule( - test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - direction = 'ingress' - ethertype = 'IPv4' - protocol = '2' - port_range_min = '22' - port_range_max = '22' - remote_ip_prefix = '10.0.0.0/24' - security_group_id = '1' - remote_group_id = '1' - args = ['--remote_ip_prefix', remote_ip_prefix, '--direction', - direction, '--ethertype', ethertype, '--protocol', protocol, - '--port_range_min', port_range_min, '--port_range_max', - port_range_max, '--remote_group_id', remote_group_id, - security_group_id] - position_names = ['remote_ip_prefix', 'direction', 'ethertype', - 'protocol', 'port_range_min', 'port_range_max', - 'remote_group_id', 'security_group_id'] - position_values = [remote_ip_prefix, direction, ethertype, protocol, - port_range_min, port_range_max, remote_group_id, - security_group_id] - self._test_create_resource(resource, cmd, None, myid, args, - position_names, position_values) - - def test_delete_security_group_rule(self): - # Delete security group rule: myid. - resource = 'security_group_rule' - cmd = securitygroup.DeleteSecurityGroupRule( - test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = [myid] - self._test_delete_resource(resource, cmd, myid, args) - - def test_list_security_group_rules(self): - resources = "security_group_rules" - cmd = securitygroup.ListSecurityGroupRule( - test_cli20.MyApp(sys.stdout), None) - self.mox.StubOutWithMock(securitygroup.ListSecurityGroupRule, - "extend_list") - securitygroup.ListSecurityGroupRule.extend_list(mox.IsA(list), - mox.IgnoreArg()) - self._test_list_resources(resources, cmd, True) - - def _test_extend_list(self, mox_calls, data): - resources = "security_groups" - - cmd = securitygroup.ListSecurityGroupRule( - test_cli20.MyApp(sys.stdout), None) - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - - cmd.get_client().MultipleTimes().AndReturn(self.client) - path = getattr(self.client, resources + '_path') - mox_calls(path, data) - self.mox.ReplayAll() - known_args, _vs = cmd.get_parser( - 'list' + resources).parse_known_args() - - cmd.extend_list(data, known_args) - self.mox.VerifyAll() - self.mox.UnsetStubs() - - def _build_test_data(self, data, excess=0): - # Length of a query filter on security group rule id - # in these testcases, id='secgroupid%02d' (with len(id)=12) - sec_group_id_filter_len = 12 - - response = [] - replace_rules = {'security_group_id': 'security_group', - 'remote_group_id': 'remote_group'} - - search_opts = {'fields': ['id', 'name']} - sec_group_ids = set() - for rule in data: - for key in replace_rules: - if rule.get(key): - sec_group_ids.add(rule[key]) - response.append({'id': rule[key], 'name': 'default'}) - sec_group_ids = list(sec_group_ids) - - result = [] - - sec_group_count = len(sec_group_ids) - max_size = ((sec_group_id_filter_len * sec_group_count) - excess) - chunk_size = max_size // sec_group_id_filter_len - - for i in range(0, sec_group_count, chunk_size): - search_opts['id'] = sec_group_ids[i: i + chunk_size] - params = utils.safe_encode_dict(search_opts) - resp_str = self.client.serialize({'security_groups': response}) - - result.append({ - 'filter': six.moves.urllib.parse.urlencode(params, doseq=1), - 'response': (test_cli20.MyResp(200), resp_str), - }) - - return result - - def test_extend_list(self): - def mox_calls(path, data): - responses = self._build_test_data(data) - self.client.httpclient.request( - test_cli20.MyUrlComparator(test_cli20.end_url( - path, responses[0]['filter']), self.client), - 'GET', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', test_cli20.TOKEN)).AndReturn( - responses[0]['response']) - - data = [{'name': 'default', - 'remote_group_id': 'remgroupid%02d' % i} - for i in range(10)] - data.append({'name': 'default', 'remote_group_id': None}) - self._test_extend_list(mox_calls, data) - - def test_extend_list_exceed_max_uri_len(self): - def mox_calls(path, data): - # 1 char of extra URI len will cause a split in 2 requests - self.mox.StubOutWithMock(self.client.httpclient, - '_check_uri_length') - self.client.httpclient._check_uri_length(mox.IgnoreArg()).AndRaise( - exceptions.RequestURITooLong(excess=1)) - responses = self._build_test_data(data, excess=1) - - for item in responses: - self.client.httpclient._check_uri_length( - mox.IgnoreArg()).AndReturn(None) - self.client.httpclient.request( - test_cli20.MyUrlComparator( - test_cli20.end_url(path, item['filter']), self.client), - 'GET', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', test_cli20.TOKEN)).AndReturn( - item['response']) - - data = [{'name': 'default', - 'security_group_id': 'secgroupid%02d' % i, - 'remote_group_id': 'remgroupid%02d' % i} - for i in range(10)] - data.append({'name': 'default', - 'security_group_id': 'secgroupid10', - 'remote_group_id': None}) - self._test_extend_list(mox_calls, data) - - def test_list_security_group_rules_pagination(self): - resources = "security_group_rules" - cmd = securitygroup.ListSecurityGroupRule( - test_cli20.MyApp(sys.stdout), None) - self.mox.StubOutWithMock(securitygroup.ListSecurityGroupRule, - "extend_list") - securitygroup.ListSecurityGroupRule.extend_list(mox.IsA(list), - mox.IgnoreArg()) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_security_group_rules_sort(self): - resources = "security_group_rules" - cmd = securitygroup.ListSecurityGroupRule( - test_cli20.MyApp(sys.stdout), None) - self.mox.StubOutWithMock(securitygroup.ListSecurityGroupRule, - "extend_list") - securitygroup.ListSecurityGroupRule.extend_list(mox.IsA(list), - mox.IgnoreArg()) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_security_group_rules_limit(self): - resources = "security_group_rules" - cmd = securitygroup.ListSecurityGroupRule( - test_cli20.MyApp(sys.stdout), None) - self.mox.StubOutWithMock(securitygroup.ListSecurityGroupRule, - "extend_list") - securitygroup.ListSecurityGroupRule.extend_list(mox.IsA(list), - mox.IgnoreArg()) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_show_security_group_rule(self): - resource = 'security_group_rule' - cmd = securitygroup.ShowSecurityGroupRule( - test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id']) - - def _test_list_security_group_rules_extend(self, api_data, expected, - args=(), conv=True, - query_fields=None): - def setup_list_stub(resources, data, query): - reses = {resources: data} - resstr = self.client.serialize(reses) - resp = (test_cli20.MyResp(200), resstr) - path = getattr(self.client, resources + '_path') - self.client.httpclient.request( - test_cli20.MyUrlComparator( - test_cli20.end_url(path, query), - self.client), - 'GET', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', test_cli20.TOKEN)).AndReturn(resp) - - cmd = securitygroup.ListSecurityGroupRule( - test_cli20.MyApp(sys.stdout), None) - self.mox.StubOutWithMock(cmd, 'get_client') - self.mox.StubOutWithMock(self.client.httpclient, 'request') - cmd.get_client().AndReturn(self.client) - query = '' - if query_fields: - query = '&'.join(['fields=' + f for f in query_fields]) - setup_list_stub('security_group_rules', api_data, query) - if conv: - cmd.get_client().AndReturn(self.client) - sec_ids = set() - for n in api_data: - sec_ids.add(n['security_group_id']) - if n.get('remote_group_id'): - sec_ids.add(n['remote_group_id']) - filters = '' - for id in sec_ids: - filters = filters + "&id=%s" % id - setup_list_stub('security_groups', - [{'id': 'myid1', 'name': 'group1'}, - {'id': 'myid2', 'name': 'group2'}, - {'id': 'myid3', 'name': 'group3'}], - query='fields=id&fields=name' + filters) - self.mox.ReplayAll() - - cmd_parser = cmd.get_parser('list_security_group_rules') - parsed_args = cmd_parser.parse_args(args) - result = cmd.take_action(parsed_args) - self.mox.VerifyAll() - self.mox.UnsetStubs() - # Check columns - self.assertEqual(expected['cols'], result[0]) - # Check data - _result = [x for x in result[1]] - self.assertEqual(len(expected['data']), len(_result)) - for res, exp in zip(_result, expected['data']): - self.assertEqual(len(exp), len(res)) - self.assertEqual(exp, res) - - def _test_list_security_group_rules_extend_sg_name( - self, expected_mode=None, args=(), conv=True, query_field=False): - if query_field: - field_filters = ['id', 'security_group_id', - 'remote_ip_prefix', 'remote_group_id'] - else: - field_filters = None - - data = [self._prepare_rule(rule_id='ruleid1', sg_id='myid1', - remote_group_id='myid1', - filters=field_filters), - self._prepare_rule(rule_id='ruleid2', sg_id='myid2', - remote_group_id='myid3', - filters=field_filters), - self._prepare_rule(rule_id='ruleid3', sg_id='myid2', - remote_group_id='myid2', - filters=field_filters), - ] - - if expected_mode == 'noconv': - expected = {'cols': ['id', 'security_group_id', 'remote_group_id'], - 'data': [('ruleid1', 'myid1', 'myid1'), - ('ruleid2', 'myid2', 'myid3'), - ('ruleid3', 'myid2', 'myid2')]} - elif expected_mode == 'remote_group_id': - expected = {'cols': ['id', 'security_group', 'remote_group'], - 'data': [('ruleid1', 'group1', 'group1'), - ('ruleid2', 'group2', 'group3'), - ('ruleid3', 'group2', 'group2')]} - else: - expected = {'cols': ['id', 'security_group', 'remote'], - 'data': [('ruleid1', 'group1', 'group1 (group)'), - ('ruleid2', 'group2', 'group3 (group)'), - ('ruleid3', 'group2', 'group2 (group)')]} - - self._test_list_security_group_rules_extend( - data, expected, args=args, conv=conv, query_fields=field_filters) - - def test_list_security_group_rules_extend_remote_sg_name(self): - args = '-c id -c security_group -c remote'.split() - self._test_list_security_group_rules_extend_sg_name(args=args) - - def test_list_security_group_rules_extend_sg_name_noconv(self): - args = '--no-nameconv -c id -c security_group_id -c remote_group_id' - args = args.split() - self._test_list_security_group_rules_extend_sg_name( - expected_mode='noconv', args=args, conv=False) - - def test_list_security_group_rules_extend_sg_name_with_columns(self): - args = '-c id -c security_group_id -c remote_group_id'.split() - self._test_list_security_group_rules_extend_sg_name( - expected_mode='remote_group_id', args=args) - - def test_list_security_group_rules_extend_sg_name_with_columns_no_id(self): - args = '-c id -c security_group -c remote_group'.split() - self._test_list_security_group_rules_extend_sg_name( - expected_mode='remote_group_id', args=args) - - def test_list_security_group_rules_extend_sg_name_with_fields(self): - # NOTE: remote_ip_prefix is required to show "remote" column - args = ('-F id -F security_group_id ' - '-F remote_ip_prefix -F remote_group_id').split() - self._test_list_security_group_rules_extend_sg_name( - args=args, query_field=True) - - def test_list_security_group_rules_extend_sg_name_with_fields_no_id(self): - # NOTE: remote_ip_prefix is required to show "remote" column - args = ('-F id -F security_group ' - '-F remote_ip_prefix -F remote_group').split() - self._test_list_security_group_rules_extend_sg_name(args=args, - query_field=True) - - def test_list_security_group_rules_extend_remote(self): - args = '-c id -c security_group -c remote'.split() - - data = [self._prepare_rule(rule_id='ruleid1', sg_id='myid1', - remote_ip_prefix='172.16.18.0/24'), - self._prepare_rule(rule_id='ruleid2', sg_id='myid2', - remote_ip_prefix='172.16.20.0/24'), - self._prepare_rule(rule_id='ruleid3', sg_id='myid2', - remote_group_id='myid3')] - expected = {'cols': ['id', 'security_group', 'remote'], - 'data': [('ruleid1', 'group1', '172.16.18.0/24 (CIDR)'), - ('ruleid2', 'group2', '172.16.20.0/24 (CIDR)'), - ('ruleid3', 'group2', 'group3 (group)')]} - self._test_list_security_group_rules_extend(data, expected, args) - - def test_list_security_group_rules_extend_proto_port(self): - data = [self._prepare_rule(rule_id='ruleid1', sg_id='myid1', - protocol='tcp', - port_range_min=22, port_range_max=22), - self._prepare_rule(rule_id='ruleid2', sg_id='myid2', - direction='egress', ethertype='IPv6', - protocol='udp', - port_range_min=80, port_range_max=81), - self._prepare_rule(rule_id='ruleid3', sg_id='myid2', - protocol='icmp', - remote_ip_prefix='10.2.0.0/16')] - expected = { - 'cols': ['id', 'security_group', 'direction', 'ethertype', - 'port/protocol', 'remote'], - 'data': [ - ('ruleid1', 'group1', 'ingress', 'IPv4', '22/tcp', 'any'), - ('ruleid2', 'group2', 'egress', 'IPv6', '80-81/udp', 'any'), - ('ruleid3', 'group2', 'ingress', 'IPv4', 'icmp', - '10.2.0.0/16 (CIDR)') - ]} - self._test_list_security_group_rules_extend(data, expected) - - def _prepare_rule(self, rule_id=None, sg_id=None, tenant_id=None, - direction=None, ethertype=None, - protocol=None, port_range_min=None, port_range_max=None, - remote_ip_prefix=None, remote_group_id=None, - filters=None): - rule = {'id': rule_id or str(uuid.uuid4()), - 'tenant_id': tenant_id or str(uuid.uuid4()), - 'security_group_id': sg_id or str(uuid.uuid4()), - 'direction': direction or 'ingress', - 'ethertype': ethertype or 'IPv4', - 'protocol': protocol, - 'port_range_min': port_range_min, - 'port_range_max': port_range_max, - 'remote_ip_prefix': remote_ip_prefix, - 'remote_group_id': remote_group_id} - if filters: - return dict([(k, v) for k, v in rule.items() if k in filters]) - else: - return rule - - def test__get_remote_both_unspecified(self): - sg_rule = self._prepare_rule(remote_ip_prefix=None, - remote_group_id=None) - self.assertIsNone(securitygroup._get_remote(sg_rule)) - - def test__get_remote_remote_ip_prefix_specified(self): - sg_rule = self._prepare_rule(remote_ip_prefix='172.16.18.0/24') - self.assertEqual('172.16.18.0/24 (CIDR)', - securitygroup._get_remote(sg_rule)) - - def test__get_remote_remote_group_specified(self): - sg_rule = self._prepare_rule(remote_group_id='sg_id1') - self.assertEqual('sg_id1 (group)', securitygroup._get_remote(sg_rule)) - - def test__get_protocol_port_all_none(self): - sg_rule = self._prepare_rule() - self.assertIsNone(securitygroup._get_protocol_port(sg_rule)) - - def test__get_protocol_port_tcp_all_port(self): - sg_rule = self._prepare_rule(protocol='tcp') - self.assertEqual('tcp', securitygroup._get_protocol_port(sg_rule)) - - def test__get_protocol_port_tcp_one_port(self): - sg_rule = self._prepare_rule(protocol='tcp', - port_range_min=22, port_range_max=22) - self.assertEqual('22/tcp', securitygroup._get_protocol_port(sg_rule)) - - def test__get_protocol_port_tcp_port_range(self): - sg_rule = self._prepare_rule(protocol='tcp', - port_range_min=5000, port_range_max=5010) - self.assertEqual('5000-5010/tcp', - securitygroup._get_protocol_port(sg_rule)) - - def test__get_protocol_port_udp_all_port(self): - sg_rule = self._prepare_rule(protocol='udp') - self.assertEqual('udp', securitygroup._get_protocol_port(sg_rule)) - - def test__get_protocol_port_udp_one_port(self): - sg_rule = self._prepare_rule(protocol='udp', - port_range_min=22, port_range_max=22) - self.assertEqual('22/udp', securitygroup._get_protocol_port(sg_rule)) - - def test__get_protocol_port_udp_port_range(self): - sg_rule = self._prepare_rule(protocol='udp', - port_range_min=5000, port_range_max=5010) - self.assertEqual('5000-5010/udp', - securitygroup._get_protocol_port(sg_rule)) - - def test__get_protocol_port_icmp_all(self): - sg_rule = self._prepare_rule(protocol='icmp') - self.assertEqual('icmp', securitygroup._get_protocol_port(sg_rule)) - - def test_get_ethertype_for_protocol_icmpv6(self): - self.assertEqual('IPv6', - securitygroup.generate_default_ethertype('icmpv6')) - - def test_get_ethertype_for_protocol_icmp(self): - self.assertEqual('IPv4', - securitygroup.generate_default_ethertype('icmp')) - - def test__get_protocol_port_udp_code_type(self): - sg_rule = self._prepare_rule(protocol='icmp', - port_range_min=1, port_range_max=8) - self.assertEqual('icmp (type:1, code:8)', - securitygroup._get_protocol_port(sg_rule)) - - def test__format_sg_rules(self): - rules = [self._prepare_rule(), - self._prepare_rule(protocol='tcp', port_range_min=80, - port_range_max=80), - self._prepare_rule(remote_ip_prefix='192.168.1.0/24'), - self._prepare_rule(remote_group_id='group1'), - self._prepare_rule(protocol='tcp', - remote_ip_prefix='10.1.1.0/24'), - self._prepare_rule(direction='egress'), - self._prepare_rule(direction='egress', ethertype='IPv6'), - ] - sg = {'security_group_rules': rules} - expected_data = ['ingress, IPv4', - 'ingress, IPv4, 80/tcp', - 'ingress, IPv4, remote_ip_prefix: 192.168.1.0/24', - 'ingress, IPv4, remote_group_id: group1', - 'ingress, IPv4, tcp, remote_ip_prefix: 10.1.1.0/24', - 'egress, IPv4', - 'egress, IPv6', - ] - expected = '\n'.join(sorted(expected_data)) - self.assertEqual(expected, securitygroup._format_sg_rules(sg)) diff --git a/neutronclient/tests/unit/test_cli20_servicetype.py b/neutronclient/tests/unit/test_cli20_servicetype.py deleted file mode 100644 index ec9a663fa..000000000 --- a/neutronclient/tests/unit/test_cli20_servicetype.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright 2013 Mirantis Inc. -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import sys - -from neutronclient.neutron.v2_0 import servicetype -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20ServiceProvidersJSON(test_cli20.CLITestV20Base): - id_field = "name" - - def setUp(self): - super(CLITestV20ServiceProvidersJSON, self).setUp( - plurals={'tags': 'tag'} - ) - - def test_list_service_providers(self): - resources = "service_providers" - cmd = servicetype.ListServiceProvider(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, True) - - def test_list_service_providers_pagination(self): - resources = "service_providers" - cmd = servicetype.ListServiceProvider(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_service_providers_sort(self): - resources = "service_providers" - cmd = servicetype.ListServiceProvider(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, - sort_key=["name"], - sort_dir=["asc", "desc"]) - - def test_list_service_providers_limit(self): - resources = "service_providers" - cmd = servicetype.ListServiceProvider(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, page_size=1000) diff --git a/neutronclient/tests/unit/test_cli20_subnet.py b/neutronclient/tests/unit/test_cli20_subnet.py deleted file mode 100644 index 33b5777e1..000000000 --- a/neutronclient/tests/unit/test_cli20_subnet.py +++ /dev/null @@ -1,688 +0,0 @@ -# Copyright 2012 OpenStack Foundation. -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import sys - -from mox3 import mox - -from neutronclient.common import exceptions -from neutronclient.neutron import v2_0 as neutronV20 -from neutronclient.neutron.v2_0 import subnet -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20SubnetJSON(test_cli20.CLITestV20Base): - - non_admin_status_resources = ['subnet'] - - def setUp(self): - super(CLITestV20SubnetJSON, self).setUp(plurals={'tags': 'tag'}) - - def test_create_subnet(self): - # Create subnet: --gateway gateway netid cidr. - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = '10.10.10.0/24' - gateway = 'gatewayvalue' - args = ['--gateway', gateway, netid, cidr, '--description', 'cave'] - position_names = ['ip_version', 'network_id', 'cidr', 'gateway_ip'] - position_values = [4, netid, cidr, gateway] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - description='cave') - - def test_create_subnet_network_cidr_seperated(self): - # For positional value, network_id and cidr can be separated. - # Create subnet: --gateway gateway netid cidr. - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = '10.10.10.0/24' - gateway = 'gatewayvalue' - args = [netid, '--gateway', gateway, cidr] - position_names = ['ip_version', 'network_id', 'cidr', 'gateway_ip'] - position_values = [4, netid, cidr, gateway] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_subnet_with_no_gateway(self): - # Create subnet: --no-gateway netid cidr. - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'cidrvalue' - args = ['--no-gateway', netid, cidr] - position_names = ['ip_version', 'network_id', 'cidr', 'gateway_ip'] - position_values = [4, netid, cidr, None] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_subnet_with_segment(self): - # Create subnet: --segment segment netid cidr. - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = '10.10.10.0/24' - segment = 'segment' - args = ['--segment', segment, netid, cidr, - '--description', 'cave'] - position_names = ['ip_version', 'network_id', 'cidr', 'segment_id'] - position_values = [4, netid, cidr, segment] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - description='cave') - - def test_create_subnet_with_bad_gateway_option(self): - # Create sbunet: --no-gateway netid cidr. - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'cidrvalue' - gateway = 'gatewayvalue' - args = ['--gateway', gateway, '--no-gateway', netid, cidr] - position_names = ['ip_version', 'network_id', 'cidr', 'gateway_ip'] - position_values = [4, netid, cidr, None] - self.assertRaises( - SystemExit, self._test_create_resource, - resource, cmd, name, myid, args, position_names, position_values) - - def _test_create_resource_and_catch_command_error(self, should_fail, - *args): - if should_fail: - params = {'no_api_call': True, - 'expected_exception': exceptions.CommandError} - else: - params = {} - self._test_create_resource(*args, **params) - - def test_create_subnet_with_enable_and_disable_dhcp(self): - # Create subnet: --enable-dhcp and --disable-dhcp. - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'cidrvalue' - position_names = ['ip_version', 'network_id', 'cidr', 'enable_dhcp'] - # enable_dhcp value is appended later inside the loop - position_values = [4, netid, cidr] - for enable_dhcp_arg, should_fail in ( - ('--enable-dhcp=False', False), - ('--enable-dhcp=True', True), - ('--enable-dhcp', True) - ): - tested_args = [enable_dhcp_arg, '--disable-dhcp'] - args = tested_args + [netid, cidr] - pos_values = position_values + [should_fail] - self._test_create_resource_and_catch_command_error( - should_fail, - resource, cmd, name, myid, args, position_names, pos_values) - - def test_create_subnet_with_multiple_enable_dhcp(self): - # Create subnet with multiple --enable-dhcp arguments passed. - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'cidrvalue' - position_names = ['ip_version', 'network_id', 'cidr', 'enable_dhcp'] - # enable_dhcp value is appended later inside the loop - position_values = [4, netid, cidr] - - _ = 'UNUSED_MARKER' - for tested_args, should_fail, pos_value in ( - (['--enable-dhcp', '--enable-dhcp=True'], False, True), - (['--enable-dhcp', '--enable-dhcp=False'], True, _), - (['--enable-dhcp=False', '--enable-dhcp'], True, _), - (['--enable-dhcp=True', '--enable-dhcp=False'], True, _), - (['--enable-dhcp=False', '--enable-dhcp=True'], True, _) - ): - args = tested_args + [netid, cidr] - pos_values = position_values + [pos_value] - self._test_create_resource_and_catch_command_error( - should_fail, - resource, cmd, name, myid, args, position_names, pos_values) - - def test_create_subnet_tenant(self): - # Create subnet: --tenant_id tenantid netid cidr. - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'prefixvalue' - args = ['--tenant_id', 'tenantid', netid, cidr] - position_names = ['ip_version', 'network_id', 'cidr'] - position_values = [4, netid, cidr] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') - - def test_create_subnet_tags(self): - # Create subnet: netid cidr --tags a b. - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'prefixvalue' - args = [netid, cidr, '--tags', 'a', 'b'] - position_names = ['ip_version', 'network_id', 'cidr'] - position_values = [4, netid, cidr] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tags=['a', 'b']) - - def test_create_subnet_allocation_pool(self): - # Create subnet: --tenant_id tenantid netid cidr. - # The is --allocation_pool start=1.1.1.10,end=1.1.1.20 - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'prefixvalue' - args = ['--tenant_id', 'tenantid', - '--allocation_pool', 'start=1.1.1.10,end=1.1.1.20', - netid, cidr] - position_names = ['ip_version', 'allocation_pools', 'network_id', - 'cidr'] - pool = [{'start': '1.1.1.10', 'end': '1.1.1.20'}] - position_values = [4, pool, netid, cidr] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') - - def test_create_subnet_allocation_pools(self): - # Create subnet: --tenant-id tenantid netid cidr. - # The are --allocation_pool start=1.1.1.10,end=1.1.1.20 and - # --allocation_pool start=1.1.1.30,end=1.1.1.40 - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'prefixvalue' - args = ['--tenant_id', 'tenantid', - '--allocation_pool', 'start=1.1.1.10,end=1.1.1.20', - '--allocation_pool', 'start=1.1.1.30,end=1.1.1.40', - netid, cidr] - position_names = ['ip_version', 'allocation_pools', 'network_id', - 'cidr'] - pools = [{'start': '1.1.1.10', 'end': '1.1.1.20'}, - {'start': '1.1.1.30', 'end': '1.1.1.40'}] - position_values = [4, pools, netid, cidr] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') - - def test_create_subnet_host_route(self): - # Create subnet: --tenant_id tenantid netid cidr. - # The is - # --host-route destination=172.16.1.0/24,nexthop=1.1.1.20 - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'prefixvalue' - args = ['--tenant_id', 'tenantid', - '--host-route', 'destination=172.16.1.0/24,nexthop=1.1.1.20', - netid, cidr] - position_names = ['ip_version', 'host_routes', 'network_id', - 'cidr'] - route = [{'destination': '172.16.1.0/24', 'nexthop': '1.1.1.20'}] - position_values = [4, route, netid, cidr] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') - - def test_create_subnet_host_routes(self): - # Create subnet: --tenant-id tenantid netid cidr. - # The are - # --host-route destination=172.16.1.0/24,nexthop=1.1.1.20 and - # --host-route destination=172.17.7.0/24,nexthop=1.1.1.40 - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'prefixvalue' - args = ['--tenant_id', 'tenantid', - '--host-route', 'destination=172.16.1.0/24,nexthop=1.1.1.20', - '--host-route', 'destination=172.17.7.0/24,nexthop=1.1.1.40', - netid, cidr] - position_names = ['ip_version', 'host_routes', 'network_id', - 'cidr'] - routes = [{'destination': '172.16.1.0/24', 'nexthop': '1.1.1.20'}, - {'destination': '172.17.7.0/24', 'nexthop': '1.1.1.40'}] - position_values = [4, routes, netid, cidr] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') - - def test_create_subnet_dns_nameservers(self): - # Create subnet: --tenant-id tenantid netid cidr. - # The are - # --dns-nameserver 1.1.1.20 and --dns-nameserver 1.1.1.40 - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'prefixvalue' - args = ['--tenant_id', 'tenantid', - '--dns-nameserver', '1.1.1.20', - '--dns-nameserver', '1.1.1.40', - netid, cidr] - position_names = ['ip_version', 'dns_nameservers', 'network_id', - 'cidr'] - nameservers = ['1.1.1.20', '1.1.1.40'] - position_values = [4, nameservers, netid, cidr] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') - - def test_create_subnet_with_use_default_subnetpool(self): - # Create subnet: --tenant-id tenantid --use-default-subnetpool \ - # netid cidr. - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'prefixvalue' - args = ['--tenant_id', 'tenantid', - '--use-default-subnetpool', - netid, cidr] - position_names = ['ip_version', 'use_default_subnetpool', 'network_id', - 'cidr'] - position_values = [4, True, netid, cidr] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') - - def test_create_subnet_with_disable_dhcp(self): - # Create subnet: --tenant-id tenantid --disable-dhcp netid cidr. - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'prefixvalue' - args = ['--tenant_id', 'tenantid', - '--disable-dhcp', - netid, cidr] - position_names = ['ip_version', 'enable_dhcp', 'network_id', - 'cidr'] - position_values = [4, False, netid, cidr] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') - - def test_create_subnet_merge_single_plurar(self): - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'prefixvalue' - args = ['--tenant_id', 'tenantid', - '--allocation-pool', 'start=1.1.1.10,end=1.1.1.20', - netid, cidr, - '--allocation-pools', 'list=true', 'type=dict', - 'start=1.1.1.30,end=1.1.1.40'] - position_names = ['ip_version', 'allocation_pools', 'network_id', - 'cidr'] - pools = [{'start': '1.1.1.10', 'end': '1.1.1.20'}, - {'start': '1.1.1.30', 'end': '1.1.1.40'}] - position_values = [4, pools, netid, cidr] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') - - def test_create_subnet_merge_plurar(self): - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'prefixvalue' - args = ['--tenant_id', 'tenantid', - netid, cidr, - '--allocation-pools', 'list=true', 'type=dict', - 'start=1.1.1.30,end=1.1.1.40'] - position_names = ['ip_version', 'allocation_pools', 'network_id', - 'cidr'] - pools = [{'start': '1.1.1.30', 'end': '1.1.1.40'}] - position_values = [4, pools, netid, cidr] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') - - def test_create_subnet_merge_single_single(self): - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'prefixvalue' - args = ['--tenant_id', 'tenantid', - '--allocation-pool', 'start=1.1.1.10,end=1.1.1.20', - netid, cidr, - '--allocation-pool', - 'start=1.1.1.30,end=1.1.1.40'] - position_names = ['ip_version', 'allocation_pools', 'network_id', - 'cidr'] - pools = [{'start': '1.1.1.10', 'end': '1.1.1.20'}, - {'start': '1.1.1.30', 'end': '1.1.1.40'}] - position_values = [4, pools, netid, cidr] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') - - def test_create_subnet_max_v4_cidr(self): - # Create subnet: --gateway gateway netid cidr. - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = '192.168.0.1/32' - gateway = 'gatewayvalue' - args = ['--gateway', gateway, netid, cidr] - position_names = ['ip_version', 'network_id', 'cidr', 'gateway_ip'] - position_values = [4, netid, cidr, gateway] - self.mox.StubOutWithMock(cmd.log, 'warning') - cmd.log.warning(mox.IgnoreArg(), {'ip': 4, 'cidr': '/32'}) - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - self.mox.VerifyAll() - self.mox.UnsetStubs() - - def test_create_subnet_with_ipv6_ra_mode(self): - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'prefixvalue' - args = ['--tenant_id', 'tenantid', - '--ip-version', '6', - '--ipv6-ra-mode', 'dhcpv6-stateful', - netid, cidr] - position_names = ['ip_version', 'ipv6_ra_mode', - 'network_id', 'cidr'] - position_values = [6, 'dhcpv6-stateful', netid, cidr] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') - - def test_create_subnet_with_ipv6_address_mode(self): - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'prefixvalue' - args = ['--tenant_id', 'tenantid', - '--ip-version', '6', - '--ipv6-address-mode', 'dhcpv6-stateful', - netid, cidr] - position_names = ['ip_version', 'ipv6_address_mode', - 'network_id', 'cidr'] - position_values = [6, 'dhcpv6-stateful', netid, cidr] - - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') - - def test_create_subnet_with_ipv6_modes(self): - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'prefixvalue' - args = ['--tenant_id', 'tenantid', - '--ip-version', '6', - '--ipv6-address-mode', 'slaac', - '--ipv6-ra-mode', 'slaac', - netid, cidr] - position_names = ['ip_version', 'ipv6_address_mode', - 'ipv6_ra_mode', 'network_id', 'cidr'] - position_values = [6, 'slaac', 'slaac', netid, cidr] - - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') - - def test_create_subnet_with_ipv6_ra_mode_ipv4(self): - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'prefixvalue' - args = ['--tenant_id', 'tenantid', - '--ip-version', '4', - '--ipv6-ra-mode', 'slaac', - netid, cidr] - position_names = ['ip_version', 'ipv6_ra_mode', - 'network_id', 'cidr'] - position_values = [4, None, netid, cidr] - self._test_create_resource( - resource, cmd, name, myid, args, position_names, - position_values, tenant_id='tenantid', - no_api_call=True, expected_exception=exceptions.CommandError) - - def test_create_subnet_with_ipv6_address_mode_ipv4(self): - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'prefixvalue' - args = ['--tenant_id', 'tenantid', - '--ip-version', '4', - '--ipv6-address-mode', 'slaac', - netid, cidr] - position_names = ['ip_version', 'ipv6_address_mode', - 'network_id', 'cidr'] - position_values = [4, None, netid, cidr] - self._test_create_resource( - resource, cmd, name, myid, args, position_names, - position_values, tenant_id='tenantid', - no_api_call=True, expected_exception=exceptions.CommandError) - - def test_create_subnet_with_subnetpool_ipv6_and_ip_ver_ignored(self): - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - args = ['--tenant_id', 'tenantid', - '--ip-version', '4', - '--subnetpool', 'subnetpool_id', - netid] - position_names = ['ip_version', 'network_id', 'subnetpool_id'] - position_values = [6, netid, 'subnetpool_id'] - self.mox.StubOutWithMock(neutronV20, 'find_resource_by_name_or_id') - neutronV20.find_resource_by_name_or_id( - self.client, - 'subnetpool', - 'subnetpool_id').AndReturn({'id': 'subnetpool_id', - 'ip_version': 6}) - self._test_create_resource( - resource, cmd, name, myid, args, position_names, - position_values, tenant_id='tenantid') - - def test_create_subnet_with_subnetpool_ipv4_with_cidr_wildcard(self): - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - cidr = 'cidrwildcard' - args = ['--tenant_id', 'tenantid', - '--ip-version', '4', - '--ipv6-address-mode', 'slaac', - '--subnetpool', 'subnetpool_id', - netid, cidr] - position_names = ['ip_version', 'ipv6_address_mode', - 'network_id', 'subnetpool_id', 'cidr'] - position_values = [4, None, netid, 'subnetpool_id', cidr] - self.mox.StubOutWithMock(neutronV20, 'find_resource_by_name_or_id') - neutronV20.find_resource_by_name_or_id( - self.client, - 'subnetpool', - 'subnetpool_id').AndReturn({'id': 'subnetpool_id', - 'ip_version': 4}) - self._test_create_resource( - resource, cmd, name, myid, args, position_names, - position_values, tenant_id='tenantid', - no_api_call=True, expected_exception=exceptions.CommandError) - - def test_create_subnet_with_subnetpool_ipv4_with_prefixlen(self): - resource = 'subnet' - cmd = subnet.CreateSubnet(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - netid = 'netid' - args = ['--tenant_id', 'tenantid', - '--ip-version', '4', - '--ipv6-address-mode', 'slaac', - '--subnetpool', 'subnetpool_id', - '--prefixlen', '31', - netid] - position_names = ['ip_version', 'ipv6_address_mode', - 'network_id', 'subnetpool_id'] - position_values = [4, None, netid, 'subnetpool_id'] - self.mox.StubOutWithMock(neutronV20, 'find_resource_by_name_or_id') - neutronV20.find_resource_by_name_or_id( - self.client, - 'subnetpool', - 'subnetpool_id').AndReturn({'id': 'subnetpool_id', - 'ip_version': 4}) - self._test_create_resource( - resource, cmd, name, myid, args, position_names, - position_values, tenant_id='tenantid', - no_api_call=True, expected_exception=exceptions.CommandError) - - def test_list_subnets_detail(self): - # List subnets: -D. - resources = "subnets" - cmd = subnet.ListSubnet(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True) - - def test_list_subnets_tags(self): - # List subnets: -- --tags a b. - resources = "subnets" - cmd = subnet.ListSubnet(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, tags=['a', 'b']) - - def test_list_subnets_detail_tags(self): - # List subnets: -D -- --tags a b. - resources = "subnets" - cmd = subnet.ListSubnet(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, detail=True, tags=['a', 'b']) - - def test_list_subnets_fields(self): - # List subnets: --fields a --fields b -- --fields c d. - resources = "subnets" - cmd = subnet.ListSubnet(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - fields_1=['a', 'b'], fields_2=['c', 'd']) - - def test_list_subnets_pagination(self): - resources = "subnets" - cmd = subnet.ListSubnet(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_subnets_sort(self): - # List subnets: --sort-key name --sort-key id --sort-key asc - # --sort-key desc - resources = "subnets" - cmd = subnet.ListSubnet(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_subnets_limit(self): - # List subnets: -P. - resources = "subnets" - cmd = subnet.ListSubnet(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_update_subnet(self): - # Update subnet: myid --name myname --tags a b. - resource = 'subnet' - cmd = subnet.UpdateSubnet(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'myname', - '--description', 'cavern', - '--tags', 'a', 'b'], - {'name': 'myname', 'tags': ['a', 'b'], - 'description': 'cavern'}) - - def test_update_subnet_allocation_pools(self): - # Update subnet: myid --name myname --tags a b. - resource = 'subnet' - cmd = subnet.UpdateSubnet(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--allocation-pool', - 'start=1.2.0.2,end=1.2.0.127'], - {'allocation_pools': [{'start': '1.2.0.2', - 'end': '1.2.0.127'}]} - ) - - def test_update_subnet_enable_disable_dhcp(self): - # Update sbunet: --enable-dhcp and --disable-dhcp. - resource = 'subnet' - cmd = subnet.UpdateSubnet(test_cli20.MyApp(sys.stdout), None) - self.assertRaises(exceptions.CommandError, - self._test_update_resource, - resource, cmd, 'myid', - ['myid', '--name', 'myname', '--enable-dhcp', - '--disable-dhcp'], {'name': 'myname', }) - - def test_show_subnet(self): - # Show subnet: --fields id --fields name myid. - resource = 'subnet' - cmd = subnet.ShowSubnet(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name']) - - def test_delete_subnet(self): - # Delete subnet: subnetid. - resource = 'subnet' - cmd = subnet.DeleteSubnet(test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = [myid] - self._test_delete_resource(resource, cmd, myid, args) diff --git a/neutronclient/tests/unit/test_cli20_subnetpool.py b/neutronclient/tests/unit/test_cli20_subnetpool.py deleted file mode 100644 index 63d04f411..000000000 --- a/neutronclient/tests/unit/test_cli20_subnetpool.py +++ /dev/null @@ -1,206 +0,0 @@ -# Copyright 2015 OpenStack Foundation. -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import sys - -from mox3 import mox - -from neutronclient.common import exceptions -from neutronclient.neutron.v2_0 import subnetpool -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20SubnetPoolJSON(test_cli20.CLITestV20Base): - - non_admin_status_resources = ['subnetpool'] - - def setUp(self): - super(CLITestV20SubnetPoolJSON, self).setUp(plurals={'tags': 'tag'}) - - def test_create_subnetpool_shared(self): - # Create subnetpool: myname. - resource = 'subnetpool' - cmd = subnetpool.CreateSubnetPool(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - min_prefixlen = 30 - prefix1 = '10.11.12.0/24' - prefix2 = '12.11.13.0/24' - args = [name, '--min-prefixlen', str(min_prefixlen), - '--pool-prefix', prefix1, '--pool-prefix', prefix2, - '--shared', '--description', 'public pool'] - position_names = ['name', 'min_prefixlen', 'prefixes', 'shared'] - position_values = [name, min_prefixlen, [prefix1, prefix2], True] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - description='public pool') - - def test_create_subnetpool_not_shared(self): - # Create subnetpool: myname. - resource = 'subnetpool' - cmd = subnetpool.CreateSubnetPool(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - min_prefixlen = 30 - prefix1 = '10.11.12.0/24' - prefix2 = '12.11.13.0/24' - args = [name, '--min-prefixlen', str(min_prefixlen), - '--pool-prefix', prefix1, '--pool-prefix', prefix2] - position_names = ['name', 'min_prefixlen', 'prefixes'] - position_values = [name, min_prefixlen, [prefix1, prefix2]] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_subnetpool(self, default='false'): - # Create subnetpool: myname. - resource = 'subnetpool' - cmd = subnetpool.CreateSubnetPool(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - min_prefixlen = 30 - prefix1 = '10.11.12.0/24' - prefix2 = '12.11.13.0/24' - args = [name, '--min-prefixlen', str(min_prefixlen), - '--pool-prefix', prefix1, '--pool-prefix', prefix2, - '--is-default', default] - position_names = ['name', 'min_prefixlen', 'prefixes', 'is_default'] - position_values = [name, min_prefixlen, [prefix1, prefix2], default] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_subnetpool_default(self): - self.test_create_subnetpool(default='true') - - def test_create_subnetpool_with_unicode(self): - # Create subnetpool: u'\u7f51\u7edc'. - resource = 'subnetpool' - cmd = subnetpool.CreateSubnetPool(test_cli20.MyApp(sys.stdout), None) - name = u'\u7f51\u7edc' - myid = 'myid' - min_prefixlen = 30 - prefixes = '10.11.12.0/24' - args = [name, '--min-prefixlen', str(min_prefixlen), - '--pool-prefix', prefixes] - position_names = ['name', 'min_prefixlen', 'prefixes'] - position_values = [name, min_prefixlen, [prefixes]] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_subnetpool_with_addrscope(self): - # Create subnetpool: myname in addrscope: foo-address-scope - resource = 'subnetpool' - cmd = subnetpool.CreateSubnetPool(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - min_prefixlen = 30 - prefix1 = '11.11.11.0/24' - prefix2 = '12.12.12.0/24' - address_scope = 'foo-address-scope' - args = [name, '--min-prefixlen', str(min_prefixlen), - '--pool-prefix', prefix1, '--pool-prefix', prefix2, - '--address-scope', address_scope] - position_names = ['name', 'min_prefixlen', 'prefixes', - 'address_scope_id'] - position_values = [name, min_prefixlen, [prefix1, prefix2], - address_scope] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_create_subnetpool_no_poolprefix(self): - # Should raise an error because --pool-prefix is required - resource = 'subnetpool' - cmd = subnetpool.CreateSubnetPool(test_cli20.MyApp(sys.stdout), None) - name = 'myname' - myid = 'myid' - args = [name] - position_names = ['name'] - position_values = [name] - self.assertRaises(SystemExit, self._test_create_resource, resource, - cmd, name, myid, args, position_names, - position_values) - - def test_list_subnetpool_pagination(self): - cmd = subnetpool.ListSubnetPool(test_cli20.MyApp(sys.stdout), None) - self.mox.StubOutWithMock(subnetpool.ListSubnetPool, "extend_list") - subnetpool.ListSubnetPool.extend_list(mox.IsA(list), mox.IgnoreArg()) - self._test_list_resources_with_pagination("subnetpools", cmd) - self.mox.VerifyAll() - self.mox.UnsetStubs() - - def test_list_subnetpools_sort(self): - # List subnetpools: - # --sort-key name --sort-key id --sort-key asc --sort-key desc - resources = "subnetpools" - cmd = subnetpool.ListSubnetPool(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_subnetpools_limit(self): - # List subnetpools: -P. - resources = "subnetpools" - cmd = subnetpool.ListSubnetPool(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_update_subnetpool_exception(self): - # Update subnetpool: myid. - resource = 'subnetpool' - cmd = subnetpool.UpdateSubnetPool(test_cli20.MyApp(sys.stdout), None) - self.assertRaises(exceptions.CommandError, self._test_update_resource, - resource, cmd, 'myid', ['myid'], {}) - - def test_update_subnetpool(self): - # Update subnetpool: myid --name myname. - resource = 'subnetpool' - cmd = subnetpool.UpdateSubnetPool(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'myname', - '--description', ':)'], - {'name': 'myname', 'description': ':)'}) - - def test_update_subnetpool_with_address_scope(self): - # Update subnetpool: myid --address-scope newscope. - resource = 'subnetpool' - cmd = subnetpool.UpdateSubnetPool(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--address-scope', 'newscope'], - {'address_scope_id': 'newscope'} - ) - - def test_update_subnetpool_with_no_address_scope(self): - # Update subnetpool: myid --no-address-scope. - resource = 'subnetpool' - cmd = subnetpool.UpdateSubnetPool(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--no-address-scope'], - {'address_scope_id': None} - ) - - def test_show_subnetpool(self): - # Show subnetpool: --fields id --fields name myid. - resource = 'subnetpool' - cmd = subnetpool.ShowSubnetPool(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, - ['id', 'name']) - - def test_delete_subnetpool(self): - # Delete subnetpool: subnetpoolid. - resource = 'subnetpool' - cmd = subnetpool.DeleteSubnetPool(test_cli20.MyApp(sys.stdout), None) - myid = 'myid' - args = [myid] - self._test_delete_resource(resource, cmd, myid, args) diff --git a/neutronclient/tests/unit/test_cli20_tag.py b/neutronclient/tests/unit/test_cli20_tag.py deleted file mode 100644 index 09b99ab0a..000000000 --- a/neutronclient/tests/unit/test_cli20_tag.py +++ /dev/null @@ -1,125 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import sys - -from mox3 import mox - -from neutronclient.common import exceptions -from neutronclient.neutron.v2_0 import network -from neutronclient.neutron.v2_0 import tag -from neutronclient import shell -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20Tag(test_cli20.CLITestV20Base): - def _test_tag_operation(self, cmd, path, method, args, prog_name, - body=None): - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) - if body: - body = test_cli20.MyComparator(body, self.client) - self.client.httpclient.request( - test_cli20.MyUrlComparator( - test_cli20.end_url(path, format=self.format), self.client), - method, body=body, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', test_cli20.TOKEN)).AndReturn( - (test_cli20.MyResp(204), None)) - self.mox.ReplayAll() - cmd_parser = cmd.get_parser(prog_name) - shell.run_command(cmd, cmd_parser, args) - self.mox.VerifyAll() - self.mox.UnsetStubs() - - def _test_tags_query(self, cmd, resources, args, query): - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) - path = getattr(self.client, resources + "_path") - res = {resources: [{'id': 'myid'}]} - resstr = self.client.serialize(res) - self.client.httpclient.request( - test_cli20.MyUrlComparator( - test_cli20.end_url(path, query, format=self.format), - self.client), - 'GET', body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', test_cli20.TOKEN)).AndReturn( - (test_cli20.MyResp(200), resstr)) - self.mox.ReplayAll() - cmd_parser = cmd.get_parser("list_networks") - shell.run_command(cmd, cmd_parser, args) - self.mox.VerifyAll() - self.mox.UnsetStubs() - _str = self.fake_stdout.make_string() - self.assertIn('myid', _str) - - def _make_tag_path(self, resource, resource_id, tag): - path = getattr(self.client, "tag_path") - resource_plural = self.client.get_resource_plural(resource) - return path % (resource_plural, resource_id, tag) - - def _make_tags_path(self, resource, resource_id): - path = getattr(self.client, "tags_path") - resource_plural = self.client.get_resource_plural(resource) - return path % (resource_plural, resource_id) - - def test_add_tag(self): - cmd = tag.AddTag(test_cli20.MyApp(sys.stdout), None) - path = self._make_tag_path('network', 'myid', 'red') - args = ['--resource-type', 'network', '--resource', 'myid', - '--tag', 'red'] - self._test_tag_operation(cmd, path, 'PUT', args, "tag-add") - - def test_replace_tag(self): - cmd = tag.ReplaceTag(test_cli20.MyApp(sys.stdout), None) - path = self._make_tags_path('network', 'myid') - args = ['--resource-type', 'network', '--resource', 'myid', - '--tag', 'red', '--tag', 'blue'] - body = {'tags': ['red', 'blue']} - self._test_tag_operation(cmd, path, 'PUT', args, "tag-replace", - body=body) - - def test_remove_tag(self): - cmd = tag.RemoveTag(test_cli20.MyApp(sys.stdout), None) - path = self._make_tag_path('network', 'myid', 'red') - args = ['--resource-type', 'network', '--resource', 'myid', - '--tag', 'red'] - self._test_tag_operation(cmd, path, 'DELETE', args, "tag-remove") - - def test_remove_tag_all(self): - cmd = tag.RemoveTag(test_cli20.MyApp(sys.stdout), None) - path = self._make_tags_path('network', 'myid') - args = ['--resource-type', 'network', '--resource', 'myid', - '--all'] - self._test_tag_operation(cmd, path, 'DELETE', args, "tag-remove") - - def test_no_tag_nor_all(self): - cmd = tag.RemoveTag(test_cli20.MyApp(sys.stdout), None) - path = self._make_tags_path('network', 'myid') - args = ['--resource-type', 'network', '--resource', 'myid'] - self.assertRaises(exceptions.CommandError, self._test_tag_operation, - cmd, path, 'DELETE', args, "tag-remove") - - def test_tags_query(self): - # This test examines that '-' in the tag related filters - # is not converted to '_'. - resources = 'networks' - cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) - self.mox.StubOutWithMock(network.ListNetwork, "extend_list") - network.ListNetwork.extend_list(mox.IsA(list), mox.IgnoreArg()) - args = ['--not-tags', 'red,blue', '--tags-any', 'green', - '--not-tags-any', 'black'] - query = "not-tags=red,blue&tags-any=green¬-tags-any=black" - self._test_tags_query(cmd, resources, args, query) diff --git a/neutronclient/tests/unit/test_client_extension.py b/neutronclient/tests/unit/test_client_extension.py deleted file mode 100644 index 973f80068..000000000 --- a/neutronclient/tests/unit/test_client_extension.py +++ /dev/null @@ -1,214 +0,0 @@ -# Copyright 2015 Rackspace Hosting Inc. -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import inspect -import sys - -import mock - -from neutronclient.common import extension -from neutronclient.neutron.v2_0.contrib import _fox_sockets as fox_sockets -from neutronclient import shell -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20ExtensionJSON(test_cli20.CLITestV20Base): - - non_admin_status_resources = ['fox_socket'] - - def setUp(self): - # need to mock before super because extensions loaded on instantiation - self._mock_extension_loading() - super(CLITestV20ExtensionJSON, self).setUp(plurals={'tags': 'tag'}) - - def _mock_extension_loading(self): - ext_pkg = 'neutronclient.common.extension' - contrib = mock.patch(ext_pkg + '._discover_via_entry_points').start() - contrib.return_value = [("_fox_sockets", fox_sockets)] - return contrib - - def test_ext_cmd_loaded(self): - shell.NeutronShell('2.0') - ext_cmd = {'fox-sockets-list': fox_sockets.FoxInSocketsList, - 'fox-sockets-create': fox_sockets.FoxInSocketsCreate, - 'fox-sockets-update': fox_sockets.FoxInSocketsUpdate, - 'fox-sockets-delete': fox_sockets.FoxInSocketsDelete, - 'fox-sockets-show': fox_sockets.FoxInSocketsShow} - self.assertDictContainsSubset(ext_cmd, shell.COMMANDS['2.0']) - - def test_ext_cmd_help_doc_with_extension_name(self): - shell.NeutronShell('2.0') - ext_cmd = {'fox-sockets-list': fox_sockets.FoxInSocketsList, - 'fox-sockets-create': fox_sockets.FoxInSocketsCreate, - 'fox-sockets-update': fox_sockets.FoxInSocketsUpdate, - 'fox-sockets-delete': fox_sockets.FoxInSocketsDelete, - 'fox-sockets-show': fox_sockets.FoxInSocketsShow} - self.assertDictContainsSubset(ext_cmd, shell.COMMANDS['2.0']) - for item in ext_cmd: - cmdcls = shell.COMMANDS['2.0'].get(item) - self.assertTrue(cmdcls.__doc__.startswith("[_fox_sockets]")) - - def test_delete_fox_socket(self): - # Delete fox socket: myid. - resource = 'fox_socket' - cmd = fox_sockets.FoxInSocketsDelete(test_cli20.MyApp(sys.stdout), - None) - myid = 'myid' - args = [myid] - self._test_delete_resource(resource, cmd, myid, args) - - def test_update_fox_socket(self): - # Update fox_socket: myid --name myname. - resource = 'fox_socket' - cmd = fox_sockets.FoxInSocketsUpdate(test_cli20.MyApp(sys.stdout), - None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'myname'], - {'name': 'myname'}) - - def test_create_fox_socket(self): - # Create fox_socket: myname. - resource = 'fox_socket' - cmd = fox_sockets.FoxInSocketsCreate(test_cli20.MyApp(sys.stdout), - None) - name = 'myname' - myid = 'myid' - args = [name, ] - position_names = ['name', ] - position_values = [name, ] - self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) - - def test_list_fox_sockets(self): - # List fox_sockets. - resources = 'fox_sockets' - cmd = fox_sockets.FoxInSocketsList(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True) - - def test_list_fox_pagination(self): - resources = 'fox_sockets' - cmd = fox_sockets.FoxInSocketsList(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_show_fox_socket(self): - # Show fox_socket: --fields id --fields name myid. - resource = 'fox_socket' - cmd = fox_sockets.FoxInSocketsShow(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name']) - - -class CLITestV20ExtensionJSONAlternatePlurals(test_cli20.CLITestV20Base): - class IPAddress(extension.NeutronClientExtension): - resource = 'ip_address' - resource_plural = '%ses' % resource - object_path = '/%s' % resource_plural - resource_path = '/%s/%%s' % resource_plural - versions = ['2.0'] - - class IPAddressesList(extension.ClientExtensionList, IPAddress): - shell_command = 'ip-address-list' - - def setUp(self): - # need to mock before super because extensions loaded on instantiation - self._mock_extension_loading() - super(CLITestV20ExtensionJSONAlternatePlurals, self).setUp() - - def _mock_extension_loading(self): - ext_pkg = 'neutronclient.common.extension' - contrib = mock.patch(ext_pkg + '._discover_via_entry_points').start() - ip_address = mock.MagicMock() - ip_address.IPAddress = self.IPAddress - ip_address.IPAddressesList = self.IPAddressesList - contrib.return_value = [("ip_address", ip_address)] - return contrib - - def test_ext_cmd_loaded(self): - shell.NeutronShell('2.0') - ext_cmd = {'ip-address-list': self.IPAddressesList} - self.assertDictContainsSubset(ext_cmd, shell.COMMANDS['2.0']) - - def test_list_ip_addresses(self): - # List ip_addresses. - resources = 'ip_addresses' - cmd = self.IPAddressesList(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True) - - -class CLITestV20ExtensionJSONChildResource(test_cli20.CLITestV20Base): - class Child(extension.NeutronClientExtension): - parent_resource = 'parents' - child_resource = 'child' - resource = '%s_%s' % (parent_resource, child_resource) - resource_plural = '%sren' % resource - child_resource_plural = '%ren' % child_resource - object_path = '/%s/%%s/%s' % (parent_resource, child_resource_plural) - resource_path = '/%s/%%s/%s/%%s' % (parent_resource, - child_resource_plural) - versions = ['2.0'] - - class ChildrenList(extension.ClientExtensionList, Child): - shell_command = 'parent-child-list' - - class ChildShow(extension.ClientExtensionShow, Child): - shell_command = 'parent-child-show' - - class ChildUpdate(extension.ClientExtensionUpdate, Child): - shell_command = 'parent-child-update' - - class ChildDelete(extension.ClientExtensionDelete, Child): - shell_command = 'parent-child-delete' - - class ChildCreate(extension.ClientExtensionCreate, Child): - shell_command = 'parent-child-create' - - def setUp(self): - # need to mock before super because extensions loaded on instantiation - self._mock_extension_loading() - super(CLITestV20ExtensionJSONChildResource, self).setUp() - - def _mock_extension_loading(self): - ext_pkg = 'neutronclient.common.extension' - contrib = mock.patch(ext_pkg + '._discover_via_entry_points').start() - child = mock.MagicMock() - child.Child = self.Child - child.ChildrenList = self.ChildrenList - child.ChildShow = self.ChildShow - child.ChildUpdate = self.ChildUpdate - child.ChildDelete = self.ChildDelete - child.ChildCreate = self.ChildCreate - contrib.return_value = [("child", child)] - return contrib - - def test_ext_cmd_loaded(self): - shell.NeutronShell('2.0') - ext_cmd = {'parent-child-list': self.ChildrenList, - 'parent-child-show': self.ChildShow, - 'parent-child-update': self.ChildUpdate, - 'parent-child-delete': self.ChildDelete, - 'parent-child-create': self.ChildCreate} - self.assertDictContainsSubset(ext_cmd, shell.COMMANDS['2.0']) - - def test_client_methods_have_parent_id_arg(self): - methods = (self.client.list_parents_children, - self.client.show_parents_child, - self.client.update_parents_child, - self.client.delete_parents_child, - self.client.create_parents_child) - for method in methods: - argspec = inspect.getargspec(method) - self.assertIn("parent_id", argspec.args) diff --git a/neutronclient/tests/unit/test_command_meta.py b/neutronclient/tests/unit/test_command_meta.py index 184f36680..dedc3dd1c 100644 --- a/neutronclient/tests/unit/test_command_meta.py +++ b/neutronclient/tests/unit/test_command_meta.py @@ -20,7 +20,6 @@ import logging import testtools -from testtools import helpers from neutronclient.neutron import v2_0 as neutronV20 @@ -30,7 +29,7 @@ def test_neutron_command_meta_defines_log(self): class FakeCommand(neutronV20.NeutronCommand): pass - self.assertTrue(helpers.safe_hasattr(FakeCommand, 'log')) + self.assertTrue(hasattr(FakeCommand, 'log')) self.assertIsInstance(FakeCommand.log, logging.getLoggerClass()) self.assertEqual(__name__ + ".FakeCommand", FakeCommand.log.name) @@ -38,5 +37,5 @@ def test_neutron_command_log_defined_explicitly(self): class FakeCommand(neutronV20.NeutronCommand): log = None - self.assertTrue(helpers.safe_hasattr(FakeCommand, 'log')) + self.assertTrue(hasattr(FakeCommand, 'log')) self.assertIsNone(FakeCommand.log) diff --git a/neutronclient/tests/unit/test_exceptions.py b/neutronclient/tests/unit/test_exceptions.py new file mode 100644 index 000000000..6e063244c --- /dev/null +++ b/neutronclient/tests/unit/test_exceptions.py @@ -0,0 +1,48 @@ +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import sys +from unittest import mock + +from oslo_utils import encodeutils +import testtools + +from neutronclient._i18n import _ +from neutronclient.common import exceptions + + +class TestExceptions(testtools.TestCase): + + def test_exception_print_with_unicode(self): + class TestException(exceptions.NeutronException): + message = _('Exception with %(reason)s') + + multibyte_unicode_string = u'\uff21\uff22\uff23' + e = TestException(reason=multibyte_unicode_string) + + with mock.patch.object(sys, 'stdout') as mock_stdout: + print(e) + + exc_str = 'Exception with %s' % multibyte_unicode_string + mock_stdout.assert_has_calls([mock.call.write(exc_str)]) + + def test_exception_message_with_encoded_unicode(self): + class TestException(exceptions.NeutronException): + message = _('Exception with %(reason)s') + + multibyte_string = u'\uff21\uff22\uff23' + multibyte_binary = encodeutils.safe_encode(multibyte_string) + e = TestException(reason=multibyte_binary) + self.assertEqual('Exception with %s' % multibyte_string, + str(e)) diff --git a/neutronclient/tests/unit/test_http.py b/neutronclient/tests/unit/test_http.py index 2f65b8ed0..5680d4375 100644 --- a/neutronclient/tests/unit/test_http.py +++ b/neutronclient/tests/unit/test_http.py @@ -15,8 +15,10 @@ import abc +from oslo_utils import uuidutils +import osprofiler.profiler +import osprofiler.web from requests_mock.contrib import fixture as mock_fixture -import six import testtools from neutronclient import client @@ -30,14 +32,13 @@ BODY = 'IAMFAKE' -@six.add_metaclass(abc.ABCMeta) -class TestHTTPClientMixin(object): +class TestHTTPClientMixin(object, metaclass=abc.ABCMeta): def setUp(self): super(TestHTTPClientMixin, self).setUp() self.requests = self.useFixture(mock_fixture.Fixture()) - self.clazz, self.http = self.initialize() + self.http = self.initialize() @abc.abstractmethod def initialize(self): @@ -72,12 +73,19 @@ def test_headers_defined_in_headers(self): 'Content-Type': 'application/json'} self._test_headers(headers, body=BODY, headers=headers) + def test_osprofiler_headers_are_injected(self): + osprofiler.profiler.init('SWORDFISH') + self.addCleanup(osprofiler.profiler.clean) + + headers = {'Accept': 'application/json'} + headers.update(osprofiler.web.get_trace_id_headers()) + self._test_headers(headers) + class TestHTTPClient(TestHTTPClientMixin, testtools.TestCase): def initialize(self): - return [client.HTTPClient, - client.HTTPClient(token=AUTH_TOKEN, endpoint_url=END_URL)] + return client.HTTPClient(token=AUTH_TOKEN, endpoint_url=END_URL) def test_request_error(self): def cb(*args, **kwargs): @@ -112,3 +120,38 @@ def test_request_forbidden_is_returned_to_caller(self): resp, resp_text = self.http._cs_request(URL, METHOD) self.assertEqual(403, resp.status_code) self.assertEqual(text, resp_text) + + def test_do_request_success(self): + text = 'test content' + self.requests.register_uri(METHOD, END_URL + URL, text=text) + + resp, resp_text = self.http.do_request(URL, METHOD) + self.assertEqual(200, resp.status_code) + self.assertEqual(text, resp_text) + + def test_do_request_with_headers_success(self): + text = 'test content' + self.requests.register_uri(METHOD, END_URL + URL, text=text, + request_headers={'key': 'value'}) + + resp, resp_text = self.http.do_request(URL, METHOD, + headers={'key': 'value'}) + self.assertEqual(200, resp.status_code) + self.assertEqual(text, resp_text) + + +class TestHTTPClientWithReqId(TestHTTPClientMixin, testtools.TestCase): + """Tests for when global_request_id is set.""" + + def initialize(self): + self.req_id = "req-%s" % uuidutils.generate_uuid() + return client.HTTPClient(token=AUTH_TOKEN, endpoint_url=END_URL, + global_request_id=self.req_id) + + def test_request_success(self): + headers = { + 'Accept': 'application/json', + 'X-OpenStack-Request-ID': self.req_id + } + self.requests.register_uri(METHOD, URL, request_headers=headers) + self.http.request(URL, METHOD) diff --git a/neutronclient/tests/unit/test_name_or_id.py b/neutronclient/tests/unit/test_name_or_id.py deleted file mode 100644 index dcb9a23e6..000000000 --- a/neutronclient/tests/unit/test_name_or_id.py +++ /dev/null @@ -1,227 +0,0 @@ -# Copyright 2012 OpenStack Foundation. -# All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import uuid - -from mox3 import mox -import testtools - -from neutronclient.common import exceptions -from neutronclient.neutron import v2_0 as neutronV20 -from neutronclient.tests.unit import test_cli20 -from neutronclient.v2_0 import client - - -class CLITestNameorID(testtools.TestCase): - - def setUp(self): - """Prepare the test environment.""" - super(CLITestNameorID, self).setUp() - self.mox = mox.Mox() - self.endurl = test_cli20.ENDURL - self.client = client.Client(token=test_cli20.TOKEN, - endpoint_url=self.endurl) - self.addCleanup(self.mox.VerifyAll) - self.addCleanup(self.mox.UnsetStubs) - - def test_get_id_from_id(self): - _id = str(uuid.uuid4()) - reses = {'networks': [{'id': _id, }, ], } - resstr = self.client.serialize(reses) - self.mox.StubOutWithMock(self.client.httpclient, "request") - path = getattr(self.client, "networks_path") - self.client.httpclient.request( - test_cli20.MyUrlComparator( - test_cli20.end_url(path, "fields=id&id=" + _id), - self.client), - 'GET', - body=None, - headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN) - ).AndReturn((test_cli20.MyResp(200), resstr)) - self.mox.ReplayAll() - returned_id = neutronV20.find_resourceid_by_name_or_id( - self.client, 'network', _id) - self.assertEqual(_id, returned_id) - - def test_get_id_from_id_then_name_empty(self): - _id = str(uuid.uuid4()) - reses = {'networks': [{'id': _id, }, ], } - resstr = self.client.serialize(reses) - resstr1 = self.client.serialize({'networks': []}) - self.mox.StubOutWithMock(self.client.httpclient, "request") - path = getattr(self.client, "networks_path") - self.client.httpclient.request( - test_cli20.MyUrlComparator( - test_cli20.end_url(path, "fields=id&id=" + _id), - self.client), - 'GET', - body=None, - headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN) - ).AndReturn((test_cli20.MyResp(200), resstr1)) - self.client.httpclient.request( - test_cli20.MyUrlComparator( - test_cli20.end_url(path, "fields=id&name=" + _id), - self.client), - 'GET', - body=None, - headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN) - ).AndReturn((test_cli20.MyResp(200), resstr)) - self.mox.ReplayAll() - returned_id = neutronV20.find_resourceid_by_name_or_id( - self.client, 'network', _id) - self.assertEqual(_id, returned_id) - - def test_get_id_from_name(self): - name = 'myname' - _id = str(uuid.uuid4()) - reses = {'networks': [{'id': _id, }, ], } - resstr = self.client.serialize(reses) - self.mox.StubOutWithMock(self.client.httpclient, "request") - path = getattr(self.client, "networks_path") - self.client.httpclient.request( - test_cli20.MyUrlComparator( - test_cli20.end_url(path, "fields=id&name=" + name), - self.client), - 'GET', - body=None, - headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN) - ).AndReturn((test_cli20.MyResp(200), resstr)) - self.mox.ReplayAll() - returned_id = neutronV20.find_resourceid_by_name_or_id( - self.client, 'network', name) - self.assertEqual(_id, returned_id) - - def test_get_id_from_name_multiple(self): - name = 'myname' - reses = {'networks': [{'id': str(uuid.uuid4())}, - {'id': str(uuid.uuid4())}]} - resstr = self.client.serialize(reses) - self.mox.StubOutWithMock(self.client.httpclient, "request") - path = getattr(self.client, "networks_path") - self.client.httpclient.request( - test_cli20.MyUrlComparator( - test_cli20.end_url(path, "fields=id&name=" + name), - self.client), - 'GET', - body=None, - headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN) - ).AndReturn((test_cli20.MyResp(200), resstr)) - self.mox.ReplayAll() - exception = self.assertRaises(exceptions.NeutronClientNoUniqueMatch, - neutronV20.find_resourceid_by_name_or_id, - self.client, 'network', name) - self.assertIn('Multiple', exception.message) - - def test_get_id_from_name_notfound(self): - name = 'myname' - reses = {'networks': []} - resstr = self.client.serialize(reses) - self.mox.StubOutWithMock(self.client.httpclient, "request") - path = getattr(self.client, "networks_path") - self.client.httpclient.request( - test_cli20.MyUrlComparator( - test_cli20.end_url(path, "fields=id&name=" + name), - self.client), - 'GET', - body=None, - headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN) - ).AndReturn((test_cli20.MyResp(200), resstr)) - self.mox.ReplayAll() - exception = self.assertRaises(exceptions.NotFound, - neutronV20.find_resourceid_by_name_or_id, - self.client, 'network', name) - self.assertIn('Unable to find', exception.message) - self.assertEqual(404, exception.status_code) - - def test_get_id_from_name_multiple_with_project(self): - name = 'web_server' - project = str(uuid.uuid4()) - expect_id = str(uuid.uuid4()) - reses = {'security_groups': - [{'id': expect_id, 'tenant_id': project}]} - resstr = self.client.serialize(reses) - self.mox.StubOutWithMock(self.client.httpclient, "request") - path = getattr(self.client, "security_groups_path") - self.client.httpclient.request( - test_cli20.MyUrlComparator( - test_cli20.end_url(path, "fields=id&name=%s&tenant_id=%s" % - (name, project)), self.client), - 'GET', - body=None, - headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN) - ).AndReturn((test_cli20.MyResp(200), resstr)) - self.mox.ReplayAll() - - observed_id = neutronV20.find_resourceid_by_name_or_id( - self.client, 'security_group', name, project) - - self.assertEqual(expect_id, observed_id) - - def test_get_id_from_name_multiple_with_project_not_found(self): - name = 'web_server' - project = str(uuid.uuid4()) - resstr_notfound = self.client.serialize({'security_groups': []}) - self.mox.StubOutWithMock(self.client.httpclient, "request") - path = getattr(self.client, "security_groups_path") - self.client.httpclient.request( - test_cli20.MyUrlComparator( - test_cli20.end_url(path, "fields=id&name=%s&tenant_id=%s" % - (name, project)), self.client), - 'GET', - body=None, - headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN) - ).AndReturn((test_cli20.MyResp(200), resstr_notfound)) - self.mox.ReplayAll() - exc = self.assertRaises(exceptions.NotFound, - neutronV20.find_resourceid_by_name_or_id, - self.client, 'security_group', name, project) - self.assertIn('Unable to find', exc.message) - self.assertEqual(404, exc.status_code) - - def _test_get_resource_by_id(self, id_only=False): - _id = str(uuid.uuid4()) - net = {'id': _id, 'name': 'test'} - reses = {'networks': [net], } - resstr = self.client.serialize(reses) - self.mox.StubOutWithMock(self.client.httpclient, "request") - path = getattr(self.client, "networks_path") - if id_only: - query_params = "fields=id&id=%s" % _id - else: - query_params = "id=%s" % _id - self.client.httpclient.request( - test_cli20.MyUrlComparator( - test_cli20.end_url(path, query_params), - self.client), - 'GET', - body=None, - headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN) - ).AndReturn((test_cli20.MyResp(200), resstr)) - self.mox.ReplayAll() - if id_only: - returned_id = neutronV20.find_resourceid_by_id( - self.client, 'network', _id) - self.assertEqual(_id, returned_id) - else: - result = neutronV20.find_resource_by_id( - self.client, 'network', _id) - self.assertEqual(net, result) - - def test_get_resource_by_id(self): - self._test_get_resource_by_id(id_only=False) - - def test_get_resourceid_by_id(self): - self._test_get_resource_by_id(id_only=True) diff --git a/neutronclient/tests/unit/test_quota.py b/neutronclient/tests/unit/test_quota.py deleted file mode 100644 index b9d7b4448..000000000 --- a/neutronclient/tests/unit/test_quota.py +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/env python -# Copyright (C) 2013 Yahoo! Inc. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import sys - -from mox3 import mox - -from neutronclient.common import exceptions -from neutronclient.neutron.v2_0 import quota as test_quota -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20Quota(test_cli20.CLITestV20Base): - def test_show_quota(self): - resource = 'quota' - cmd = test_quota.ShowQuota( - test_cli20.MyApp(sys.stdout), None) - args = ['--tenant-id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args) - - def test_update_quota(self): - resource = 'quota' - cmd = test_quota.UpdateQuota( - test_cli20.MyApp(sys.stdout), None) - args = ['--tenant-id', self.test_id, '--network', 'test'] - self.assertRaises( - exceptions.NeutronClientException, self._test_update_resource, - resource, cmd, self.test_id, args=args, - extrafields={'network': 'new'}) - - def test_delete_quota_get_parser(self): - cmd = test_cli20.MyApp(sys.stdout) - test_quota.DeleteQuota(cmd, None).get_parser(cmd) - - def test_show_quota_positional(self): - resource = 'quota' - cmd = test_quota.ShowQuota( - test_cli20.MyApp(sys.stdout), None) - args = [self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args) - - def test_update_quota_positional(self): - resource = 'quota' - cmd = test_quota.UpdateQuota( - test_cli20.MyApp(sys.stdout), None) - args = [self.test_id, '--network', 'test'] - self.assertRaises( - exceptions.NeutronClientException, self._test_update_resource, - resource, cmd, self.test_id, args=args, - extrafields={'network': 'new'}) - - def test_show_quota_default(self): - resource = 'quota' - cmd = test_quota.ShowQuotaDefault( - test_cli20.MyApp(sys.stdout), None) - args = ['--tenant-id', self.test_id] - self.mox.StubOutWithMock(cmd, "get_client") - self.mox.StubOutWithMock(self.client.httpclient, "request") - cmd.get_client().MultipleTimes().AndReturn(self.client) - expected_res = {'quota': {'port': 50, 'network': 10, 'subnet': 10}} - resstr = self.client.serialize(expected_res) - path = getattr(self.client, "quota_default_path") - return_tup = (test_cli20.MyResp(200), resstr) - self.client.httpclient.request( - test_cli20.end_url(path % self.test_id), 'GET', - body=None, - headers=mox.ContainsKeyValue( - 'X-Auth-Token', test_cli20.TOKEN)).AndReturn(return_tup) - self.mox.ReplayAll() - - cmd_parser = cmd.get_parser("test_" + resource) - parsed_args = cmd_parser.parse_args(args) - cmd.run(parsed_args) - - self.mox.VerifyAll() - self.mox.UnsetStubs() - _str = self.fake_stdout.make_string() - self.assertIn('network', _str) - self.assertIn('subnet', _str) - self.assertIn('port', _str) - self.assertNotIn('subnetpool', _str) diff --git a/neutronclient/tests/unit/test_shell.py b/neutronclient/tests/unit/test_shell.py deleted file mode 100644 index a3326a17d..000000000 --- a/neutronclient/tests/unit/test_shell.py +++ /dev/null @@ -1,355 +0,0 @@ -# Copyright (C) 2013 Yahoo! Inc. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import argparse -import logging -import os -import re -import sys - -import fixtures -from keystoneauth1 import session -import mock -import six -import testtools -from testtools import matchers - -from neutronclient.common import clientmanager -from neutronclient import shell as openstack_shell - - -DEFAULT_USERNAME = 'username' -DEFAULT_PASSWORD = 'password' -DEFAULT_TENANT_ID = 'tenant_id' -DEFAULT_TENANT_NAME = 'tenant_name' -DEFAULT_AUTH_URL = 'http://127.0.0.1:5000/v2.0/' -DEFAULT_TOKEN = '3bcc3d3a03f44e3d8377f9247b0ad155' -DEFAULT_URL = 'http://quantum.example.org:9696/' -DEFAULT_REGION = 'regionOne' -DEFAULT_ENDPOINT_TYPE = 'public' -DEFAULT_API_VERSION = '2.0' -DEFAULT_SERVICE_TYPE = 'network' -DEFAULT_SERVICE_NAME = 'neutron' -DEFAULT_RETRIES = 3 -DEFAULT_TIMEOUT = 3.0 - - -class ShellTest(testtools.TestCase): - - FAKE_ENV = { - 'OS_USERNAME': DEFAULT_USERNAME, - 'OS_PASSWORD': DEFAULT_PASSWORD, - 'OS_TENANT_ID': DEFAULT_TENANT_ID, - 'OS_TENANT_NAME': DEFAULT_TENANT_NAME, - 'OS_AUTH_URL': DEFAULT_AUTH_URL, - 'OS_REGION_NAME': None, - 'HTTP_PROXY': None, - 'http_proxy': None, - } - - # Patch os.environ to avoid required auth info. - def setUp(self): - super(ShellTest, self).setUp() - for var in self.FAKE_ENV: - self.useFixture( - fixtures.EnvironmentVariable( - var, self.FAKE_ENV[var])) - - def shell(self, argstr, check=False, expected_val=0): - # expected_val is the expected return value after executing - # the command in NeutronShell - orig = (sys.stdout, sys.stderr) - clean_env = {} - _old_env, os.environ = os.environ, clean_env.copy() - try: - sys.stdout = six.moves.cStringIO() - sys.stderr = six.moves.cStringIO() - _shell = openstack_shell.NeutronShell('2.0') - _shell.run(argstr.split()) - except SystemExit: - exc_type, exc_value, exc_traceback = sys.exc_info() - self.assertEqual(expected_val, exc_value.code) - finally: - stdout = sys.stdout.getvalue() - stderr = sys.stderr.getvalue() - sys.stdout.close() - sys.stderr.close() - sys.stdout, sys.stderr = orig - os.environ = _old_env - return stdout, stderr - - def test_run_unknown_command(self): - self.useFixture(fixtures.FakeLogger(level=logging.DEBUG)) - stdout, stderr = self.shell('fake', check=True) - self.assertFalse(stdout) - self.assertEqual("Unknown command ['fake']", stderr.strip()) - - def test_help(self): - required = 'usage:' - help_text, stderr = self.shell('help') - self.assertThat( - help_text, - matchers.MatchesRegex(required)) - self.assertFalse(stderr) - - def test_bash_completion(self): - required = '.*os_user_domain_id.*' - bash_completion, stderr = self.shell('bash-completion') - self.assertThat( - bash_completion, - matchers.MatchesRegex(required)) - self.assertFalse(stderr) - - def test_help_on_subcommand(self): - required = [ - '.*?^usage: .* quota-list'] - stdout, stderr = self.shell('help quota-list') - for r in required: - self.assertThat( - stdout, - matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE)) - self.assertFalse(stderr) - - def test_help_command(self): - required = 'usage:' - help_text, stderr = self.shell('help network-create') - self.assertThat( - help_text, - matchers.MatchesRegex(required)) - self.assertFalse(stderr) - - def test_bash_completion_in_outputs_of_help_command(self): - help_text, stderr = self.shell('help') - self.assertFalse(stderr) - completion_cmd = "bash-completion" - completion_help_str = ("Prints all of the commands and options " - "for bash-completion.") - self.assertIn(completion_cmd, help_text) - self.assertIn(completion_help_str, help_text) - - def test_bash_completion_command(self): - # just check we have some output - required = [ - '.*--tenant_id', - '.*help', - '.*--dns-nameserver'] - help_text, stderr = self.shell('neutron bash-completion') - self.assertFalse(stderr) - for r in required: - self.assertThat(help_text, - matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE)) - - def test_build_option_parser(self): - neutron_shell = openstack_shell.NeutronShell('2.0') - result = neutron_shell.build_option_parser('descr', '2.0') - self.assertIsInstance(result, argparse.ArgumentParser) - - @mock.patch.object(openstack_shell.NeutronShell, 'run') - def test_main_with_unicode(self, fake_shell): - unicode_text = u'\u7f51\u7edc' - argv = ['net-list', unicode_text, unicode_text] - fake_shell.return_value = 0 - ret = openstack_shell.main(argv=argv) - fake_shell.assert_called_once_with([u'net-list', unicode_text, - unicode_text]) - self.assertEqual(0, ret) - - def test_endpoint_option(self): - shell = openstack_shell.NeutronShell('2.0') - parser = shell.build_option_parser('descr', '2.0') - - # Neither $OS_ENDPOINT_TYPE nor --os-endpoint-type - namespace = parser.parse_args([]) - self.assertEqual('public', namespace.os_endpoint_type) - - # --endpoint-type but not $OS_ENDPOINT_TYPE - namespace = parser.parse_args(['--os-endpoint-type=admin']) - self.assertEqual('admin', namespace.os_endpoint_type) - - def test_endpoint_environment_variable(self): - fixture = fixtures.EnvironmentVariable("OS_ENDPOINT_TYPE", - "public") - self.useFixture(fixture) - - shell = openstack_shell.NeutronShell('2.0') - parser = shell.build_option_parser('descr', '2.0') - - # $OS_ENDPOINT_TYPE but not --endpoint-type - namespace = parser.parse_args([]) - self.assertEqual("public", namespace.os_endpoint_type) - - # --endpoint-type and $OS_ENDPOINT_TYPE - namespace = parser.parse_args(['--endpoint-type=admin']) - self.assertEqual('admin', namespace.endpoint_type) - - def test_timeout_option(self): - shell = openstack_shell.NeutronShell('2.0') - parser = shell.build_option_parser('descr', '2.0') - - # Neither $OS_ENDPOINT_TYPE nor --endpoint-type - namespace = parser.parse_args([]) - self.assertIsNone(namespace.http_timeout) - - # --endpoint-type but not $OS_ENDPOINT_TYPE - namespace = parser.parse_args(['--http-timeout=50']) - self.assertEqual(50, namespace.http_timeout) - - def test_timeout_environment_variable(self): - fixture = fixtures.EnvironmentVariable("OS_NETWORK_TIMEOUT", - "50") - self.useFixture(fixture) - - shell = openstack_shell.NeutronShell('2.0') - parser = shell.build_option_parser('descr', '2.0') - - namespace = parser.parse_args([]) - self.assertEqual(50, namespace.http_timeout) - - def test_run_incomplete_command(self): - self.useFixture(fixtures.FakeLogger(level=logging.DEBUG)) - cmd = ( - '--os-username test --os-password test --os-project-id test ' - '--os-auth-strategy keystone --os-auth-url ' - '%s port-create' % - DEFAULT_AUTH_URL) - stdout, stderr = self.shell(cmd, check=True, expected_val=2) - search_str = "Try 'neutron help port-create' for more information" - self.assertTrue(any(search_str in string for string - in stderr.split('\n'))) - - def _test_authenticate_user(self, expect_verify, expect_insecure, - **options): - base_options = {'os_cloud': None, - 'http_timeout': DEFAULT_TIMEOUT, - 'region_name': DEFAULT_REGION, - 'network_service_name': DEFAULT_SERVICE_NAME, - 'neutron_service_type': DEFAULT_SERVICE_TYPE} - - options.update(base_options) - if options.get('os_token'): - options.update({'os_token': 'token', 'os_url': 'url'}) - else: - options.update({'os_token': None, 'os_url': None}) - - with mock.patch.object(openstack_shell.NeutronShell, - 'run_subcommand'), \ - mock.patch.object(session, 'Session') as session_mock, \ - mock.patch.object(clientmanager, 'ClientManager') as cmgr_mock: - - shell = openstack_shell.NeutronShell(DEFAULT_API_VERSION) - shell.options = mock.Mock(spec=options.keys()) - for k, v in options.items(): - setattr(shell.options, k, v) - shell.options.os_endpoint_type = DEFAULT_ENDPOINT_TYPE - shell.options.retries = DEFAULT_RETRIES - - if not (options.get('os_token') and options.get('os_url')): - auth = mock.ANY - auth_session = mock.sentinel.session - session_mock.return_value = auth_session - else: - auth = None - auth_session = None - - shell.authenticate_user() - - if not (options.get('os_token') and options.get('os_url')): - session_mock.assert_called_once_with( - auth=mock.ANY, verify=expect_verify, - cert=options.get('cert'), - timeout=DEFAULT_TIMEOUT) - else: - self.assertFalse(session_mock.called) - - cmgr_mock.assert_called_once_with( - retries=DEFAULT_RETRIES, - raise_errors=False, - session=auth_session, - url=options.get('os_url'), - token=options.get('os_token'), - region_name=DEFAULT_REGION, - api_version=DEFAULT_API_VERSION, - service_type=DEFAULT_SERVICE_TYPE, - service_name=DEFAULT_SERVICE_NAME, - endpoint_type=DEFAULT_ENDPOINT_TYPE, - auth=auth, - insecure=expect_insecure, - log_credentials=True) - - def test_authenticate_secure_with_cacert_with_cert(self): - self._test_authenticate_user( - insecure=False, cacert='cacert', cert='cert', - expect_verify='cacert', expect_insecure=False) - - def test_authenticate_secure_with_cacert_with_cert_with_token(self): - self._test_authenticate_user( - os_token='token', - insecure=False, cacert='cacert', cert='cert', - expect_verify='cacert', expect_insecure=False) - - def test_authenticate_insecure_with_cacert_with_cert(self): - self._test_authenticate_user( - insecure=True, cacert='cacert', cert='cert', - expect_verify=False, expect_insecure=True) - - def test_authenticate_insecure_with_cacert_with_cert_with_token(self): - self._test_authenticate_user( - os_token='token', - insecure=True, cacert='cacert', cert='cert', - expect_verify=False, expect_insecure=True) - - def test_authenticate_secure_without_cacert_with_cert(self): - self._test_authenticate_user( - insecure=False, cert='cert', - expect_verify=True, expect_insecure=False) - - def test_authenticate_secure_without_cacert_with_cert_with_token(self): - self._test_authenticate_user( - os_token='token', - insecure=False, cert='cert', - expect_verify=True, expect_insecure=False) - - def test_authenticate_insecure_without_cacert_with_cert(self): - self._test_authenticate_user( - insecure=True, cert='cert', - expect_verify=False, expect_insecure=True) - - def test_authenticate_insecure_without_cacert_with_cert_with_token(self): - self._test_authenticate_user( - os_token='token', - insecure=True, cert='cert', - expect_verify=False, expect_insecure=True) - - def test_authenticate_secure_with_cacert_without_cert(self): - self._test_authenticate_user( - insecure=False, cacert='cacert', - expect_verify='cacert', expect_insecure=False) - - def test_authenticate_secure_with_cacert_without_cert_with_token(self): - self._test_authenticate_user( - os_token='token', - insecure=False, cacert='cacert', - expect_verify='cacert', expect_insecure=False) - - def test_authenticate_insecure_with_cacert_without_cert(self): - self._test_authenticate_user( - insecure=True, cacert='cacert', - expect_verify=False, expect_insecure=True) - - def test_authenticate_insecure_with_cacert_without_cert_with_token(self): - self._test_authenticate_user( - os_token='token', - insecure=True, cacert='cacert', - expect_verify=False, expect_insecure=True) diff --git a/neutronclient/tests/unit/test_utils.py b/neutronclient/tests/unit/test_utils.py index 44152418d..4806682b6 100644 --- a/neutronclient/tests/unit/test_utils.py +++ b/neutronclient/tests/unit/test_utils.py @@ -15,6 +15,8 @@ import argparse +from oslo_utils import netutils + import testtools from neutronclient.common import exceptions @@ -49,6 +51,13 @@ def test_invalid_string_to_dictionary(self): self.assertRaises(argparse.ArgumentTypeError, utils.str2dict, input_str) + def test_string_with_comma_value_to_dictionary(self): + input_str = ('opt_name=classless-static-route,' + 'opt_value=169.254.169.254/32,10.0.0.1') + expected = {'opt_name': 'classless-static-route', + 'opt_value': '169.254.169.254/32,10.0.0.1'} + self.assertEqual(expected, utils.str2dict(input_str)) + def test_str2dict_optional_keys(self): self.assertDictEqual({'key1': 'value1'}, utils.str2dict('key1=value1', @@ -140,9 +149,9 @@ def __call__(self, *args, **kwargs): self.assertEqual(('test_name', 'test_id', 'test', 'pass'), act) def test_is_cidr(self): - self.assertTrue(utils.is_valid_cidr('10.10.10.0/24')) - self.assertFalse(utils.is_valid_cidr('10.10.10..0/24')) - self.assertFalse(utils.is_valid_cidr('wrong_cidr_format')) + self.assertTrue(netutils.is_valid_cidr('10.10.10.0/24')) + self.assertFalse(netutils.is_valid_cidr('10.10.10..0/24')) + self.assertFalse(netutils.is_valid_cidr('wrong_cidr_format')) class ImportClassTestCase(testtools.TestCase): diff --git a/neutronclient/tests/unit/vpn/__init__.py b/neutronclient/tests/unit/vpn/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutronclient/tests/unit/vpn/test_cli20_endpoint_group.py b/neutronclient/tests/unit/vpn/test_cli20_endpoint_group.py deleted file mode 100644 index f43da906b..000000000 --- a/neutronclient/tests/unit/vpn/test_cli20_endpoint_group.py +++ /dev/null @@ -1,145 +0,0 @@ -# (c) Copyright 2015 Cisco Systems, Inc. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import sys - -from neutronclient.neutron.v2_0.vpn import endpoint_group -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20VpnEndpointGroupJSON(test_cli20.CLITestV20Base): - - def setUp(self): - super(CLITestV20VpnEndpointGroupJSON, self).setUp() - self.register_non_admin_status_resource('endpoint_group') - - def test_create_endpoint_group_with_cidrs(self): - # vpn-endpoint-group-create with CIDR endpoints.""" - resource = 'endpoint_group' - cmd = endpoint_group.CreateEndpointGroup(test_cli20.MyApp(sys.stdout), - None) - tenant_id = 'mytenant-id' - my_id = 'my-id' - name = 'my-endpoint-group' - description = 'my endpoint group' - endpoint_type = 'cidr' - endpoints = ['10.0.0.0/24', '20.0.0.0/24'] - - args = ['--name', name, - '--description', description, - '--tenant-id', tenant_id, - '--type', endpoint_type, - '--value', '10.0.0.0/24', - '--value', '20.0.0.0/24'] - - position_names = ['name', 'description', 'tenant_id', - 'type', 'endpoints'] - - position_values = [name, description, tenant_id, - endpoint_type, endpoints] - - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values) - - def test_create_endpoint_group_with_subnets(self): - # vpn-endpoint-group-create with subnet endpoints.""" - resource = 'endpoint_group' - cmd = endpoint_group.CreateEndpointGroup(test_cli20.MyApp(sys.stdout), - None) - tenant_id = 'mytenant-id' - my_id = 'my-id' - endpoint_type = 'subnet' - subnet = 'subnet-id' - endpoints = [subnet] - - args = ['--type', endpoint_type, - '--value', subnet, - '--tenant-id', tenant_id] - - position_names = ['type', 'endpoints', 'tenant_id'] - - position_values = [endpoint_type, endpoints, tenant_id] - - self._test_create_resource(resource, cmd, None, my_id, args, - position_names, position_values) - - def test_list_endpoint_group(self): - # vpn-endpoint-group-list. - resources = "endpoint_groups" - cmd = endpoint_group.ListEndpointGroup(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, True) - - def test_list_endpoint_group_pagination(self): - # vpn-endpoint-group-list. - resources = "endpoint_groups" - cmd = endpoint_group.ListEndpointGroup(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_endpoint_group_sort(self): - # vpn-endpoint-group-list --sort-key name --sort-key id - # --sort-key asc --sort-key desc - resources = "endpoint_groups" - cmd = endpoint_group.ListEndpointGroup(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_endpoint_group_limit(self): - # vpn-endpoint-group-list -P. - resources = "endpoint_groups" - cmd = endpoint_group.ListEndpointGroup(test_cli20.MyApp(sys.stdout), - None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_show_endpoint_group_id(self): - # vpn-endpoint-group-show test_id. - resource = 'endpoint_group' - cmd = endpoint_group.ShowEndpointGroup(test_cli20.MyApp(sys.stdout), - None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id']) - - def test_show_endpoint_group_id_name(self): - # vpn-endpoint-group-show. - resource = 'endpoint_group' - cmd = endpoint_group.ShowEndpointGroup(test_cli20.MyApp(sys.stdout), - None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name']) - - def test_update_endpoint_group(self): - # vpn-endpoint-group-update myid --name newname --description newdesc. - resource = 'endpoint_group' - cmd = endpoint_group.UpdateEndpointGroup(test_cli20.MyApp(sys.stdout), - None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'newname', - '--description', 'newdesc'], - {'name': 'newname', - 'description': 'newdesc'}) - - def test_delete_endpoint_group(self): - # vpn-endpoint-group-delete my-id. - resource = 'endpoint_group' - cmd = endpoint_group.DeleteEndpointGroup(test_cli20.MyApp(sys.stdout), - None) - my_id = 'my-id' - args = [my_id] - self._test_delete_resource(resource, cmd, my_id, args) diff --git a/neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py b/neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py deleted file mode 100644 index 1dbf5885c..000000000 --- a/neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py +++ /dev/null @@ -1,229 +0,0 @@ -# (c) Copyright 2013 Hewlett-Packard Development Company, L.P. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import sys - -from neutronclient.common import exceptions -from neutronclient.neutron.v2_0.vpn import ikepolicy -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20VpnIkePolicyJSON(test_cli20.CLITestV20Base): - - non_admin_status_resources = ['ikepolicy'] - - def _test_create_ikepolicy_all_params(self, auth='sha1', - expected_exc=None): - # vpn-ikepolicy-create all params. - resource = 'ikepolicy' - cmd = ikepolicy.CreateIKEPolicy(test_cli20.MyApp(sys.stdout), None) - name = 'ikepolicy1' - description = 'my-ike-policy' - auth_algorithm = auth - encryption_algorithm = 'aes-256' - ike_version = 'v1' - phase1_negotiation_mode = 'main' - pfs = 'group5' - tenant_id = 'my-tenant' - my_id = 'my-id' - lifetime = 'units=seconds,value=20000' - - args = [name, - '--description', description, - '--tenant-id', tenant_id, - '--auth-algorithm', auth_algorithm, - '--encryption-algorithm', encryption_algorithm, - '--ike-version', ike_version, - '--phase1-negotiation-mode', phase1_negotiation_mode, - '--lifetime', lifetime, - '--pfs', pfs] - - position_names = ['name', 'description', - 'auth_algorithm', 'encryption_algorithm', - 'phase1_negotiation_mode', - 'ike_version', 'pfs', - 'tenant_id'] - - position_values = [name, description, - auth_algorithm, encryption_algorithm, - phase1_negotiation_mode, ike_version, pfs, - tenant_id] - extra_body = { - 'lifetime': { - 'units': 'seconds', - 'value': 20000, - }, - } - - if not expected_exc: - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values, - extra_body=extra_body) - else: - self.assertRaises( - expected_exc, - self._test_create_resource, - resource, cmd, name, my_id, args, - position_names, position_values, - extra_body=extra_body) - - def test_create_ikepolicy_all_params(self): - self._test_create_ikepolicy_all_params() - - def test_create_ikepolicy_auth_sha256(self): - self._test_create_ikepolicy_all_params(auth='sha256') - - def test_create_ikepolicy_invalid_auth(self): - self._test_create_ikepolicy_all_params(auth='xyz', - expected_exc=SystemExit) - - def test_create_ikepolicy_with_limited_params(self): - # vpn-ikepolicy-create with limited params. - resource = 'ikepolicy' - cmd = ikepolicy.CreateIKEPolicy(test_cli20.MyApp(sys.stdout), None) - name = 'ikepolicy1' - auth_algorithm = 'sha1' - encryption_algorithm = 'aes-128' - ike_version = 'v1' - phase1_negotiation_mode = 'main' - pfs = 'group5' - tenant_id = 'my-tenant' - my_id = 'my-id' - - args = [name, - '--tenant-id', tenant_id] - - position_names = ['name', - 'auth_algorithm', 'encryption_algorithm', - 'phase1_negotiation_mode', - 'ike_version', 'pfs', - 'tenant_id'] - - position_values = [name, - auth_algorithm, encryption_algorithm, - phase1_negotiation_mode, - ike_version, pfs, - tenant_id] - - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values) - - def _test_lifetime_values(self, lifetime, expected_exc=None): - resource = 'ikepolicy' - cmd = ikepolicy.CreateIKEPolicy(test_cli20.MyApp(sys.stdout), None) - name = 'ikepolicy1' - description = 'my-ike-policy' - auth_algorithm = 'sha1' - encryption_algorithm = 'aes-256' - ike_version = 'v1' - phase1_negotiation_mode = 'main' - pfs = 'group5' - tenant_id = 'my-tenant' - my_id = 'my-id' - - args = [name, - '--description', description, - '--tenant-id', tenant_id, - '--auth-algorithm', auth_algorithm, - '--encryption-algorithm', encryption_algorithm, - '--ike-version', ike_version, - '--phase1-negotiation-mode', phase1_negotiation_mode, - '--lifetime', lifetime, - '--pfs', pfs] - - position_names = ['name', 'description', - 'auth_algorithm', 'encryption_algorithm', - 'phase1_negotiation_mode', - 'ike_version', 'pfs', - 'tenant_id'] - - position_values = [name, description, - auth_algorithm, encryption_algorithm, - phase1_negotiation_mode, ike_version, pfs, - tenant_id] - if not expected_exc: - expected_exc = exceptions.CommandError - self.assertRaises( - expected_exc, - self._test_create_resource, - resource, cmd, name, my_id, args, - position_names, position_values) - - def test_create_ikepolicy_with_invalid_lifetime_keys(self): - lifetime = 'uts=seconds,val=20000' - self._test_lifetime_values(lifetime, expected_exc=SystemExit) - - def test_create_ikepolicy_with_invalid_lifetime_value(self): - lifetime = 'units=seconds,value=-1' - self._test_lifetime_values(lifetime) - - def test_list_ikepolicy(self): - # vpn-ikepolicy-list. - resources = "ikepolicies" - cmd = ikepolicy.ListIKEPolicy(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True) - - def test_list_ikepolicy_pagination(self): - # vpn-ikepolicy-list. - resources = "ikepolicies" - cmd = ikepolicy.ListIKEPolicy(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_ikepolicy_sort(self): - # vpn-ikepolicy-list --sort-key name --sort-key id --sort-key asc - # --sort-key desc - resources = "ikepolicies" - cmd = ikepolicy.ListIKEPolicy(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_ikepolicy_limit(self): - # vpn-ikepolicy-list -P. - resources = "ikepolicies" - cmd = ikepolicy.ListIKEPolicy(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_show_ikepolicy_id(self): - # vpn-ikepolicy-show ikepolicy_id. - resource = 'ikepolicy' - cmd = ikepolicy.ShowIKEPolicy(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id']) - - def test_show_ikepolicy_id_name(self): - # vpn-ikepolicy-show. - resource = 'ikepolicy' - cmd = ikepolicy.ShowIKEPolicy(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name']) - - def test_update_ikepolicy(self): - # vpn-ikepolicy-update myid --name newname --tags a b. - resource = 'ikepolicy' - cmd = ikepolicy.UpdateIKEPolicy(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'newname'], - {'name': 'newname', }) - - def test_delete_ikepolicy(self): - # vpn-ikepolicy-delete my-id. - resource = 'ikepolicy' - cmd = ikepolicy.DeleteIKEPolicy(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - args = [my_id] - self._test_delete_resource(resource, cmd, my_id, args) diff --git a/neutronclient/tests/unit/vpn/test_cli20_ipsec_site_connection.py b/neutronclient/tests/unit/vpn/test_cli20_ipsec_site_connection.py deleted file mode 100644 index 8ead88f2a..000000000 --- a/neutronclient/tests/unit/vpn/test_cli20_ipsec_site_connection.py +++ /dev/null @@ -1,331 +0,0 @@ -# (c) Copyright 2013 Hewlett-Packard Development Company, L.P. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import sys - -from neutronclient.common import exceptions -from neutronclient.neutron.v2_0.vpn import ipsec_site_connection -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20IPsecSiteConnectionJSON(test_cli20.CLITestV20Base): - - # TODO(pcm): Remove, once peer-cidr is deprecated completely - def test_create_ipsec_site_connection_all_params_using_peer_cidrs(self): - # ipsecsite-connection-create all params using peer CIDRs. - resource = 'ipsec_site_connection' - cmd = ipsec_site_connection.CreateIPsecSiteConnection( - test_cli20.MyApp(sys.stdout), None - ) - tenant_id = 'mytenant_id' - name = 'connection1' - my_id = 'my_id' - peer_address = '192.168.2.10' - peer_id = '192.168.2.10' - psk = 'abcd' - mtu = '1500' - initiator = 'bi-directional' - vpnservice_id = 'vpnservice_id' - ikepolicy_id = 'ikepolicy_id' - ipsecpolicy_id = 'ipsecpolicy_id' - peer_cidrs = ['192.168.3.0/24', '192.168.2.0/24'] - admin_state = True - description = 'my-vpn-connection' - dpd = 'action=restart,interval=30,timeout=120' - - args = ['--tenant-id', tenant_id, - '--peer-address', peer_address, '--peer-id', peer_id, - '--psk', psk, '--initiator', initiator, - '--vpnservice-id', vpnservice_id, - '--ikepolicy-id', ikepolicy_id, '--name', name, - '--ipsecpolicy-id', ipsecpolicy_id, '--mtu', mtu, - '--description', description, - '--peer-cidr', '192.168.3.0/24', - '--peer-cidr', '192.168.2.0/24', - '--dpd', dpd] - - position_names = ['name', 'tenant_id', 'admin_state_up', - 'peer_address', 'peer_id', 'peer_cidrs', - 'psk', 'mtu', 'initiator', 'description', - 'vpnservice_id', 'ikepolicy_id', - 'ipsecpolicy_id'] - - position_values = [name, tenant_id, admin_state, peer_address, - peer_id, peer_cidrs, psk, mtu, - initiator, description, - vpnservice_id, ikepolicy_id, ipsecpolicy_id] - extra_body = { - 'dpd': { - 'action': 'restart', - 'interval': 30, - 'timeout': 120, - }, - } - - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values, - extra_body=extra_body) - - def test_create_ipsec_site_conn_all_params(self): - # ipsecsite-connection-create all params using endpoint groups. - resource = 'ipsec_site_connection' - cmd = ipsec_site_connection.CreateIPsecSiteConnection( - test_cli20.MyApp(sys.stdout), None - ) - tenant_id = 'mytenant_id' - name = 'connection1' - my_id = 'my_id' - peer_address = '192.168.2.10' - peer_id = '192.168.2.10' - psk = 'abcd' - mtu = '1500' - initiator = 'bi-directional' - vpnservice_id = 'vpnservice_id' - ikepolicy_id = 'ikepolicy_id' - ipsecpolicy_id = 'ipsecpolicy_id' - local_ep_group = 'local-epg' - peer_ep_group = 'peer-epg' - admin_state = True - description = 'my-vpn-connection' - dpd = 'action=restart,interval=30,timeout=120' - - args = ['--tenant-id', tenant_id, - '--peer-address', peer_address, '--peer-id', peer_id, - '--psk', psk, '--initiator', initiator, - '--vpnservice-id', vpnservice_id, - '--ikepolicy-id', ikepolicy_id, '--name', name, - '--ipsecpolicy-id', ipsecpolicy_id, '--mtu', mtu, - '--description', description, - '--local-ep-group', local_ep_group, - '--peer-ep-group', peer_ep_group, - '--dpd', dpd] - - position_names = ['name', 'tenant_id', 'admin_state_up', - 'peer_address', 'peer_id', 'psk', 'mtu', - 'local_ep_group_id', 'peer_ep_group_id', - 'initiator', 'description', - 'vpnservice_id', 'ikepolicy_id', - 'ipsecpolicy_id'] - - position_values = [name, tenant_id, admin_state, peer_address, - peer_id, psk, mtu, local_ep_group, - peer_ep_group, initiator, description, - vpnservice_id, ikepolicy_id, ipsecpolicy_id] - extra_body = { - 'dpd': { - 'action': 'restart', - 'interval': 30, - 'timeout': 120, - }, - } - - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values, - extra_body=extra_body) - - def test_create_ipsec_site_connection_with_limited_params(self): - # ipsecsite-connection-create with limited params. - resource = 'ipsec_site_connection' - cmd = ipsec_site_connection.CreateIPsecSiteConnection( - test_cli20.MyApp(sys.stdout), None - ) - tenant_id = 'mytenant_id' - my_id = 'my_id' - peer_address = '192.168.2.10' - peer_id = '192.168.2.10' - psk = 'abcd' - mtu = '1500' - initiator = 'bi-directional' - vpnservice_id = 'vpnservice_id' - ikepolicy_id = 'ikepolicy_id' - ipsecpolicy_id = 'ipsecpolicy_id' - local_ep_group = 'local-epg' - peer_ep_group = 'peer-epg' - admin_state = True - - args = ['--tenant-id', tenant_id, - '--peer-address', peer_address, - '--peer-id', peer_id, - '--psk', psk, - '--vpnservice-id', vpnservice_id, - '--ikepolicy-id', ikepolicy_id, - '--ipsecpolicy-id', ipsecpolicy_id, - '--local-ep-group', local_ep_group, - '--peer-ep-group', peer_ep_group] - - position_names = ['tenant_id', 'admin_state_up', - 'peer_address', 'peer_id', - 'local_ep_group_id', 'peer_ep_group_id', - 'psk', 'mtu', 'initiator', - 'vpnservice_id', 'ikepolicy_id', - 'ipsecpolicy_id'] - - position_values = [tenant_id, admin_state, peer_address, peer_id, - local_ep_group, peer_ep_group, psk, mtu, initiator, - vpnservice_id, ikepolicy_id, ipsecpolicy_id] - - self._test_create_resource(resource, cmd, None, my_id, args, - position_names, position_values) - - def _test_create_failure(self, additional_args=None, expected_exc=None): - # Helper to test failure of IPSec site-to-site creation failure. - resource = 'ipsec_site_connection' - cmd = ipsec_site_connection.CreateIPsecSiteConnection( - test_cli20.MyApp(sys.stdout), None - ) - tenant_id = 'mytenant_id' - my_id = 'my_id' - peer_address = '192.168.2.10' - peer_id = '192.168.2.10' - psk = 'abcd' - mtu = '1500' - initiator = 'bi-directional' - vpnservice_id = 'vpnservice_id' - ikepolicy_id = 'ikepolicy_id' - ipsecpolicy_id = 'ipsecpolicy_id' - admin_state = True - - args = ['--tenant-id', tenant_id, - '--peer-address', peer_address, - '--peer-id', peer_id, - '--psk', psk, - '--vpnservice-id', vpnservice_id, - '--ikepolicy-id', ikepolicy_id, - '--ipsecpolicy-id', ipsecpolicy_id] - if additional_args is not None: - args += additional_args - position_names = ['tenant_id', 'admin_state_up', 'peer_address', - 'peer_id', 'psk', 'mtu', 'initiator', - 'local_ep_group_id', 'peer_ep_group_id', - 'vpnservice_id', 'ikepolicy_id', 'ipsecpolicy_id'] - - position_values = [tenant_id, admin_state, peer_address, peer_id, psk, - mtu, initiator, None, None, vpnservice_id, - ikepolicy_id, ipsecpolicy_id] - if not expected_exc: - expected_exc = exceptions.CommandError - self.assertRaises(expected_exc, - self._test_create_resource, - resource, cmd, None, my_id, args, - position_names, position_values) - - def test_fail_create_with_invalid_mtu(self): - # ipsecsite-connection-create with invalid dpd values. - bad_mtu = ['--mtu', '67'] - self._test_create_failure(bad_mtu) - - def test_fail_create_with_invalid_dpd_keys(self): - bad_dpd_key = ['--dpd', 'act=restart,interval=30,time=120'] - self._test_create_failure(bad_dpd_key, SystemExit) - - def test_fail_create_with_invalid_dpd_values(self): - bad_dpd_values = ['--dpd', 'action=hold,interval=30,timeout=-1'] - self._test_create_failure(bad_dpd_values) - - def test_fail_create_missing_endpoint_groups_or_cidr(self): - # Must provide either endpoint groups or peer cidrs. - self._test_create_failure() - - def test_fail_create_missing_peer_endpoint_group(self): - # Fails if dont have both endpoint groups - missing peer. - self._test_create_failure(['--local-ep-group', 'local-epg']) - - def test_fail_create_missing_local_endpoint_group(self): - # Fails if dont have both endpoint groups - missing local. - self._test_create_failure(['--peer-ep-group', 'peer-epg']) - - def test_fail_create_when_both_endpoints_and_peer_cidr(self): - # Cannot intermix endpoint groups and peer CIDRs for create. - additional_args = ['--local-ep-group', 'local-epg', - '--peer-ep-group', 'peer-epg', - '--peer-cidr', '10.2.0.0/24'] - self._test_create_failure(additional_args) - - def test_list_ipsec_site_connection(self): - # ipsecsite-connection-list. - resources = "ipsec_site_connections" - cmd = ipsec_site_connection.ListIPsecSiteConnection( - test_cli20.MyApp(sys.stdout), None - ) - self._test_list_resources(resources, cmd, True) - - def test_list_ipsec_site_connection_pagination(self): - # ipsecsite-connection-list. - resources = "ipsec_site_connections" - cmd = ipsec_site_connection.ListIPsecSiteConnection( - test_cli20.MyApp(sys.stdout), None - ) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_ipsec_site_connection_sort(self): - # ipsecsite-connection-list. - # --sort-key name --sort-key id --sort-key asc --sort-key desc - resources = "ipsec_site_connections" - cmd = ipsec_site_connection.ListIPsecSiteConnection( - test_cli20.MyApp(sys.stdout), None - ) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_ipsec_site_connection_limit(self): - # ipsecsite-connection-list -P. - resources = "ipsec_site_connections" - cmd = ipsec_site_connection.ListIPsecSiteConnection( - test_cli20.MyApp(sys.stdout), None - ) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_delete_ipsec_site_connection(self): - # ipsecsite-connection-delete my-id. - resource = 'ipsec_site_connection' - cmd = ipsec_site_connection.DeleteIPsecSiteConnection( - test_cli20.MyApp(sys.stdout), None - ) - my_id = 'my-id' - args = [my_id] - self._test_delete_resource(resource, cmd, my_id, args) - - def test_update_ipsec_site_connection(self): - # ipsecsite-connection-update myid --name myname --tags a b.""" - resource = 'ipsec_site_connection' - cmd = ipsec_site_connection.UpdateIPsecSiteConnection( - test_cli20.MyApp(sys.stdout), None - ) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'Branch-new', - '--tags', 'a', 'b'], - {'name': 'Branch-new', - 'tags': ['a', 'b'], }) - - def test_show_ipsec_site_connection_id(self): - # ipsecsite-connection-show test_id.""" - resource = 'ipsec_site_connection' - cmd = ipsec_site_connection.ShowIPsecSiteConnection( - test_cli20.MyApp(sys.stdout), None - ) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id']) - - def test_show_ipsec_site_connection_id_name(self): - # ipsecsite-connection-show.""" - resource = 'ipsec_site_connection' - cmd = ipsec_site_connection.ShowIPsecSiteConnection( - test_cli20.MyApp(sys.stdout), None - ) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name']) diff --git a/neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py b/neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py deleted file mode 100644 index c8f70f6b4..000000000 --- a/neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py +++ /dev/null @@ -1,230 +0,0 @@ -# (c) Copyright 2013 Hewlett-Packard Development Company, L.P. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import sys - -from neutronclient.common import exceptions -from neutronclient.neutron.v2_0.vpn import ipsecpolicy -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20VpnIpsecPolicyJSON(test_cli20.CLITestV20Base): - - non_admin_status_resources = ['ipsecpolicy'] - - def _test_create_ipsecpolicy_all_params(self, auth='sha1', - expected_exc=None): - # vpn-ipsecpolicy-create all params with dashes. - resource = 'ipsecpolicy' - cmd = ipsecpolicy.CreateIPsecPolicy(test_cli20.MyApp(sys.stdout), None) - name = 'ipsecpolicy1' - description = 'first-ipsecpolicy1' - auth_algorithm = auth - encryption_algorithm = 'aes-256' - encapsulation_mode = 'tunnel' - pfs = 'group5' - transform_protocol = 'ah' - tenant_id = 'my-tenant' - my_id = 'my-id' - lifetime = 'units=seconds,value=20000' - - args = [name, - '--description', description, - '--tenant-id', tenant_id, - '--auth-algorithm', auth_algorithm, - '--encryption-algorithm', encryption_algorithm, - '--transform-protocol', transform_protocol, - '--encapsulation-mode', encapsulation_mode, - '--lifetime', lifetime, - '--pfs', pfs] - - position_names = ['name', 'auth_algorithm', 'encryption_algorithm', - 'encapsulation_mode', 'description', - 'transform_protocol', 'pfs', - 'tenant_id'] - - position_values = [name, auth_algorithm, encryption_algorithm, - encapsulation_mode, description, - transform_protocol, pfs, - tenant_id] - extra_body = { - 'lifetime': { - 'units': 'seconds', - 'value': 20000, - }, - } - - if not expected_exc: - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values, - extra_body=extra_body) - else: - self.assertRaises( - expected_exc, - self._test_create_resource, - resource, cmd, name, my_id, args, - position_names, position_values, - extra_body=extra_body) - - def test_create_ipsecpolicy_all_params(self): - self._test_create_ipsecpolicy_all_params() - - def test_create_ipsecpolicy_auth_sha256(self): - self._test_create_ipsecpolicy_all_params(auth='sha256') - - def test_create_ipsecpolicy_invalid_auth(self): - self._test_create_ipsecpolicy_all_params(auth='xyz', - expected_exc=SystemExit) - - def test_create_ipsecpolicy_with_limited_params(self): - # vpn-ipsecpolicy-create with limited params. - resource = 'ipsecpolicy' - cmd = ipsecpolicy.CreateIPsecPolicy(test_cli20.MyApp(sys.stdout), None) - name = 'ipsecpolicy1' - auth_algorithm = 'sha1' - encryption_algorithm = 'aes-128' - encapsulation_mode = 'tunnel' - pfs = 'group5' - transform_protocol = 'esp' - tenant_id = 'my-tenant' - my_id = 'my-id' - - args = [name, - '--tenant-id', tenant_id] - - position_names = ['name', 'auth_algorithm', 'encryption_algorithm', - 'encapsulation_mode', - 'transform_protocol', 'pfs', - 'tenant_id'] - - position_values = [name, auth_algorithm, encryption_algorithm, - encapsulation_mode, - transform_protocol, pfs, - tenant_id] - - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values) - - def _test_lifetime_values(self, lifetime, expected_exc=None): - resource = 'ipsecpolicy' - cmd = ipsecpolicy.CreateIPsecPolicy(test_cli20.MyApp(sys.stdout), None) - name = 'ipsecpolicy1' - description = 'my-ipsec-policy' - auth_algorithm = 'sha1' - encryption_algorithm = 'aes-256' - ike_version = 'v1' - phase1_negotiation_mode = 'main' - pfs = 'group5' - tenant_id = 'my-tenant' - my_id = 'my-id' - - args = [name, - '--description', description, - '--tenant-id', tenant_id, - '--auth-algorithm', auth_algorithm, - '--encryption-algorithm', encryption_algorithm, - '--ike-version', ike_version, - '--phase1-negotiation-mode', phase1_negotiation_mode, - '--lifetime', lifetime, - '--pfs', pfs] - - position_names = ['name', 'description', - 'auth_algorithm', 'encryption_algorithm', - 'phase1_negotiation_mode', - 'ike_version', 'pfs', - 'tenant_id'] - - position_values = [name, description, - auth_algorithm, encryption_algorithm, - phase1_negotiation_mode, ike_version, pfs, - tenant_id] - if not expected_exc: - expected_exc = exceptions.CommandError - self.assertRaises( - expected_exc, - self._test_create_resource, - resource, cmd, name, my_id, args, - position_names, position_values) - - def test_create_ipsecpolicy_with_invalid_lifetime_keys(self): - lifetime = 'uts=seconds,val=20000' - self._test_lifetime_values(lifetime, SystemExit) - - def test_create_ipsecpolicy_with_invalid_lifetime_units(self): - lifetime = 'units=minutes,value=600' - self._test_lifetime_values(lifetime) - - def test_create_ipsecpolicy_with_invalid_lifetime_value(self): - lifetime = 'units=seconds,value=0' - self._test_lifetime_values(lifetime) - - def test_list_ipsecpolicy(self): - # vpn-ipsecpolicy-list. - resources = "ipsecpolicies" - cmd = ipsecpolicy.ListIPsecPolicy(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True) - - def test_list_ipsecpolicy_pagination(self): - # vpn-ipsecpolicy-list. - resources = "ipsecpolicies" - cmd = ipsecpolicy.ListIPsecPolicy(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_ipsecpolicy_sort(self): - # vpn-ipsecpolicy-list --sort-key name --sort-key id --sort-key asc - # --sort-key desc - resources = "ipsecpolicies" - cmd = ipsecpolicy.ListIPsecPolicy(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_ipsecpolicy_limit(self): - # vpn-ipsecpolicy-list -P. - resources = "ipsecpolicies" - cmd = ipsecpolicy.ListIPsecPolicy(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_show_ipsecpolicy_id(self): - # vpn-ipsecpolicy-show ipsecpolicy_id. - resource = 'ipsecpolicy' - cmd = ipsecpolicy.ShowIPsecPolicy(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id']) - - def test_show_ipsecpolicy_id_name(self): - # vpn-ipsecpolicy-show. - resource = 'ipsecpolicy' - cmd = ipsecpolicy.ShowIPsecPolicy(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name']) - - def test_update_ipsecpolicy(self): - # vpn-ipsecpolicy-update myid --name newname --tags a b. - resource = 'ipsecpolicy' - cmd = ipsecpolicy.UpdateIPsecPolicy(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'newname'], - {'name': 'newname', }) - - def test_delete_ipsecpolicy(self): - # vpn-ipsecpolicy-delete my-id. - resource = 'ipsecpolicy' - cmd = ipsecpolicy.DeleteIPsecPolicy(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - args = [my_id] - self._test_delete_resource(resource, cmd, my_id, args) diff --git a/neutronclient/tests/unit/vpn/test_cli20_vpnservice.py b/neutronclient/tests/unit/vpn/test_cli20_vpnservice.py deleted file mode 100644 index 9b28c7364..000000000 --- a/neutronclient/tests/unit/vpn/test_cli20_vpnservice.py +++ /dev/null @@ -1,153 +0,0 @@ -# (c) Copyright 2013 Hewlett-Packard Development Company, L.P. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import sys - -from neutronclient.neutron.v2_0.vpn import vpnservice -from neutronclient.tests.unit import test_cli20 - - -class CLITestV20VpnServiceJSON(test_cli20.CLITestV20Base): - - def test_create_vpnservice_all_params(self): - # vpn-service-create all params. - resource = 'vpnservice' - cmd = vpnservice.CreateVPNService(test_cli20.MyApp(sys.stdout), None) - subnet = 'mysubnet-id' - router = 'myrouter-id' - tenant_id = 'mytenant-id' - my_id = 'my-id' - name = 'myvpnservice' - description = 'my-vpn-service' - admin_state = True - - args = ['--name', name, - '--description', description, - router, - subnet, - '--tenant-id', tenant_id] - - position_names = ['admin_state_up', 'name', 'description', - 'subnet_id', 'router_id', - 'tenant_id'] - - position_values = [admin_state, name, description, - subnet, router, tenant_id] - - self._test_create_resource(resource, cmd, name, my_id, args, - position_names, position_values) - - def test_create_vpnservice_with_limited_params(self): - # vpn-service-create with limited params. - resource = 'vpnservice' - cmd = vpnservice.CreateVPNService(test_cli20.MyApp(sys.stdout), None) - subnet = 'mysubnet-id' - router = 'myrouter-id' - tenant_id = 'mytenant-id' - my_id = 'my-id' - admin_state = True - - args = [router, - subnet, - '--tenant-id', tenant_id] - - position_names = ['admin_state_up', - 'subnet_id', 'router_id', - 'tenant_id'] - - position_values = [admin_state, subnet, router, tenant_id] - - self._test_create_resource(resource, cmd, None, my_id, args, - position_names, position_values) - - def test_create_vpnservice_without_subnet(self): - # vpn-service-create with no subnet provided. - resource = 'vpnservice' - cmd = vpnservice.CreateVPNService(test_cli20.MyApp(sys.stdout), None) - router = 'myrouter-id' - tenant_id = 'mytenant-id' - my_id = 'my-id' - admin_state = True - - args = [router, - '--tenant-id', tenant_id] - - position_names = ['admin_state_up', - 'subnet_id', 'router_id', - 'tenant_id'] - - position_values = [admin_state, None, router, tenant_id] - - self._test_create_resource(resource, cmd, None, my_id, args, - position_names, position_values) - - def test_list_vpnservice(self): - # vpn-service-list. - resources = "vpnservices" - cmd = vpnservice.ListVPNService(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True) - - def test_list_vpnservice_pagination(self): - # vpn-service-list. - resources = "vpnservices" - cmd = vpnservice.ListVPNService(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources_with_pagination(resources, cmd) - - def test_list_vpnservice_sort(self): - # vpn-service-list --sort-key name --sort-key id --sort-key asc - # --sort-key desc - resources = "vpnservices" - cmd = vpnservice.ListVPNService(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - sort_key=["name", "id"], - sort_dir=["asc", "desc"]) - - def test_list_vpnservice_limit(self): - # vpn-service-list -P. - resources = "vpnservices" - cmd = vpnservice.ListVPNService(test_cli20.MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, page_size=1000) - - def test_show_vpnservice_id(self): - # vpn-service-show test_id. - resource = 'vpnservice' - cmd = vpnservice.ShowVPNService(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, args, ['id']) - - def test_show_vpnservice_id_name(self): - # vpn-service-show.""" - resource = 'vpnservice' - cmd = vpnservice.ShowVPNService(test_cli20.MyApp(sys.stdout), None) - args = ['--fields', 'id', '--fields', 'name', self.test_id] - self._test_show_resource(resource, cmd, self.test_id, - args, ['id', 'name']) - - def test_update_vpnservice(self): - # vpn-service-update myid --name newname --tags a b. - resource = 'vpnservice' - cmd = vpnservice.UpdateVPNService(test_cli20.MyApp(sys.stdout), None) - self._test_update_resource(resource, cmd, 'myid', - ['myid', '--name', 'newname'], - {'name': 'newname', }) - - def test_delete_vpnservice(self): - # vpn-service-delete my-id. - resource = 'vpnservice' - cmd = vpnservice.DeleteVPNService(test_cli20.MyApp(sys.stdout), None) - my_id = 'my-id' - args = [my_id] - self._test_delete_resource(resource, cmd, my_id, args) diff --git a/neutronclient/tests/unit/vpn/test_utils.py b/neutronclient/tests/unit/vpn/test_utils.py deleted file mode 100644 index c39f7ceb5..000000000 --- a/neutronclient/tests/unit/vpn/test_utils.py +++ /dev/null @@ -1,130 +0,0 @@ -# (c) Copyright 2013 Hewlett-Packard Development Company, L.P. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import testtools - -from neutronclient.common import exceptions -from neutronclient.common import utils -from neutronclient.neutron.v2_0.vpn import utils as vpn_utils - - -class TestVPNUtils(testtools.TestCase): - - def test_validate_lifetime_dictionary_seconds(self): - input_str = utils.str2dict("units=seconds,value=3600") - self.assertIsNone(vpn_utils.validate_lifetime_dict(input_str)) - - def test_validate_dpd_dictionary_action_hold(self): - input_str = utils.str2dict("action=hold,interval=30,timeout=120") - self.assertIsNone(vpn_utils.validate_dpd_dict(input_str)) - - def test_validate_dpd_dictionary_action_restart(self): - input_str = utils.str2dict("action=restart,interval=30,timeout=120") - self.assertIsNone(vpn_utils.validate_dpd_dict(input_str)) - - def test_validate_dpd_dictionary_action_restart_by_peer(self): - input_str = utils.str2dict( - "action=restart-by-peer,interval=30,timeout=120" - ) - self.assertIsNone(vpn_utils.validate_dpd_dict(input_str)) - - def test_validate_dpd_dictionary_action_clear(self): - input_str = utils.str2dict('action=clear,interval=30,timeout=120') - self.assertIsNone(vpn_utils.validate_dpd_dict(input_str)) - - def test_validate_dpd_dictionary_action_disabled(self): - input_str = utils.str2dict('action=disabled,interval=30,timeout=120') - self.assertIsNone(vpn_utils.validate_dpd_dict(input_str)) - - def test_validate_lifetime_dictionary_invalid_unit_key(self): - input_str = utils.str2dict('ut=seconds,value=3600') - self._test_validate_lifetime_negative_test_case(input_str) - - def test_validate_lifetime_dictionary_invalid_unit_key_value(self): - input_str = utils.str2dict('units=seconds,val=3600') - self._test_validate_lifetime_negative_test_case(input_str) - - def test_validate_lifetime_dictionary_unsupported_units(self): - input_str = utils.str2dict('units=minutes,value=3600') - self._test_validate_lifetime_negative_test_case(input_str) - - def test_validate_lifetime_dictionary_invalid_empty_unit(self): - input_str = utils.str2dict('units=,value=3600') - self._test_validate_lifetime_negative_test_case(input_str) - - def test_validate_lifetime_dictionary_under_minimum_integer_value(self): - input_str = utils.str2dict('units=seconds,value=59') - self._test_validate_lifetime_negative_test_case(input_str) - - def test_validate_lifetime_dictionary_negative_integer_value(self): - input_str = utils.str2dict('units=seconds,value=-1') - self._test_validate_lifetime_negative_test_case(input_str) - - def test_validate_lifetime_dictionary_empty_value(self): - input_str = utils.str2dict('units=seconds,value=') - self._test_validate_lifetime_negative_test_case(input_str) - - def test_validate_dpd_dictionary_invalid_key_action(self): - input_str = utils.str2dict('act=hold,interval=30,timeout=120') - self._test_validate_dpd_negative_test_case(input_str) - - def test_validate_dpd_dictionary_invalid_key_interval(self): - input_str = utils.str2dict('action=hold,int=30,timeout=120') - self._test_validate_dpd_negative_test_case(input_str) - - def test_validate_dpd_dictionary_invalid_key_timeout(self): - input_str = utils.str2dict('action=hold,interval=30,tiut=120') - self._test_validate_dpd_negative_test_case(input_str) - - def test_validate_dpd_dictionary_unsupported_action(self): - input_str = utils.str2dict('action=bye-bye,interval=30,timeout=120') - self._test_validate_dpd_negative_test_case(input_str) - - def test_validate_dpd_dictionary_empty_action(self): - input_str = utils.str2dict('action=,interval=30,timeout=120') - self._test_validate_dpd_negative_test_case(input_str) - - def test_validate_dpd_dictionary_empty_interval(self): - input_str = utils.str2dict('action=hold,interval=,timeout=120') - self._test_validate_dpd_negative_test_case(input_str) - - def test_validate_dpd_dictionary_negative_interval_value(self): - input_str = utils.str2dict('action=hold,interval=-1,timeout=120') - self._test_validate_lifetime_negative_test_case(input_str) - - def test_validate_dpd_dictionary_zero_timeout(self): - input_str = utils.str2dict('action=hold,interval=30,timeout=0') - self._test_validate_dpd_negative_test_case(input_str) - - def test_validate_dpd_dictionary_empty_timeout(self): - input_str = utils.str2dict('action=hold,interval=30,timeout=') - self._test_validate_dpd_negative_test_case(input_str) - - def test_validate_dpd_dictionary_negative_timeout_value(self): - input_str = utils.str2dict('action=hold,interval=30,timeout=-1') - self._test_validate_lifetime_negative_test_case(input_str) - - def _test_validate_lifetime_negative_test_case(self, input_str): - """Generic handler for negative lifetime tests.""" - self.assertRaises(exceptions.CommandError, - vpn_utils.validate_lifetime_dict, - (input_str)) - - def _test_validate_dpd_negative_test_case(self, input_str): - """Generic handler for negative lifetime tests.""" - self.assertRaises(exceptions.CommandError, - vpn_utils.validate_lifetime_dict, - (input_str)) diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index 9c48fe009..82676b1c1 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -1,5 +1,6 @@ # Copyright 2012 OpenStack Foundation. # Copyright 2015 Hewlett-Packard Development Company, L.P. +# Copyright 2017 FUJITSU LIMITED # All Rights Reserved # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -20,11 +21,11 @@ import logging import re import time +import urllib.parse as urlparse import debtcollector.renames +from keystoneauth1 import exceptions as ksa_exc import requests -import six.moves.urllib.parse as urlparse -from six import string_types from neutronclient._i18n import _ from neutronclient import client @@ -118,13 +119,6 @@ def _append_request_id(self, resp): # Extract 'x-openstack-request-id' from headers if # response is a Response object. request_id = resp.headers.get('x-openstack-request-id') - # log request-id for each api call - _logger.debug('%(method)s call to neutron for ' - '%(url)s used request id ' - '%(response_request_id)s', - {'method': resp.request.method, - 'url': resp.url, - 'response_request_id': request_id}) else: # If resp is of type string. request_id = resp @@ -222,6 +216,10 @@ class ClientBase(object): :param bool log_credentials: Allow for logging of passwords or not. Defaults to False. (optional) :param string ca_cert: SSL CA bundle file to use. (optional) + :param cert: A client certificate to pass to requests. These are of the + same form as requests expects. Either a single filename + containing both the certificate and key or a tuple containing + the path to the certificate then a path to the key. (optional) :param integer retries: How many times idempotent (GET, PUT, DELETE) requests to Neutron server should be retried if they fail (default: 0). @@ -252,11 +250,13 @@ class ClientBase(object): def __init__(self, **kwargs): """Initialize a new client for the Neutron v2.0 API.""" super(ClientBase, self).__init__() + _logger.warning("The python binding code in neutronclient is " + "deprecated in favor of OpenstackSDK, please use " + "that as this will be removed in a future release.") self.retries = kwargs.pop('retries', 0) self.raise_errors = kwargs.pop('raise_errors', True) self.httpclient = client.construct_http_client(**kwargs) self.version = '2.0' - self.format = 'json' self.action_prefix = "/v%s" % (self.version) self.retry_interval = 1 @@ -276,7 +276,6 @@ def _handle_fault_response(self, status_code, response_body, resp): def do_request(self, method, action, body=None, headers=None, params=None): # Add format and project_id - action += ".%s" % self.format action = self.action_prefix + action if isinstance(params, dict) and params: params = utils.safe_encode_dict(params) @@ -285,7 +284,8 @@ def do_request(self, method, action, body=None, headers=None, params=None): if body: body = self.serialize(body) - resp, replybody = self.httpclient.do_request(action, method, body=body) + resp, replybody = self.httpclient.do_request(action, method, body=body, + headers=headers) status_code = resp.status_code if status_code in (requests.codes.ok, @@ -318,7 +318,7 @@ def serialize(self, data): def deserialize(self, data, status_code): """Deserializes a JSON string into a dictionary.""" - if status_code == 204: + if not data: return data return serializer.Serializer().deserialize( data)['body'] @@ -335,7 +335,7 @@ def retry_request(self, method, action, body=None, try: return self.do_request(method, action, body=body, headers=headers, params=params) - except exceptions.ConnectionFailed: + except (exceptions.ConnectionFailed, ksa_exc.ConnectionError): # Exception has already been logged by do_request() if i < self.retries: _logger.debug('Retrying connection to Neutron service') @@ -404,7 +404,7 @@ def _convert_into_with_meta(self, item, resp): if item: if isinstance(item, dict): return _DictWithMeta(item, resp) - elif isinstance(item, string_types): + elif isinstance(item, str): return _StrWithMeta(item, resp) else: return _TupleWithMeta((), resp) @@ -498,8 +498,12 @@ class Client(ClientBase): network_path = "/networks/%s" ports_path = "/ports" port_path = "/ports/%s" + port_bindings_path = "/ports/%s/bindings" + port_binding_path = "/ports/%s/bindings/%s" + port_binding_path_activate = "/ports/%s/bindings/%s/activate" subnets_path = "/subnets" subnet_path = "/subnets/%s" + onboard_network_subnets_path = "/subnetpools/%s/onboard_network_subnets" subnetpools_path = "/subnetpools" subnetpool_path = "/subnetpools/%s" address_scopes_path = "/address-scopes" @@ -507,16 +511,33 @@ class Client(ClientBase): quotas_path = "/quotas" quota_path = "/quotas/%s" quota_default_path = "/quotas/%s/default" + quota_details_path = "/quotas/%s/details.json" extensions_path = "/extensions" extension_path = "/extensions/%s" routers_path = "/routers" router_path = "/routers/%s" floatingips_path = "/floatingips" floatingip_path = "/floatingips/%s" + port_forwardings_path = "/floatingips/%s/port_forwardings" + port_forwarding_path = "/floatingips/%s/port_forwardings/%s" security_groups_path = "/security-groups" security_group_path = "/security-groups/%s" security_group_rules_path = "/security-group-rules" security_group_rule_path = "/security-group-rules/%s" + segments_path = "/segments" + segment_path = "/segments/%s" + + sfc_flow_classifiers_path = "/sfc/flow_classifiers" + sfc_flow_classifier_path = "/sfc/flow_classifiers/%s" + sfc_port_pairs_path = "/sfc/port_pairs" + sfc_port_pair_path = "/sfc/port_pairs/%s" + sfc_port_pair_groups_path = "/sfc/port_pair_groups" + sfc_port_pair_group_path = "/sfc/port_pair_groups/%s" + sfc_port_chains_path = "/sfc/port_chains" + sfc_port_chain_path = "/sfc/port_chains/%s" + sfc_service_graphs_path = "/sfc/service_graphs" + sfc_service_graph_path = "/sfc/service_graphs/%s" + endpoint_groups_path = "/vpn/endpoint-groups" endpoint_group_path = "/vpn/endpoint-groups/%s" vpnservices_path = "/vpn/vpnservices" @@ -587,14 +608,36 @@ class Client(ClientBase): firewall_policy_remove_path = "/fw/firewall_policies/%s/remove_rule" firewalls_path = "/fw/firewalls" firewall_path = "/fw/firewalls/%s" + fwaas_firewall_groups_path = "/fwaas/firewall_groups" + fwaas_firewall_group_path = "/fwaas/firewall_groups/%s" + fwaas_firewall_rules_path = "/fwaas/firewall_rules" + fwaas_firewall_rule_path = "/fwaas/firewall_rules/%s" + fwaas_firewall_policies_path = "/fwaas/firewall_policies" + fwaas_firewall_policy_path = "/fwaas/firewall_policies/%s" + fwaas_firewall_policy_insert_path = \ + "/fwaas/firewall_policies/%s/insert_rule" + fwaas_firewall_policy_remove_path = \ + "/fwaas/firewall_policies/%s/remove_rule" rbac_policies_path = "/rbac-policies" rbac_policy_path = "/rbac-policies/%s" qos_policies_path = "/qos/policies" qos_policy_path = "/qos/policies/%s" qos_bandwidth_limit_rules_path = "/qos/policies/%s/bandwidth_limit_rules" qos_bandwidth_limit_rule_path = "/qos/policies/%s/bandwidth_limit_rules/%s" + qos_packet_rate_limit_rules_path = \ + "/qos/policies/%s/packet_rate_limit_rules" + qos_packet_rate_limit_rule_path = \ + "/qos/policies/%s/packet_rate_limit_rules/%s" qos_dscp_marking_rules_path = "/qos/policies/%s/dscp_marking_rules" qos_dscp_marking_rule_path = "/qos/policies/%s/dscp_marking_rules/%s" + qos_minimum_bandwidth_rules_path = \ + "/qos/policies/%s/minimum_bandwidth_rules" + qos_minimum_bandwidth_rule_path = \ + "/qos/policies/%s/minimum_bandwidth_rules/%s" + qos_minimum_packet_rate_rules_path = \ + "/qos/policies/%s/minimum_packet_rate_rules" + qos_minimum_packet_rate_rule_path = \ + "/qos/policies/%s/minimum_packet_rate_rules/%s" qos_rule_types_path = "/qos/rule-types" qos_rule_type_path = "/qos/rule-types/%s" flavors_path = "/flavors" @@ -622,6 +665,20 @@ class Client(ClientBase): subports_path = "/trunks/%s/get_subports" subports_add_path = "/trunks/%s/add_subports" subports_remove_path = "/trunks/%s/remove_subports" + bgpvpns_path = "/bgpvpn/bgpvpns" + bgpvpn_path = "/bgpvpn/bgpvpns/%s" + bgpvpn_network_associations_path =\ + "/bgpvpn/bgpvpns/%s/network_associations" + bgpvpn_network_association_path =\ + "/bgpvpn/bgpvpns/%s/network_associations/%s" + bgpvpn_router_associations_path = "/bgpvpn/bgpvpns/%s/router_associations" + bgpvpn_router_association_path =\ + "/bgpvpn/bgpvpns/%s/router_associations/%s" + bgpvpn_port_associations_path = "/bgpvpn/bgpvpns/%s/port_associations" + bgpvpn_port_association_path = "/bgpvpn/bgpvpns/%s/port_associations/%s" + network_logs_path = "/log/logs" + network_log_path = "/log/logs/%s" + network_loggables_path = "/log/loggable-resources" # API has no way to report plurals, so we have to hard code them EXTED_PLURALS = {'routers': 'router', @@ -630,6 +687,7 @@ class Client(ClientBase): 'service_definitions': 'service_definition', 'security_groups': 'security_group', 'security_group_rules': 'security_group_rule', + 'segments': 'segment', 'ipsecpolicies': 'ipsecpolicy', 'ikepolicies': 'ikepolicy', 'ipsec_site_connections': 'ipsec_site_connection', @@ -644,6 +702,9 @@ class Client(ClientBase): 'firewall_rules': 'firewall_rule', 'firewall_policies': 'firewall_policy', 'firewalls': 'firewall', + 'fwaas_firewall_rules': 'fwaas_firewall_rule', + 'fwaas_firewall_policies': 'fwaas_firewall_policy', + 'fwaas_firewall_groups': 'fwaas_firewall_group', 'metering_labels': 'metering_label', 'metering_label_rules': 'metering_label_rule', 'loadbalancers': 'loadbalancer', @@ -660,6 +721,9 @@ class Client(ClientBase): 'qos_policies': 'qos_policy', 'policies': 'policy', 'bandwidth_limit_rules': 'bandwidth_limit_rule', + 'packet_rate_limit_rules': 'packet_rate_limit_rule', + 'minimum_bandwidth_rules': 'minimum_bandwidth_rule', + 'minimum_packet_rate_rules': 'minimum_packet_rate_rule', 'rules': 'rule', 'dscp_marking_rules': 'dscp_marking_rule', 'rule_types': 'rule_type', @@ -668,6 +732,17 @@ class Client(ClientBase): 'bgp_peers': 'bgp_peer', 'network_ip_availabilities': 'network_ip_availability', 'trunks': 'trunk', + 'bgpvpns': 'bgpvpn', + 'network_associations': 'network_association', + 'router_associations': 'router_association', + 'port_associations': 'port_association', + 'flow_classifiers': 'flow_classifier', + 'port_pairs': 'port_pair', + 'port_pair_groups': 'port_pair_group', + 'port_chains': 'port_chain', + 'service_graphs': 'service_graph', + 'logs': 'log', + 'loggable_resources': 'loggable_resource', } def list_ext(self, collection, path, retrieve_all, **_params): @@ -704,6 +779,13 @@ def show_quota(self, project_id, **_params): """Fetch information of a certain project's quotas.""" return self.get(self.quota_path % (project_id), params=_params) + @debtcollector.renames.renamed_kwarg( + 'tenant_id', 'project_id', replace=True) + def show_quota_details(self, project_id, **_params): + """Fetch information of a certain project's quota details.""" + return self.get(self.quota_details_path % (project_id), + params=_params) + @debtcollector.renames.renamed_kwarg( 'tenant_id', 'project_id', replace=True) def show_quota_default(self, project_id, **_params): @@ -744,14 +826,37 @@ def create_port(self, body=None): """Creates a new port.""" return self.post(self.ports_path, body=body) - def update_port(self, port, body=None): + def update_port(self, port, body=None, revision_number=None): """Updates a port.""" - return self.put(self.port_path % (port), body=body) + return self._update_resource(self.port_path % (port), body=body, + revision_number=revision_number) def delete_port(self, port): """Deletes the specified port.""" return self.delete(self.port_path % (port)) + def create_port_binding(self, port_id, body=None): + """Creates a new port binding.""" + return self.post(self.port_bindings_path % port_id, body=body) + + def delete_port_binding(self, port_id, host_id): + """Deletes the specified port binding.""" + return self.delete(self.port_binding_path % (port_id, host_id)) + + def show_port_binding(self, port_id, host_id, **_params): + """Fetches information for a certain port binding.""" + return self.get(self.port_binding_path % (port_id, host_id), + params=_params) + + def list_port_bindings(self, port_id, retrieve_all=True, **_params): + """Fetches a list of all bindings for a certain port.""" + return self.list('bindings', self.port_bindings_path % port_id, + retrieve_all, **_params) + + def activate_port_binding(self, port_id, host_id): + """Activates a port binding.""" + return self.put(self.port_binding_path_activate % (port_id, host_id)) + def list_networks(self, retrieve_all=True, **_params): """Fetches a list of all networks for a project.""" # Pass filters in "params" argument to do_request @@ -766,9 +871,10 @@ def create_network(self, body=None): """Creates a new network.""" return self.post(self.networks_path, body=body) - def update_network(self, network, body=None): + def update_network(self, network, body=None, revision_number=None): """Updates a network.""" - return self.put(self.network_path % (network), body=body) + return self._update_resource(self.network_path % (network), body=body, + revision_number=revision_number) def delete_network(self, network): """Deletes the specified network.""" @@ -787,9 +893,10 @@ def create_subnet(self, body=None): """Creates a new subnet.""" return self.post(self.subnets_path, body=body) - def update_subnet(self, subnet, body=None): + def update_subnet(self, subnet, body=None, revision_number=None): """Updates a subnet.""" - return self.put(self.subnet_path % (subnet), body=body) + return self._update_resource(self.subnet_path % (subnet), body=body, + revision_number=revision_number) def delete_subnet(self, subnet): """Deletes the specified subnet.""" @@ -808,9 +915,11 @@ def create_subnetpool(self, body=None): """Creates a new subnetpool.""" return self.post(self.subnetpools_path, body=body) - def update_subnetpool(self, subnetpool, body=None): + def update_subnetpool(self, subnetpool, body=None, revision_number=None): """Updates a subnetpool.""" - return self.put(self.subnetpool_path % (subnetpool), body=body) + return self._update_resource(self.subnetpool_path % (subnetpool), + body=body, + revision_number=revision_number) def delete_subnetpool(self, subnetpool): """Deletes the specified subnetpool.""" @@ -830,9 +939,10 @@ def create_router(self, body=None): """Creates a new router.""" return self.post(self.routers_path, body=body) - def update_router(self, router, body=None): + def update_router(self, router, body=None, revision_number=None): """Updates a router.""" - return self.put(self.router_path % (router), body=body) + return self._update_resource(self.router_path % (router), body=body, + revision_number=revision_number) def delete_router(self, router): """Deletes the specified router.""" @@ -870,6 +980,16 @@ def remove_interface_router(self, router, body=None): return self.put((self.router_path % router) + "/remove_router_interface", body=body) + def add_extra_routes_to_router(self, router, body=None): + """Adds extra routes to the specified router.""" + return self.put((self.router_path % router) + "/add_extraroutes", + body=body) + + def remove_extra_routes_from_router(self, router, body=None): + """Removes extra routes from the specified router.""" + return self.put((self.router_path % router) + "/remove_extraroutes", + body=body) + def add_gateway_router(self, router, body=None): """Adds an external network gateway to the specified router.""" return self.put((self.router_path % router), @@ -894,22 +1014,52 @@ def create_floatingip(self, body=None): """Creates a new floatingip.""" return self.post(self.floatingips_path, body=body) - def update_floatingip(self, floatingip, body=None): + def update_floatingip(self, floatingip, body=None, revision_number=None): """Updates a floatingip.""" - return self.put(self.floatingip_path % (floatingip), body=body) + return self._update_resource(self.floatingip_path % (floatingip), + body=body, + revision_number=revision_number) def delete_floatingip(self, floatingip): """Deletes the specified floatingip.""" return self.delete(self.floatingip_path % (floatingip)) + def show_port_forwarding(self, floatingip, portforwarding): + """Fetches information of a certain portforwarding""" + return self.get(self.port_forwarding_path % (floatingip, + portforwarding)) + + def list_port_forwardings(self, floatingip, retrieve_all=True, **_params): + """Fetches a list of all portforwardings for a floatingip.""" + return self.list('port_forwardings', + self.port_forwardings_path % floatingip, retrieve_all, + **_params) + + def create_port_forwarding(self, floatingip, body=None): + """Creates a new portforwarding.""" + return self.post(self.port_forwardings_path % floatingip, body=body) + + def delete_port_forwarding(self, floatingip, portforwarding): + """Deletes the specified portforwarding.""" + return self.delete(self.port_forwarding_path % (floatingip, + portforwarding)) + + def update_port_forwarding(self, floatingip, portforwarding, body=None): + """Updates a portforwarding.""" + return self.put(self.port_forwarding_path % (floatingip, + portforwarding), + body=body) + def create_security_group(self, body=None): """Creates a new security group.""" return self.post(self.security_groups_path, body=body) - def update_security_group(self, security_group, body=None): + def update_security_group(self, security_group, body=None, + revision_number=None): """Updates a security group.""" - return self.put(self.security_group_path % - security_group, body=body) + return self._update_resource(self.security_group_path % + security_group, body=body, + revision_number=revision_number) def list_security_groups(self, retrieve_all=True, **_params): """Fetches a list of all security groups for a project.""" @@ -945,6 +1095,28 @@ def show_security_group_rule(self, security_group_rule, **_params): return self.get(self.security_group_rule_path % (security_group_rule), params=_params) + def create_segment(self, body=None): + """Creates a new segment.""" + return self.post(self.segments_path, body=body) + + def update_segment(self, segment, body=None, revision_number=None): + """Updates a segment.""" + return self._update_resource(self.segment_path % segment, body=body, + revision_number=revision_number) + + def list_segments(self, retrieve_all=True, **_params): + """Fetches a list of all segments for a project.""" + return self.list('segments', self.segments_path, retrieve_all, + **_params) + + def show_segment(self, segment, **_params): + """Fetches information of a certain segment.""" + return self.get(self.segment_path % segment, params=_params) + + def delete_segment(self, segment): + """Deletes the specified segment.""" + return self.delete(self.segment_path % segment) + def list_endpoint_groups(self, retrieve_all=True, **_params): """Fetches a list of all VPN endpoint groups for a project.""" return self.list('endpoint_groups', self.endpoint_groups_path, @@ -1435,7 +1607,7 @@ def list_dhcp_agent_hosting_networks(self, network, **_params): params=_params) def list_networks_on_dhcp_agent(self, dhcp_agent, **_params): - """Fetches a list of dhcp agents hosting a network.""" + """Fetches a list of networks hosted on a DHCP agent.""" return self.get((self.agent_path + self.DHCP_NETS) % dhcp_agent, params=_params) @@ -1455,7 +1627,7 @@ def list_l3_agent_hosting_routers(self, router, **_params): params=_params) def list_routers_on_l3_agent(self, l3_agent, **_params): - """Fetches a list of L3 agents hosting a router.""" + """Fetches a list of routers hosted on an L3 agent.""" return self.get((self.agent_path + self.L3_ROUTERS) % l3_agent, params=_params) @@ -1566,6 +1738,87 @@ def delete_firewall(self, firewall): """Deletes the specified firewall.""" return self.delete(self.firewall_path % (firewall)) + def list_fwaas_firewall_groups(self, retrieve_all=True, **_params): + """Fetches a list of all firewall groups for a project""" + return self.list('firewall_groups', self.fwaas_firewall_groups_path, + retrieve_all, **_params) + + def show_fwaas_firewall_group(self, fwg, **_params): + """Fetches information of a certain firewall group""" + return self.get(self.fwaas_firewall_group_path % (fwg), params=_params) + + def create_fwaas_firewall_group(self, body=None): + """Creates a new firewall group""" + return self.post(self.fwaas_firewall_groups_path, body=body) + + def update_fwaas_firewall_group(self, fwg, body=None): + """Updates a firewall group""" + return self.put(self.fwaas_firewall_group_path % (fwg), body=body) + + def delete_fwaas_firewall_group(self, fwg): + """Deletes the specified firewall group""" + return self.delete(self.fwaas_firewall_group_path % (fwg)) + + def list_fwaas_firewall_rules(self, retrieve_all=True, **_params): + """Fetches a list of all firewall rules for a project""" + # Pass filters in "params" argument to do_request + return self.list('firewall_rules', self.fwaas_firewall_rules_path, + retrieve_all, **_params) + + def show_fwaas_firewall_rule(self, firewall_rule, **_params): + """Fetches information of a certain firewall rule""" + return self.get(self.fwaas_firewall_rule_path % (firewall_rule), + params=_params) + + def create_fwaas_firewall_rule(self, body=None): + """Creates a new firewall rule""" + return self.post(self.fwaas_firewall_rules_path, body=body) + + def update_fwaas_firewall_rule(self, firewall_rule, body=None): + """Updates a firewall rule""" + return self.put(self.fwaas_firewall_rule_path % (firewall_rule), + body=body) + + def delete_fwaas_firewall_rule(self, firewall_rule): + """Deletes the specified firewall rule""" + return self.delete(self.fwaas_firewall_rule_path % (firewall_rule)) + + def list_fwaas_firewall_policies(self, retrieve_all=True, **_params): + """Fetches a list of all firewall policies for a project""" + # Pass filters in "params" argument to do_request + + return self.list('firewall_policies', + self.fwaas_firewall_policies_path, + retrieve_all, **_params) + + def show_fwaas_firewall_policy(self, firewall_policy, **_params): + """Fetches information of a certain firewall policy""" + return self.get(self.fwaas_firewall_policy_path % (firewall_policy), + params=_params) + + def create_fwaas_firewall_policy(self, body=None): + """Creates a new firewall policy""" + return self.post(self.fwaas_firewall_policies_path, body=body) + + def update_fwaas_firewall_policy(self, firewall_policy, body=None): + """Updates a firewall policy""" + return self.put(self.fwaas_firewall_policy_path % (firewall_policy), + body=body) + + def delete_fwaas_firewall_policy(self, firewall_policy): + """Deletes the specified firewall policy""" + return self.delete(self.fwaas_firewall_policy_path % (firewall_policy)) + + def insert_rule_fwaas_firewall_policy(self, firewall_policy, body=None): + """Inserts specified rule into firewall policy""" + return self.put((self.fwaas_firewall_policy_insert_path % + (firewall_policy)), body=body) + + def remove_rule_fwaas_firewall_policy(self, firewall_policy, body=None): + """Removes specified rule from firewall policy""" + return self.put((self.fwaas_firewall_policy_remove_path % + (firewall_policy)), body=body) + def remove_router_from_l3_agent(self, l3_agent, router_id): """Remove a router from l3 agent.""" return self.delete((self.agent_path + self.L3_ROUTERS + "/%s") % ( @@ -1672,10 +1925,11 @@ def create_qos_policy(self, body=None): """Creates a new qos policy.""" return self.post(self.qos_policies_path, body=body) - def update_qos_policy(self, qos_policy, body=None): + def update_qos_policy(self, qos_policy, body=None, revision_number=None): """Updates a qos policy.""" - return self.put(self.qos_policy_path % qos_policy, - body=body) + return self._update_resource(self.qos_policy_path % qos_policy, + body=body, + revision_number=revision_number) def delete_qos_policy(self, qos_policy): """Deletes the specified qos policy.""" @@ -1693,10 +1947,10 @@ def list_bandwidth_limit_rules(self, policy_id, self.qos_bandwidth_limit_rules_path % policy_id, retrieve_all, **_params) - def show_bandwidth_limit_rule(self, rule, policy, body=None): + def show_bandwidth_limit_rule(self, rule, policy, **_params): """Fetches information of a certain bandwidth limit rule.""" return self.get(self.qos_bandwidth_limit_rule_path % - (policy, rule), body=body) + (policy, rule), params=_params) def create_bandwidth_limit_rule(self, policy, body=None): """Creates a new bandwidth limit rule.""" @@ -1720,10 +1974,10 @@ def list_dscp_marking_rules(self, policy_id, self.qos_dscp_marking_rules_path % policy_id, retrieve_all, **_params) - def show_dscp_marking_rule(self, rule, policy, body=None): + def show_dscp_marking_rule(self, rule, policy, **_params): """Shows information of a certain DSCP marking rule.""" return self.get(self.qos_dscp_marking_rule_path % - (policy, rule), body=body) + (policy, rule), params=_params) def create_dscp_marking_rule(self, policy, body=None): """Creates a new DSCP marking rule.""" @@ -1740,6 +1994,93 @@ def delete_dscp_marking_rule(self, rule, policy): return self.delete(self.qos_dscp_marking_rule_path % (policy, rule)) + def list_minimum_bandwidth_rules(self, policy_id, retrieve_all=True, + **_params): + """Fetches a list of all minimum bandwidth rules for the given policy. + + """ + return self.list('minimum_bandwidth_rules', + self.qos_minimum_bandwidth_rules_path % + policy_id, retrieve_all, **_params) + + def show_minimum_bandwidth_rule(self, rule, policy, body=None): + """Fetches information of a certain minimum bandwidth rule.""" + return self.get(self.qos_minimum_bandwidth_rule_path % + (policy, rule), body=body) + + def create_minimum_bandwidth_rule(self, policy, body=None): + """Creates a new minimum bandwidth rule.""" + return self.post(self.qos_minimum_bandwidth_rules_path % policy, + body=body) + + def list_packet_rate_limit_rules(self, policy_id, retrieve_all=True, + **_params): + """Fetches a list of all packet rate limit rules for the given policy + + """ + return self.list('packet_rate_limit_rules', + self.qos_packet_rate_limit_rules_path % + policy_id, retrieve_all, **_params) + + def show_packet_rate_limit_rule(self, rule, policy, body=None): + """Fetches information of a certain packet rate limit rule.""" + return self.get(self.qos_packet_rate_limit_rule_path % + (policy, rule), body=body) + + def create_packet_rate_limit_rule(self, policy, body=None): + """Creates a new packet rate limit rule.""" + return self.post(self.qos_packet_rate_limit_rules_path % policy, + body=body) + + def update_packet_rate_limit_rule(self, rule, policy, body=None): + """Updates a packet rate limit rule.""" + return self.put(self.qos_packet_rate_limit_rule_path % + (policy, rule), body=body) + + def delete_packet_rate_limit_rule(self, rule, policy): + """Deletes a packet rate limit rule.""" + return self.delete(self.qos_packet_rate_limit_rule_path % + (policy, rule)) + + def update_minimum_bandwidth_rule(self, rule, policy, body=None): + """Updates a minimum bandwidth rule.""" + return self.put(self.qos_minimum_bandwidth_rule_path % + (policy, rule), body=body) + + def delete_minimum_bandwidth_rule(self, rule, policy): + """Deletes a minimum bandwidth rule.""" + return self.delete(self.qos_minimum_bandwidth_rule_path % + (policy, rule)) + + def list_minimum_packet_rate_rules(self, policy_id, retrieve_all=True, + **_params): + """Fetches a list of all minimum packet rate rules for the given policy + + """ + return self.list('minimum_packet_rate_rules', + self.qos_minimum_packet_rate_rules_path % + policy_id, retrieve_all, **_params) + + def show_minimum_packet_rate_rule(self, rule, policy, body=None): + """Fetches information of a certain minimum packet rate rule.""" + return self.get(self.qos_minimum_packet_rate_rule_path % + (policy, rule), body=body) + + def create_minimum_packet_rate_rule(self, policy, body=None): + """Creates a new minimum packet rate rule.""" + return self.post(self.qos_minimum_packet_rate_rules_path % policy, + body=body) + + def update_minimum_packet_rate_rule(self, rule, policy, body=None): + """Updates a minimum packet rate rule.""" + return self.put(self.qos_minimum_packet_rate_rule_path % + (policy, rule), body=body) + + def delete_minimum_packet_rate_rule(self, rule, policy): + """Deletes a minimum packet rate rule.""" + return self.delete(self.qos_minimum_packet_rate_rule_path % + (policy, rule)) + def create_flavor(self, body=None): """Creates a new Neutron service flavor.""" return self.post(self.flavors_path, body=body) @@ -1807,6 +2148,14 @@ def get_auto_allocated_topology(self, project_id, **_params): self.auto_allocated_topology_path % project_id, params=_params) + @debtcollector.renames.renamed_kwarg( + 'tenant_id', 'project_id', replace=True) + def delete_auto_allocated_topology(self, project_id, **_params): + """Delete a project's auto-allocated topology.""" + return self.delete( + self.auto_allocated_topology_path % project_id, + params=_params) + @debtcollector.renames.renamed_kwarg( 'tenant_id', 'project_id', replace=True) def validate_auto_allocated_topology_requirements(self, project_id): @@ -1882,7 +2231,7 @@ def delete_bgp_peer(self, peer_id): return self.delete(self.bgp_peer_path % peer_id) def list_network_ip_availabilities(self, retrieve_all=True, **_params): - """Fetches IP availibility information for all networks""" + """Fetches IP availability information for all networks""" return self.list('network_ip_availabilities', self.network_ip_availabilities_path, retrieve_all, **_params) @@ -1912,9 +2261,10 @@ def create_trunk(self, body=None): """Create a trunk port.""" return self.post(self.trunks_path, body=body) - def update_trunk(self, trunk, body=None): + def update_trunk(self, trunk, body=None, revision_number=None): """Update a trunk port.""" - return self.put(self.trunk_path % trunk, body=body) + return self._update_resource(self.trunk_path % trunk, body=body, + revision_number=revision_number) def delete_trunk(self, trunk): """Delete a trunk port.""" @@ -1941,11 +2291,265 @@ def trunk_get_subports(self, trunk, **_params): """Fetch a list of all subports attached to given trunk.""" return self.get(self.subports_path % (trunk), params=_params) + def list_bgpvpns(self, retrieve_all=True, **_params): + """Fetches a list of all BGP VPNs for a project""" + return self.list('bgpvpns', self.bgpvpns_path, retrieve_all, **_params) + + def show_bgpvpn(self, bgpvpn, **_params): + """Fetches information of a certain BGP VPN""" + return self.get(self.bgpvpn_path % bgpvpn, params=_params) + + def create_bgpvpn(self, body=None): + """Creates a new BGP VPN""" + return self.post(self.bgpvpns_path, body=body) + + def update_bgpvpn(self, bgpvpn, body=None): + """Updates a BGP VPN""" + return self.put(self.bgpvpn_path % bgpvpn, body=body) + + def delete_bgpvpn(self, bgpvpn): + """Deletes the specified BGP VPN""" + return self.delete(self.bgpvpn_path % bgpvpn) + + def list_bgpvpn_network_assocs(self, bgpvpn, retrieve_all=True, **_params): + """Fetches a list of network associations for a given BGP VPN.""" + return self.list('network_associations', + self.bgpvpn_network_associations_path % bgpvpn, + retrieve_all, **_params) + + def show_bgpvpn_network_assoc(self, bgpvpn, net_assoc, **_params): + """Fetches information of a certain BGP VPN's network association""" + return self.get( + self.bgpvpn_network_association_path % (bgpvpn, net_assoc), + params=_params) + + def create_bgpvpn_network_assoc(self, bgpvpn, body=None): + """Creates a new BGP VPN network association""" + return self.post(self.bgpvpn_network_associations_path % bgpvpn, + body=body) + + def update_bgpvpn_network_assoc(self, bgpvpn, net_assoc, body=None): + """Updates a BGP VPN network association""" + return self.put( + self.bgpvpn_network_association_path % (bgpvpn, net_assoc), + body=body) + + def delete_bgpvpn_network_assoc(self, bgpvpn, net_assoc): + """Deletes the specified BGP VPN network association""" + return self.delete( + self.bgpvpn_network_association_path % (bgpvpn, net_assoc)) + + def list_bgpvpn_router_assocs(self, bgpvpn, retrieve_all=True, **_params): + """Fetches a list of router associations for a given BGP VPN.""" + return self.list('router_associations', + self.bgpvpn_router_associations_path % bgpvpn, + retrieve_all, **_params) + + def show_bgpvpn_router_assoc(self, bgpvpn, router_assoc, **_params): + """Fetches information of a certain BGP VPN's router association""" + return self.get( + self.bgpvpn_router_association_path % (bgpvpn, router_assoc), + params=_params) + + def create_bgpvpn_router_assoc(self, bgpvpn, body=None): + """Creates a new BGP VPN router association""" + return self.post(self.bgpvpn_router_associations_path % bgpvpn, + body=body) + + def update_bgpvpn_router_assoc(self, bgpvpn, router_assoc, body=None): + """Updates a BGP VPN router association""" + return self.put( + self.bgpvpn_router_association_path % (bgpvpn, router_assoc), + body=body) + + def delete_bgpvpn_router_assoc(self, bgpvpn, router_assoc): + """Deletes the specified BGP VPN router association""" + return self.delete( + self.bgpvpn_router_association_path % (bgpvpn, router_assoc)) + + def list_bgpvpn_port_assocs(self, bgpvpn, retrieve_all=True, **_params): + """Fetches a list of port associations for a given BGP VPN.""" + return self.list('port_associations', + self.bgpvpn_port_associations_path % bgpvpn, + retrieve_all, **_params) + + def show_bgpvpn_port_assoc(self, bgpvpn, port_assoc, **_params): + """Fetches information of a certain BGP VPN's port association""" + return self.get( + self.bgpvpn_port_association_path % (bgpvpn, port_assoc), + params=_params) + + def create_bgpvpn_port_assoc(self, bgpvpn, body=None): + """Creates a new BGP VPN port association""" + return self.post(self.bgpvpn_port_associations_path % bgpvpn, + body=body) + + def update_bgpvpn_port_assoc(self, bgpvpn, port_assoc, body=None): + """Updates a BGP VPN port association""" + return self.put( + self.bgpvpn_port_association_path % (bgpvpn, port_assoc), + body=body) + + def delete_bgpvpn_port_assoc(self, bgpvpn, port_assoc): + """Deletes the specified BGP VPN port association""" + return self.delete( + self.bgpvpn_port_association_path % (bgpvpn, port_assoc)) + + def create_sfc_port_pair(self, body=None): + """Creates a new Port Pair.""" + return self.post(self.sfc_port_pairs_path, body=body) + + def update_sfc_port_pair(self, port_pair, body=None): + """Update a Port Pair.""" + return self.put(self.sfc_port_pair_path % port_pair, body=body) + + def delete_sfc_port_pair(self, port_pair): + """Deletes the specified Port Pair.""" + return self.delete(self.sfc_port_pair_path % (port_pair)) + + def list_sfc_port_pairs(self, retrieve_all=True, **_params): + """Fetches a list of all Port Pairs.""" + return self.list('port_pairs', self.sfc_port_pairs_path, retrieve_all, + **_params) + + def show_sfc_port_pair(self, port_pair, **_params): + """Fetches information of a certain Port Pair.""" + return self.get(self.sfc_port_pair_path % (port_pair), params=_params) + + def create_sfc_port_pair_group(self, body=None): + """Creates a new Port Pair Group.""" + return self.post(self.sfc_port_pair_groups_path, body=body) + + def update_sfc_port_pair_group(self, port_pair_group, body=None): + """Update a Port Pair Group.""" + return self.put(self.sfc_port_pair_group_path % port_pair_group, + body=body) + + def delete_sfc_port_pair_group(self, port_pair_group): + """Deletes the specified Port Pair Group.""" + return self.delete(self.sfc_port_pair_group_path % (port_pair_group)) + + def list_sfc_port_pair_groups(self, retrieve_all=True, **_params): + """Fetches a list of all Port Pair Groups.""" + return self.list('port_pair_groups', self.sfc_port_pair_groups_path, + retrieve_all, **_params) + + def show_sfc_port_pair_group(self, port_pair_group, **_params): + """Fetches information of a certain Port Pair Group.""" + return self.get(self.sfc_port_pair_group_path % (port_pair_group), + params=_params) + + def create_sfc_port_chain(self, body=None): + """Creates a new Port Chain.""" + return self.post(self.sfc_port_chains_path, body=body) + + def update_sfc_port_chain(self, port_chain, body=None): + """Update a Port Chain.""" + return self.put(self.sfc_port_chain_path % port_chain, body=body) + + def delete_sfc_port_chain(self, port_chain): + """Deletes the specified Port Chain.""" + return self.delete(self.sfc_port_chain_path % (port_chain)) + + def list_sfc_port_chains(self, retrieve_all=True, **_params): + """Fetches a list of all Port Chains.""" + return self.list('port_chains', self.sfc_port_chains_path, + retrieve_all, **_params) + + def show_sfc_port_chain(self, port_chain, **_params): + """Fetches information of a certain Port Chain.""" + return self.get(self.sfc_port_chain_path % (port_chain), + params=_params) + + def create_sfc_flow_classifier(self, body=None): + """Creates a new Flow Classifier.""" + return self.post(self.sfc_flow_classifiers_path, body=body) + + def update_sfc_flow_classifier(self, flow_classifier, body=None): + """Update a Flow Classifier.""" + return self.put(self.sfc_flow_classifier_path % flow_classifier, + body=body) + + def delete_sfc_flow_classifier(self, flow_classifier): + """Deletes the specified Flow Classifier.""" + return self.delete(self.sfc_flow_classifier_path % (flow_classifier)) + + def list_sfc_flow_classifiers(self, retrieve_all=True, **_params): + """Fetches a list of all Flow Classifiers.""" + return self.list('flow_classifiers', self.sfc_flow_classifiers_path, + retrieve_all, **_params) + + def show_sfc_flow_classifier(self, flow_classifier, **_params): + """Fetches information of a certain Flow Classifier.""" + return self.get(self.sfc_flow_classifier_path % (flow_classifier), + params=_params) + + def create_sfc_service_graph(self, body=None): + """Create the specified Service Graph.""" + return self.post(self.sfc_service_graphs_path, body=body) + + def update_sfc_service_graph(self, service_graph, body=None): + """Update a Service Graph.""" + return self.put(self.sfc_service_graph_path % service_graph, + body=body) + + def delete_sfc_service_graph(self, service_graph): + """Deletes the specified Service Graph.""" + return self.delete(self.sfc_service_graph_path % service_graph) + + def list_sfc_service_graphs(self, retrieve_all=True, **_params): + """Fetches a list of all Service Graphs.""" + return self.list('service_graphs', self.sfc_service_graphs_path, + retrieve_all, **_params) + + def show_sfc_service_graph(self, service_graph, **_params): + """Fetches information of a certain Service Graph.""" + return self.get(self.sfc_service_graph_path % service_graph, + params=_params) + + def create_network_log(self, body=None): + """Create a network log.""" + return self.post(self.network_logs_path, body=body) + + def delete_network_log(self, net_log): + """Delete a network log.""" + return self.delete(self.network_log_path % net_log) + + def list_network_logs(self, retrieve_all=True, **_params): + """Fetch a list of all network logs.""" + return self.list( + 'logs', self.network_logs_path, retrieve_all, **_params) + + def show_network_log(self, net_log, **_params): + """Fetch information for a certain network log.""" + return self.get(self.network_log_path % net_log, params=_params) + + def update_network_log(self, net_log, body=None): + """Update a network log.""" + return self.put(self.network_log_path % net_log, body=body) + + def list_network_loggable_resources(self, retrieve_all=True, **_params): + """Fetch a list of supported resource types for network log.""" + return self.list('loggable_resources', self.network_loggables_path, + retrieve_all, **_params) + + def onboard_network_subnets(self, subnetpool, body=None): + """Onboard the specified network's subnets into a subnet pool.""" + return self.put(self.onboard_network_subnets_path % (subnetpool), + body=body) + def __init__(self, **kwargs): """Initialize a new client for the Neutron v2.0 API.""" super(Client, self).__init__(**kwargs) self._register_extensions(self.version) + def _update_resource(self, path, **kwargs): + revision_number = kwargs.pop('revision_number', None) + if revision_number: + headers = kwargs.setdefault('headers', {}) + headers['If-Match'] = 'revision_number=%s' % revision_number + return self.put(path, **kwargs) + def extend_show(self, resource_singular, path, parent_resource): def _fx(obj, **_params): return self.show_ext(path, obj, **_params) diff --git a/releasenotes/notes/Define-IpAddressAlreadyAllocatedClient-exception-e8600ca5ba1c7f45.yaml b/releasenotes/notes/Define-IpAddressAlreadyAllocatedClient-exception-e8600ca5ba1c7f45.yaml new file mode 100644 index 000000000..4182585e0 --- /dev/null +++ b/releasenotes/notes/Define-IpAddressAlreadyAllocatedClient-exception-e8600ca5ba1c7f45.yaml @@ -0,0 +1,6 @@ +--- +other: + - | + Define a new exception type ``IpAddressAlreadyAllocatedClient``. + Users can catch this specific exception instead of the generic + ``NeutronClientException``. diff --git a/releasenotes/notes/add-aggressive-negotiation-mode-5218b1baff930eb8.yaml b/releasenotes/notes/add-aggressive-negotiation-mode-5218b1baff930eb8.yaml new file mode 100644 index 000000000..ae1b6672f --- /dev/null +++ b/releasenotes/notes/add-aggressive-negotiation-mode-5218b1baff930eb8.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + The ``--phase1-negotiation-mode`` option supports ``aggressive`` mode + in VPNaaS ikepolicy commands. diff --git a/releasenotes/notes/add-auto-allocated-topology-delete-aaccd60bd0f2e7b2.yaml b/releasenotes/notes/add-auto-allocated-topology-delete-aaccd60bd0f2e7b2.yaml new file mode 100644 index 000000000..84c395236 --- /dev/null +++ b/releasenotes/notes/add-auto-allocated-topology-delete-aaccd60bd0f2e7b2.yaml @@ -0,0 +1,4 @@ +--- +features: + - The ``auto-allocated-topology-delete`` command allows users to + delete the auto allocated topology. diff --git a/releasenotes/notes/add-new-session-clear-option-3c0b78ebc133a10c.yaml b/releasenotes/notes/add-new-session-clear-option-3c0b78ebc133a10c.yaml new file mode 100644 index 000000000..2782efe4f --- /dev/null +++ b/releasenotes/notes/add-new-session-clear-option-3c0b78ebc133a10c.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + A new option ``--no-session-persistence`` has been added to + the ``neutron lbaas-pool-update`` CLI to clear the session persistence + with which the current pool is associated. diff --git a/releasenotes/notes/add-osc-dynamic-routing-support-11130b2f440c0ac2.yaml b/releasenotes/notes/add-osc-dynamic-routing-support-11130b2f440c0ac2.yaml new file mode 100644 index 000000000..58cd1ae30 --- /dev/null +++ b/releasenotes/notes/add-osc-dynamic-routing-support-11130b2f440c0ac2.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Add OSC plugin to support "Neutron Dynamic Routing" diff --git a/releasenotes/notes/add-service-graph-ce4a25b3e32d70a6.yaml b/releasenotes/notes/add-service-graph-ce4a25b3e32d70a6.yaml new file mode 100644 index 000000000..f0b52abe7 --- /dev/null +++ b/releasenotes/notes/add-service-graph-ce4a25b3e32d70a6.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Added support for SFC Service Graph resource. + Related RFE: https://bugs.launchpad.net/networking-sfc/+bug/1587486. diff --git a/releasenotes/notes/add-sfc-commands.yaml b/releasenotes/notes/add-sfc-commands.yaml new file mode 100644 index 000000000..c081f50f9 --- /dev/null +++ b/releasenotes/notes/add-sfc-commands.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add OSC plugin support for the “Networking Service Function Chaining” feature commands along with client bindings. + [Blueprint `openstackclient-cli-porting `_] \ No newline at end of file diff --git a/releasenotes/notes/add-subnet-onboard-e60772bc4984f698.yaml b/releasenotes/notes/add-subnet-onboard-e60772bc4984f698.yaml new file mode 100644 index 000000000..358fdf006 --- /dev/null +++ b/releasenotes/notes/add-subnet-onboard-e60772bc4984f698.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add ``network onboard subnets`` OSC command to enable subnet onboard support from the CLI + [Blueprint `subnet-onboard `_] diff --git a/releasenotes/notes/add-support-to-floating-ip-port-forwardings-9dc838a5c5727eb7.yaml b/releasenotes/notes/add-support-to-floating-ip-port-forwardings-9dc838a5c5727eb7.yaml new file mode 100644 index 000000000..3ba168ec9 --- /dev/null +++ b/releasenotes/notes/add-support-to-floating-ip-port-forwardings-9dc838a5c5727eb7.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Add support to floating ip port forwarding. \ No newline at end of file diff --git a/releasenotes/notes/bug-1676922-81341b70bc6f055a.yaml b/releasenotes/notes/bug-1676922-81341b70bc6f055a.yaml new file mode 100644 index 000000000..9dc6aef41 --- /dev/null +++ b/releasenotes/notes/bug-1676922-81341b70bc6f055a.yaml @@ -0,0 +1,19 @@ +--- +deprecations: + - | + The ``--public`` and ``--private`` attribute of Firewall-as-a-Service v2 + have been deprecated. While the ``--public`` attribute will now be replaced + by ``--share``, the ``--private`` attribute will be replaced by + ``--no-share``. This is because of the similarity between the behavior of + ``--public`` attribute in FireWall-as-a-Service and the ``--share`` + attribute used in OpenStack. This deprecation affects the following CLIs. + + * openstack firewall group create + * openstack firewall group set + * openstack firewall group unset + * openstack firewall policy create + * openstack firewall policy set + * openstack firewall policy unset + * openstack firewall rule create + * openstack firewall rule set + * openstack firewall rule unset diff --git a/releasenotes/notes/deprecate-bgp-speaker-show-dragents-2fcce99cf6bb5b60.yaml b/releasenotes/notes/deprecate-bgp-speaker-show-dragents-2fcce99cf6bb5b60.yaml new file mode 100644 index 000000000..6233c6f4f --- /dev/null +++ b/releasenotes/notes/deprecate-bgp-speaker-show-dragents-2fcce99cf6bb5b60.yaml @@ -0,0 +1,10 @@ +--- +deprecations: + - | + The ``openstack bgp speaker show dragents`` CLI is deprecated and + will be removed in the future. Use ``openstack bgp dragent list + --bgp-speaker `` CLI instead. +features: + - | + The ``openstack bgp dragent list`` CLI is added to support showing + the list of dynamic routing agents. diff --git a/releasenotes/notes/deprecate-cli-7be1123817969439.yaml b/releasenotes/notes/deprecate-cli-7be1123817969439.yaml new file mode 100644 index 000000000..4833ddda6 --- /dev/null +++ b/releasenotes/notes/deprecate-cli-7be1123817969439.yaml @@ -0,0 +1,8 @@ +--- +deprecations: + - The neutron CLI is now deprecated. This is the signal that it is + time to start using the openstack CLI. No new features will be + added to the neutron CLI, though fixes to the CLI will be assessed + on a case by case basis. Fixes to the API bindings, as well as + development of new API bindings as well as OSC plugins are exempt + from this deprecation. diff --git a/releasenotes/notes/drop-python-2.7-f615ebae463b2143.yaml b/releasenotes/notes/drop-python-2.7-f615ebae463b2143.yaml new file mode 100644 index 000000000..6fb4d22ab --- /dev/null +++ b/releasenotes/notes/drop-python-2.7-f615ebae463b2143.yaml @@ -0,0 +1,5 @@ +--- +upgrade: + - | + Python 2.7 support has been dropped. The minimum version of Python now + supported by python-neutronclient is Python 3.6. diff --git a/releasenotes/notes/drop-python-3-6-and-3-7-73767fa0bbe89a6e.yaml b/releasenotes/notes/drop-python-3-6-and-3-7-73767fa0bbe89a6e.yaml new file mode 100644 index 000000000..db420d739 --- /dev/null +++ b/releasenotes/notes/drop-python-3-6-and-3-7-73767fa0bbe89a6e.yaml @@ -0,0 +1,5 @@ +--- +upgrade: + - | + Python 3.6 & 3.7 support has been dropped. The minimum version of Python now + supported is Python 3.8. diff --git a/releasenotes/notes/extraroute-atomic-b11919d8e33b0d92.yaml b/releasenotes/notes/extraroute-atomic-b11919d8e33b0d92.yaml new file mode 100644 index 000000000..fed4a1e10 --- /dev/null +++ b/releasenotes/notes/extraroute-atomic-b11919d8e33b0d92.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + New client methods: ``add_extra_routes_to_router`` and + ``remove_extra_routes_from_router``. diff --git a/releasenotes/notes/fix-quota-update-zero-args-d596c4169c2d2e30.yaml b/releasenotes/notes/fix-quota-update-zero-args-d596c4169c2d2e30.yaml new file mode 100644 index 000000000..eb70752a3 --- /dev/null +++ b/releasenotes/notes/fix-quota-update-zero-args-d596c4169c2d2e30.yaml @@ -0,0 +1,9 @@ +--- +fixes: + - | + Fix CLI quota-update to return an error message for no args + + * ``quota-update`` CLI will return an error message + ``Must specify a valid resource with new quota value`` if no + argument is provided while executing it. If arguments are + provided with CLI, no existing behavior is changed. diff --git a/releasenotes/notes/fix-rbac-create-command-dd40a474f0f092db.yaml b/releasenotes/notes/fix-rbac-create-command-dd40a474f0f092db.yaml new file mode 100644 index 000000000..7ae438abf --- /dev/null +++ b/releasenotes/notes/fix-rbac-create-command-dd40a474f0f092db.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - Fix 'bug 1596750 ' + that using 'rbac-create' without specifying 'target-tenant' will + return 'Request Failed internal server error while processing your + request'. + Update the default value of the argument '--target-tenant' to '*' diff --git a/releasenotes/notes/global_request_id-56856a93b982a6b3.yaml b/releasenotes/notes/global_request_id-56856a93b982a6b3.yaml new file mode 100644 index 000000000..168987613 --- /dev/null +++ b/releasenotes/notes/global_request_id-56856a93b982a6b3.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Adds a new ``global_request_id`` parameter to the Client + constructors, which will pass that id on all requests as the + ``X-OpenStack-Request-ID`` header. diff --git a/releasenotes/notes/minimum-packet-rate-34576b8fd98a3034.yaml b/releasenotes/notes/minimum-packet-rate-34576b8fd98a3034.yaml new file mode 100644 index 000000000..7ce33f65e --- /dev/null +++ b/releasenotes/notes/minimum-packet-rate-34576b8fd98a3034.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + Added new client methods for QoS minimum packet rate rule: + ``list_minimum_packet_rate_rules``, ``show_minimum_packet_rate_rule``, + ``create_minimum_packet_rate_rule``, ``update_minimum_packet_rate_rule``, + ``delete_minimum_packet_rate_rule``. diff --git a/releasenotes/notes/neutron-cli-deprecation-398823c87270a296.yaml b/releasenotes/notes/neutron-cli-deprecation-398823c87270a296.yaml new file mode 100644 index 000000000..a8065940e --- /dev/null +++ b/releasenotes/notes/neutron-cli-deprecation-398823c87270a296.yaml @@ -0,0 +1,10 @@ +--- +deprecations: + - | + ``neutron`` CLI will be removed in 'Z' release. + While it has been marked as deprecated for removal for long, + all features in ``neutron`` CLI have been supported in ``openstack`` CLI + (OpenStackClient) as of Xena release and the neutron team plans to + remove it in 'Z' release. Consider using ``openstack`` CLI and + `Mapping Guide `__ + in the OSC documentation would help you. diff --git a/releasenotes/notes/no-new-binding-code-b03c9abbcaf2839e.yaml b/releasenotes/notes/no-new-binding-code-b03c9abbcaf2839e.yaml new file mode 100644 index 000000000..bc41e7438 --- /dev/null +++ b/releasenotes/notes/no-new-binding-code-b03c9abbcaf2839e.yaml @@ -0,0 +1,7 @@ +--- +prelude: > + Openstack community decided to use one SDK for its services, that is + in ``openstacksdk`` repository. To avoid duplication, sooner or later the + python binding code in ``python-neutronclient`` will be deprecated, and + ``Neutron`` team decided on the ``2023.1 (Antelope)`` PTG to not allow + new features\' bindings implemented here. diff --git a/releasenotes/notes/osprofiler-support-9ba539761ae437e9.yaml b/releasenotes/notes/osprofiler-support-9ba539761ae437e9.yaml new file mode 100644 index 000000000..98e2bb168 --- /dev/null +++ b/releasenotes/notes/osprofiler-support-9ba539761ae437e9.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Add osprofiler support to the neutronclient python binding. + If osprofiler is initiated, neutronclient sends a special HTTP + header that contains trace info. diff --git a/releasenotes/notes/paket_rate_limit-1266a2a30f18727f.yaml b/releasenotes/notes/paket_rate_limit-1266a2a30f18727f.yaml new file mode 100644 index 000000000..9fdfc1e62 --- /dev/null +++ b/releasenotes/notes/paket_rate_limit-1266a2a30f18727f.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + Added new client methods for QoS packet rate limit rule: + ``list_packet_rate_limit_rules``, ``show_packet_rate_limit_rule``, + ``create_packet_rate_limit_rule``, ``update_packet_rate_limit_rule``, + ``delete_packet_rate_limit_rule``. diff --git a/releasenotes/notes/port-bindings-c3f36bd76ece0a71.yaml b/releasenotes/notes/port-bindings-c3f36bd76ece0a71.yaml new file mode 100644 index 000000000..7d2ef725e --- /dev/null +++ b/releasenotes/notes/port-bindings-c3f36bd76ece0a71.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + New client methods: ``create_port_binding``, ``delete_port_binding``, + ``show_port_binding``, ``list_port_bindings`` and ``activate_port_binding``. diff --git a/releasenotes/notes/qos_minimum_bandwidth-dc4adb23c51de30b.yaml b/releasenotes/notes/qos_minimum_bandwidth-dc4adb23c51de30b.yaml new file mode 100644 index 000000000..2d1002f07 --- /dev/null +++ b/releasenotes/notes/qos_minimum_bandwidth-dc4adb23c51de30b.yaml @@ -0,0 +1,4 @@ +--- +features: + - New create, update, list, show, and delete commands are added for the QoS + minimum bandwidth rule. diff --git a/releasenotes/notes/quota-update-for-LB-b21e7bc9e4a10f3e.yaml b/releasenotes/notes/quota-update-for-LB-b21e7bc9e4a10f3e.yaml new file mode 100644 index 000000000..9d6321db3 --- /dev/null +++ b/releasenotes/notes/quota-update-for-LB-b21e7bc9e4a10f3e.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Quota of Loadbalancers and listeners can now be updated. diff --git a/releasenotes/notes/quota-update-for-rbac-192a8e65bf481941.yaml b/releasenotes/notes/quota-update-for-rbac-192a8e65bf481941.yaml new file mode 100644 index 000000000..572ce35c2 --- /dev/null +++ b/releasenotes/notes/quota-update-for-rbac-192a8e65bf481941.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Quota for RBAC policies can now be set. diff --git a/releasenotes/notes/relnotes-from-3.0.0-d7306f5af5e3868d.yaml b/releasenotes/notes/relnotes-from-3.0.0-d7306f5af5e3868d.yaml index 012bc7fc2..2bf4d5ab0 100644 --- a/releasenotes/notes/relnotes-from-3.0.0-d7306f5af5e3868d.yaml +++ b/releasenotes/notes/relnotes-from-3.0.0-d7306f5af5e3868d.yaml @@ -2,7 +2,7 @@ features: - Support os-client-config. OS_CLOUD environment variable is used for selecting named cloud configuration. - - Support keystoneauth1 library which brings us better kyestone v3 support. + - Support keystoneauth1 library which brings us better keystone v3 support. - Client command extension now supports a child resource. - New CLI for VPNaaS multiple local subnets. - New CLI for VPNaaS endpoint group API. diff --git a/releasenotes/notes/remote_fwg-0f5362e5be8b2e84.yaml b/releasenotes/notes/remote_fwg-0f5362e5be8b2e84.yaml new file mode 100644 index 000000000..5f5073aee --- /dev/null +++ b/releasenotes/notes/remote_fwg-0f5362e5be8b2e84.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Adds the remote source firewall group and the remote destination + firewall group field to the firewall rules. diff --git a/releasenotes/notes/remove-bgp-speaker-show-dragents-0a0db4b72b2feffc.yaml b/releasenotes/notes/remove-bgp-speaker-show-dragents-0a0db4b72b2feffc.yaml new file mode 100644 index 000000000..5d4fdda2c --- /dev/null +++ b/releasenotes/notes/remove-bgp-speaker-show-dragents-0a0db4b72b2feffc.yaml @@ -0,0 +1,6 @@ +--- +upgrade: + - | + The ``openstack bgp speaker show dragents`` CLI is removed. It was + deprecated in the 7.1.0 release (Ussuri). Use ``openstack bgp dragent list + --bgp-speaker `` CLI instead. diff --git a/releasenotes/notes/remove-case-dependency-773ccb3237c38e81.yaml b/releasenotes/notes/remove-case-dependency-773ccb3237c38e81.yaml new file mode 100644 index 000000000..d32f96b4f --- /dev/null +++ b/releasenotes/notes/remove-case-dependency-773ccb3237c38e81.yaml @@ -0,0 +1,7 @@ +--- +other: + - | + This patch provides user the support to use + any form of casing, thus removing the specific + UPPER/lower case inputs required by different + neutron CLIs. diff --git a/releasenotes/notes/remove-cli-code-53969e9aa927e530.yaml b/releasenotes/notes/remove-cli-code-53969e9aa927e530.yaml new file mode 100644 index 000000000..92ab5423c --- /dev/null +++ b/releasenotes/notes/remove-cli-code-53969e9aa927e530.yaml @@ -0,0 +1,12 @@ +prelude: > + | + This new version of ``python-neutronclient`` does not include the + command line interface code. The "neutron" script is no longer + supported. This project only contains the OpenStack Client bindings + that are currently being moved to OpenStack SDK + _. +deprecations: + - | + This project no longer provides CLI support. All code has been removed. + Please use openstack CLI instead. See `openstack CLI command list + `_. diff --git a/releasenotes/notes/remove-deprecated-option-b53f5d7e6a16ce95.yaml b/releasenotes/notes/remove-deprecated-option-b53f5d7e6a16ce95.yaml new file mode 100644 index 000000000..fb304fbda --- /dev/null +++ b/releasenotes/notes/remove-deprecated-option-b53f5d7e6a16ce95.yaml @@ -0,0 +1,4 @@ +--- +deprecations: + - | + "admin-state-down" option was deprecated in Mitaka and has been removed in Newton. diff --git a/releasenotes/notes/remove-fwaas-osc-plugin-a1b2c3d4e5f6g7h8.yaml b/releasenotes/notes/remove-fwaas-osc-plugin-a1b2c3d4e5f6g7h8.yaml new file mode 100644 index 000000000..171b3eb62 --- /dev/null +++ b/releasenotes/notes/remove-fwaas-osc-plugin-a1b2c3d4e5f6g7h8.yaml @@ -0,0 +1,11 @@ +--- +deprecations: + - | + The Firewall as a Service (FWaaS) v2 OSC plugin commands have been moved + to ``python-openstackclient``. The FWaaS commands are no longer provided + by ``python-neutronclient``. Please use ``python-openstackclient`` for + the following commands: + + * ``firewall group create/delete/list/set/show/unset`` + * ``firewall group policy create/delete/list/set/show/unset/add rule/remove rule`` + * ``firewall group rule create/delete/list/set/show/unset`` diff --git a/releasenotes/notes/remove-public-and-private-parameters-d683e7c30ecedc3b.yaml b/releasenotes/notes/remove-public-and-private-parameters-d683e7c30ecedc3b.yaml new file mode 100644 index 000000000..d340c1763 --- /dev/null +++ b/releasenotes/notes/remove-public-and-private-parameters-d683e7c30ecedc3b.yaml @@ -0,0 +1,6 @@ +--- +upgrade: + - | + The deprecated options ``--public`` and ``--private`` were + dropped in FWaaS v2 related commands. Use ``--share`` and + ``--no-share`` instead. diff --git a/releasenotes/notes/remove-py38-26a1befde3f44b82.yaml b/releasenotes/notes/remove-py38-26a1befde3f44b82.yaml new file mode 100644 index 000000000..a0440dc80 --- /dev/null +++ b/releasenotes/notes/remove-py38-26a1befde3f44b82.yaml @@ -0,0 +1,5 @@ +--- +upgrade: + - | + Python 3.8 support was dropped. The minimum version of Python now supported + is Python 3.9. diff --git a/releasenotes/notes/segments-8557f5b0caa5ee26.yaml b/releasenotes/notes/segments-8557f5b0caa5ee26.yaml new file mode 100644 index 000000000..292adf85e --- /dev/null +++ b/releasenotes/notes/segments-8557f5b0caa5ee26.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + New client methods: ``create_segment``, ``update_segment``, + ``list_segments``, ``show_segment`` and ``delete_segment``. diff --git a/releasenotes/notes/sfc-tap-service-function-support-a05242f25f79066b.yaml b/releasenotes/notes/sfc-tap-service-function-support-a05242f25f79066b.yaml new file mode 100644 index 000000000..ccfcc8276 --- /dev/null +++ b/releasenotes/notes/sfc-tap-service-function-support-a05242f25f79066b.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Add OSC support to create Port pair group for Tap service functions. diff --git a/releasenotes/notes/show-tenant-id-admin-listing-dc13ee7eb889d418.yaml b/releasenotes/notes/show-tenant-id-admin-listing-dc13ee7eb889d418.yaml new file mode 100644 index 000000000..6b84e1843 --- /dev/null +++ b/releasenotes/notes/show-tenant-id-admin-listing-dc13ee7eb889d418.yaml @@ -0,0 +1,6 @@ +--- +features: + - Show tenant_id when ``*-list`` command is run by admin. In neutron + the list operations by admin retrieve all resources from all tenants. + It is not easy to distinguish resources without tenant_id. + This feature is useful for admin operations. diff --git a/releasenotes/notes/support-bgpvpn-route-control-aeda3e698486f73b.yaml b/releasenotes/notes/support-bgpvpn-route-control-aeda3e698486f73b.yaml new file mode 100644 index 000000000..2f7ed1720 --- /dev/null +++ b/releasenotes/notes/support-bgpvpn-route-control-aeda3e698486f73b.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Add BGP VPN `port association `_ + support to the CLI, which are introduced for BGP VPN interconnections by the + ``bgpvpn-routes-control`` API extension. diff --git a/releasenotes/notes/support-firewall-group-resource-type-5ad1b69cabcb4aa6.yaml b/releasenotes/notes/support-firewall-group-resource-type-5ad1b69cabcb4aa6.yaml new file mode 100644 index 000000000..78fbff8fd --- /dev/null +++ b/releasenotes/notes/support-firewall-group-resource-type-5ad1b69cabcb4aa6.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + CLI support for the "firewal_group" as a loggable resource type for logging + feature, which is enhanced FWaaS functionality, as OSC plugin commands. diff --git a/releasenotes/notes/support-fwaasv2-cli-7f21676c551f8ae0.yaml b/releasenotes/notes/support-fwaasv2-cli-7f21676c551f8ae0.yaml new file mode 100644 index 000000000..2032bfa63 --- /dev/null +++ b/releasenotes/notes/support-fwaasv2-cli-7f21676c551f8ae0.yaml @@ -0,0 +1,4 @@ +--- +features: + - CLI support for the "Firewall as a Service v2" feature, which is enhanced + FWaaS functionality, as OSC plugin commands. diff --git a/releasenotes/notes/support-logging-cli-cd02d3bb03367106.yaml b/releasenotes/notes/support-logging-cli-cd02d3bb03367106.yaml new file mode 100644 index 000000000..415c30cd7 --- /dev/null +++ b/releasenotes/notes/support-logging-cli-cd02d3bb03367106.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + CLI support for 'Logging' feature, which enable to collect packet logs + for specified resource. Currently, only security-group can be logged. diff --git a/releasenotes/notes/support-networking-bgpvpn-cli-fdd0cc3a5b14983d.yaml b/releasenotes/notes/support-networking-bgpvpn-cli-fdd0cc3a5b14983d.yaml new file mode 100644 index 000000000..287e0427c --- /dev/null +++ b/releasenotes/notes/support-networking-bgpvpn-cli-fdd0cc3a5b14983d.yaml @@ -0,0 +1,5 @@ +--- +features: + - CLI support for the "Neutron BGP VPN Interconnection" feature, + which is an API extension to support inter-connection between + L3VPNs/E-VPNs and Neutron resources, as OSC plugin commands. diff --git a/releasenotes/notes/support-routes-advertise-9356a38cf3e2fe5a.yaml b/releasenotes/notes/support-routes-advertise-9356a38cf3e2fe5a.yaml new file mode 100644 index 000000000..f1dbd5ed0 --- /dev/null +++ b/releasenotes/notes/support-routes-advertise-9356a38cf3e2fe5a.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Add optional flag to control the advertisement in BGPVPNs + of the routes defined on a Router resource + (``bgpvpn-routes-control`` API extension). diff --git a/releasenotes/notes/support-vni-in-networking-bgpvpn-cli-d284b73b40b79495.yaml b/releasenotes/notes/support-vni-in-networking-bgpvpn-cli-d284b73b40b79495.yaml new file mode 100644 index 000000000..3c3e51c28 --- /dev/null +++ b/releasenotes/notes/support-vni-in-networking-bgpvpn-cli-d284b73b40b79495.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + CLI support for VXLAN VNI ID attribute in bgpvpn. + An optional argument ``--vni`` is added to ``openstack bgpvpn`` + commands to configure VXLAN Network Identifier when VXLAN + encapsulation is used for the bgpvpn. diff --git a/releasenotes/notes/support-vpnaas-cli-9478fb7cfe603e26.yaml b/releasenotes/notes/support-vpnaas-cli-9478fb7cfe603e26.yaml new file mode 100644 index 000000000..3b5e6e491 --- /dev/null +++ b/releasenotes/notes/support-vpnaas-cli-9478fb7cfe603e26.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + CLI support for the "VPN as a Service" feature, which is enhanced + VPNaaS functionality, as OSC plugin commands. diff --git a/releasenotes/notes/tag-support-subnet-port-subnetpool-router-6250ec4714ee8690.yaml b/releasenotes/notes/tag-support-subnet-port-subnetpool-router-6250ec4714ee8690.yaml new file mode 100644 index 000000000..1bc27aeb8 --- /dev/null +++ b/releasenotes/notes/tag-support-subnet-port-subnetpool-router-6250ec4714ee8690.yaml @@ -0,0 +1,4 @@ +--- +features: + - Tag operation for subnet, port, subnetpool and router resources + are now supported. diff --git a/releasenotes/source/2023.1.rst b/releasenotes/source/2023.1.rst new file mode 100644 index 000000000..2c9a36fae --- /dev/null +++ b/releasenotes/source/2023.1.rst @@ -0,0 +1,6 @@ +=========================== +2023.1 Series Release Notes +=========================== + +.. release-notes:: + :branch: unmaintained/2023.1 diff --git a/releasenotes/source/2023.2.rst b/releasenotes/source/2023.2.rst new file mode 100644 index 000000000..a4838d7d0 --- /dev/null +++ b/releasenotes/source/2023.2.rst @@ -0,0 +1,6 @@ +=========================== +2023.2 Series Release Notes +=========================== + +.. release-notes:: + :branch: stable/2023.2 diff --git a/releasenotes/source/2024.1.rst b/releasenotes/source/2024.1.rst new file mode 100644 index 000000000..6896656be --- /dev/null +++ b/releasenotes/source/2024.1.rst @@ -0,0 +1,6 @@ +=========================== +2024.1 Series Release Notes +=========================== + +.. release-notes:: + :branch: unmaintained/2024.1 diff --git a/releasenotes/source/2024.2.rst b/releasenotes/source/2024.2.rst new file mode 100644 index 000000000..aaebcbc8c --- /dev/null +++ b/releasenotes/source/2024.2.rst @@ -0,0 +1,6 @@ +=========================== +2024.2 Series Release Notes +=========================== + +.. release-notes:: + :branch: stable/2024.2 diff --git a/releasenotes/source/2025.1.rst b/releasenotes/source/2025.1.rst new file mode 100644 index 000000000..3add0e53a --- /dev/null +++ b/releasenotes/source/2025.1.rst @@ -0,0 +1,6 @@ +=========================== +2025.1 Series Release Notes +=========================== + +.. release-notes:: + :branch: stable/2025.1 diff --git a/releasenotes/source/2025.2.rst b/releasenotes/source/2025.2.rst new file mode 100644 index 000000000..4dae18d86 --- /dev/null +++ b/releasenotes/source/2025.2.rst @@ -0,0 +1,6 @@ +=========================== +2025.2 Series Release Notes +=========================== + +.. release-notes:: + :branch: stable/2025.2 diff --git a/releasenotes/source/2026.1.rst b/releasenotes/source/2026.1.rst new file mode 100644 index 000000000..3d2861580 --- /dev/null +++ b/releasenotes/source/2026.1.rst @@ -0,0 +1,6 @@ +=========================== +2026.1 Series Release Notes +=========================== + +.. release-notes:: + :branch: stable/2026.1 diff --git a/releasenotes/source/conf.py b/releasenotes/source/conf.py index e1e2a5ae5..498287605 100644 --- a/releasenotes/source/conf.py +++ b/releasenotes/source/conf.py @@ -38,10 +38,16 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'oslosphinx', 'reno.sphinxext', + 'openstackdocstheme', ] +# openstackdocstheme options +openstackdocs_repo_name = 'openstack/python-neutronclient' +openstackdocs_bug_project = 'python-neutronclient' +openstackdocs_bug_tag = 'doc' +openstackdocs_auto_name = False + # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -55,19 +61,14 @@ master_doc = 'index' # General information about the project. -project = u'Neutron Client Release Notes' -copyright = u'2015, Neutron Developers' +project = 'Neutron Client Release Notes' +copyright = '2015, Neutron Developers' -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -from neutronclient.version import version_info +# Release notes are version independent. # The full version, including alpha/beta/rc tags. -release = version_info.version_string_with_vcs() +release = '' # The short X.Y version. -version = version_info.canonical_version_string() +version = '' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -112,7 +113,7 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'default' +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 @@ -148,10 +149,6 @@ # 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 @@ -192,85 +189,5 @@ # Output file base name for HTML help builder. htmlhelp_basename = 'NeutronReleaseNotesdoc' - -# -- Options for LaTeX output --------------------------------------------- - -latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # 'papersize': 'letterpaper', - - # The font size ('10pt', '11pt' or '12pt'). - # 'pointsize': '10pt', - - # Additional stuff for the LaTeX preamble. - # 'preamble': '', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - ('index', 'NeutronClientReleaseNotes.tex', - u'Neutron Client Release Notes Documentation', - u'Neutron Developers', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -# latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -# latex_use_parts = False - -# If true, show page references after internal links. -# latex_show_pagerefs = False - -# If true, show URL addresses after external links. -# latex_show_urls = False - -# Documents to append as an appendix to all manuals. -# latex_appendices = [] - -# If false, no module index is generated. -# latex_domain_indices = True - - -# -- Options for manual page output --------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - ('index', 'neutronclientreleasenotes', - u'Neutron Client Release Notes Documentation', - [u'Neutron Developers'], 1) -] - -# If true, show URL addresses after external links. -# man_show_urls = False - - -# -- Options for Texinfo output ------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - ('index', 'NeutronClientReleaseNotes', - u'Neutron Client Release Notes Documentation', - u'Neutron Developers', 'NeutronClientReleaseNotes', - 'One line description of project.', - 'Miscellaneous'), -] - -# Documents to append as an appendix to all manuals. -# texinfo_appendices = [] - -# If false, no module index is generated. -# texinfo_domain_indices = True - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -# texinfo_show_urls = 'footnote' - -# If true, do not generate a @detailmenu in the "Top" node's menu. -# texinfo_no_detailmenu = False +# -- Options for Internationalization output ------------------------------ +locale_dirs = ['locale/'] diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst index 40b342ddd..7fe1c4a53 100644 --- a/releasenotes/source/index.rst +++ b/releasenotes/source/index.rst @@ -6,5 +6,25 @@ :maxdepth: 1 unreleased + 2026.1 + 2025.2 + 2025.1 + 2024.2 + 2024.1 + 2023.2 + 2023.1 + zed + yoga + xena + wallaby + victoria + ussuri + train + stein + rocky + queens + pike + ocata + newton mitaka old_relnotes 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 000000000..cb1dcb6bd --- /dev/null +++ b/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po @@ -0,0 +1,400 @@ +# Andi Chandler , 2022. #zanata +# Andi Chandler , 2023. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Neutron Client Release Notes\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-09-08 20:18+0000\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"PO-Revision-Date: 2023-07-28 12:44+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 "" +"\"admin-state-down\" option was deprecated in Mitaka and has been removed in " +"Newton." +msgstr "" +"\"admin-state-down\" option was deprecated in Mitaka and has been removed in " +"Newton." + +msgid "10.0.0" +msgstr "10.0.0" + +msgid "2.0" +msgstr "2.0" + +msgid "2.2.0" +msgstr "2.2.0" + +msgid "2.2.2" +msgstr "2.2.2" + +msgid "2023.1 Series Release Notes" +msgstr "2023.1 Series Release Notes" + +msgid "4.0.0" +msgstr "4.0.0" + +msgid "4.1.0" +msgstr "4.1.0" + +msgid "4.1.1" +msgstr "4.1.1" + +msgid "4.1.2-15" +msgstr "4.1.2-15" + +msgid "4.2.0" +msgstr "4.2.0" + +msgid "5.0.0" +msgstr "5.0.0" + +msgid "5.1.0" +msgstr "5.1.0" + +msgid "6.0.0" +msgstr "6.0.0" + +msgid "6.1.0" +msgstr "6.1.0" + +msgid "6.1.1" +msgstr "6.1.1" + +msgid "6.10.0" +msgstr "6.10.0" + +msgid "6.11.0" +msgstr "6.11.0" + +msgid "6.12.0" +msgstr "6.12.0" + +msgid "6.14.0" +msgstr "6.14.0" + +msgid "6.2.0" +msgstr "6.2.0" + +msgid "6.3.0" +msgstr "6.3.0" + +msgid "6.4.0" +msgstr "6.4.0" + +msgid "6.5.0" +msgstr "6.5.0" + +msgid "6.6.0" +msgstr "6.6.0" + +msgid "6.7.0" +msgstr "6.7.0" + +msgid "7.0.0" +msgstr "7.0.0" + +msgid "7.1.0" +msgstr "7.1.0" + +msgid "7.2.0" +msgstr "7.2.0" + +msgid "7.2.1" +msgstr "7.2.1" + +msgid "7.5.0" +msgstr "7.5.0" + +msgid "7.7.0" +msgstr "7.7.0" + +msgid "7.8.0" +msgstr "7.8.0" + +msgid "8.0.0" +msgstr "8.0.0" + +msgid "8.1.0" +msgstr "8.1.0" + +msgid "8.2.0" +msgstr "8.2.0" + +msgid "" +"A new option ``--no-session-persistence`` has been added to the ``neutron " +"lbaas-pool-update`` CLI to clear the session persistence with which the " +"current pool is associated." +msgstr "" +"A new option ``--no-session-persistence`` has been added to the ``neutron " +"lbaas-pool-update`` CLI to clear the session persistence with which the " +"current pool is associated." + +msgid "" +"Add BGP VPN `port association `_ support to the CLI, which are " +"introduced for BGP VPN interconnections by the ``bgpvpn-routes-control`` API " +"extension." +msgstr "" +"Add BGP VPN `port association `_ support to the CLI, which are " +"introduced for BGP VPN interconnections by the ``bgpvpn-routes-control`` API " +"extension." + +msgid "" +"Add OSC plugin support for the “Networking Service Function Chaining” " +"feature commands along with client bindings. [Blueprint `openstackclient-cli-" +"porting `_]" +msgstr "" +"Add OSC plugin support for the “Networking Service Function Chaining” " +"feature commands along with client bindings. [Blueprint `openstackclient-cli-" +"porting `_]" + +msgid "Add OSC plugin to support \"Neutron Dynamic Routing\"" +msgstr "Add OSC plugin to support \"Neutron Dynamic Routing\"" + +msgid "Add OSC support to create Port pair group for Tap service functions." +msgstr "Add OSC support to create Port pair group for Tap service functions." + +msgid "" +"Add ``network onboard subnets`` OSC command to enable subnet onboard support " +"from the CLI [Blueprint `subnet-onboard `_]" +msgstr "" +"Add ``network onboard subnets`` OSC command to enable subnet onboard support " +"from the CLI [Blueprint `subnet-onboard `_]" + +msgid "" +"Add ``network trunk create``, ``network trunk list``, ``network trunk set``, " +"``network trunk unset``, ``network trunk delete`` and ``network subport " +"list`` OSC commands for trunk resource along with client bindings. " +"[Blueprint `vlan-aware-vms `_]" +msgstr "" +"Add ``network trunk create``, ``network trunk list``, ``network trunk set``, " +"``network trunk unset``, ``network trunk delete`` and ``network subport " +"list`` OSC commands for trunk resource along with client bindings. " +"[Blueprint `vlan-aware-vms `_]" + +msgid "" +"Add optional flag to control the advertisement in BGPVPNs of the routes " +"defined on a Router resource (``bgpvpn-routes-control`` API extension)." +msgstr "" +"Add optional flag to control the advertisement in BGPVPNs of the routes " +"defined on a Router resource (``bgpvpn-routes-control`` API extension)." + +msgid "" +"Add osprofiler support to the neutronclient python binding. If osprofiler is " +"initiated, neutronclient sends a special HTTP header that contains trace " +"info." +msgstr "" +"Add osprofiler support to the neutronclient python binding. If osprofiler is " +"initiated, neutronclient sends a special HTTP header that contains trace " +"info." + +msgid "Add support to floating ip port forwarding." +msgstr "Add support to floating ip port forwarding." + +msgid "" +"By using this feature, multiple resource can be deleted using a single " +"command." +msgstr "" +"By using this feature, multiple resource can be deleted using a single " +"command." + +msgid "" +"CLI support for 'Logging' feature, which enable to collect packet logs for " +"specified resource. Currently, only security-group can be logged." +msgstr "" +"CLI support for 'Logging' feature, which enable to collect packet logs for " +"specified resource. Currently, only security-group can be logged." + +msgid "CLI support for Layer 7 content policies and rules." +msgstr "CLI support for Layer 7 content policies and rules." + +msgid "CLI support for Neutron-LBaaS v2 shared pools added." +msgstr "CLI support for Neutron-LBaaS v2 shared pools added." + +msgid "CLI support for QoS policy RBAC." +msgstr "CLI support for QoS policy RBAC." + +msgid "Current Series Release Notes" +msgstr "Current Series Release Notes" + +msgid "Mitaka Series Release Notes" +msgstr "Mitaka Series Release Notes" + +msgid "Neutron Client Release Notes" +msgstr "Neutron Client Release Notes" + +msgid "Newton Series Release Notes" +msgstr "Newton Series Release Notes" + +msgid "Ocata Series Release Notes" +msgstr "Ocata Series Release Notes" + +msgid "Old Release Notes" +msgstr "Old Release Notes" + +msgid "Pike Series Release Notes" +msgstr "Pike Series Release Notes" + +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 "The ``rbac-create`` command include a --type qos-policy option." +msgstr "The ``rbac-create`` command include a --type qos-policy option." + +msgid "The ``rbac-list`` command output includes a new 'type' column." +msgstr "The ``rbac-list`` command output includes a new 'type' column." + +msgid "" +"The ``subnetpool-create`` and ``subnetpool-update`` commands include a ``--" +"is-default`` option." +msgstr "" +"The ``subnetpool-create`` and ``subnetpool-update`` commands include a ``--" +"is-default`` option." + +msgid "" +"The ``subnetpool-list`` and ``subnetpool-show`` command output includes the " +"``is_default`` field." +msgstr "" +"The ``subnetpool-list`` and ``subnetpool-show`` command output includes the " +"``is_default`` field." + +msgid "" +"The ``tag-add`` command sets a tag on the network resource. It also includes " +"``--resource-type``, ``--resource`` and ``--tag`` options." +msgstr "" +"The ``tag-add`` command sets a tag on the network resource. It also includes " +"``--resource-type``, ``--resource`` and ``--tag`` options." + +msgid "" +"This patch provides user the support to use any form of casing, thus " +"removing the specific UPPER/lower case inputs required by different neutron " +"CLIs." +msgstr "" +"This patch provides users the support to use any form of casing, thus " +"removing the specific UPPER/lower case inputs required by different Neutron " +"CLIs." + +msgid "" +"This project no longer provides CLI support. All code has been removed. " +"Please use openstack CLI instead. See `openstack CLI command list `_." +msgstr "" +"This project no longer provides CLI support. All code has been removed. " +"Please use OpenStack CLI instead. See `openstack CLI command list `_." + +msgid "Train Series Release Notes" +msgstr "Train Series Release Notes" + +msgid "Upgrade Notes" +msgstr "Upgrade Notes" + +msgid "" +"Using 'tenant_id' and 'tenant_name' arguments in API bindings is deprecated. " +"Use 'project_id' and 'project_name' arguments instead." +msgstr "" +"Using 'tenant_id' and 'tenant_name' arguments in API bindings is deprecated. " +"Use 'project_id' and 'project_name' arguments instead." + +msgid "Ussuri Series Release Notes" +msgstr "Ussuri Series Release Notes" + +msgid "Victoria Series Release Notes" +msgstr "Victoria Series Release Notes" + +msgid "Wallaby Series Release Notes" +msgstr "Wallaby Series Release Notes" + +msgid "XML request format support has been removed." +msgstr "XML request format support has been removed." + +msgid "Xena Series Release Notes" +msgstr "Xena Series Release Notes" + +msgid "Yoga Series Release Notes" +msgstr "Yoga Series Release Notes" + +msgid "Zed Series Release Notes" +msgstr "Zed Series Release Notes" + +msgid "add --endpoint-type and OS_ENDPOINT_TYPE to shell client" +msgstr "add --endpoint-type and OS_ENDPOINT_TYPE to shell client" + +msgid "add Lbaas commands" +msgstr "add Lbaas commands" + +msgid "add NVP queue and net gateway commands" +msgstr "add NVP queue and net gateway commands" + +msgid "" +"add ability to update security group name (requires 2013.2-Havana or later)" +msgstr "" +"add ability to update security group name (requires 2013.2-Havana or later)" + +msgid "add commands for DHCP and L3 agents scheduling" +msgstr "add commands for DHCP and L3 agents scheduling" + +msgid "add commands for agent management extensions" +msgstr "add commands for agent management extensions" + +msgid "add flake8 and pbr support for testing and building" +msgstr "add flake8 and pbr support for testing and building" + +msgid "add security group commands" +msgstr "add security group commands" + +msgid "allow options put after positional arguments" +msgstr "allow options put after positional arguments" + +msgid "improved support for listing a large number of filtered subnets" +msgstr "improved support for listing a large number of filtered subnets" + +msgid "made the publicURL the default endpoint instead of adminURL" +msgstr "made the publicURL the default endpoint instead of adminURL" + +msgid "openstack firewall rule create" +msgstr "openstack firewall rule create" + +msgid "openstack firewall rule set" +msgstr "openstack firewall rule set" + +msgid "openstack firewall rule unset" +msgstr "openstack firewall rule unset" + +msgid "py26 support has been dropped." +msgstr "py26 support has been dropped." + +msgid "py33 support has been dropped." +msgstr "py33 support has been dropped." + +msgid "request-format option is deprecated." +msgstr "request-format option is deprecated." + +msgid "support Neutron API 2.0" +msgstr "support Neutron API 2.0" + +msgid "support XML request format" +msgstr "support XML request format" + +msgid "support pagination options" +msgstr "support pagination options" diff --git a/releasenotes/source/newton.rst b/releasenotes/source/newton.rst new file mode 100644 index 000000000..97036ed25 --- /dev/null +++ b/releasenotes/source/newton.rst @@ -0,0 +1,6 @@ +=================================== + Newton Series Release Notes +=================================== + +.. release-notes:: + :branch: origin/stable/newton diff --git a/releasenotes/source/ocata.rst b/releasenotes/source/ocata.rst new file mode 100644 index 000000000..ebe62f42e --- /dev/null +++ b/releasenotes/source/ocata.rst @@ -0,0 +1,6 @@ +=================================== + Ocata Series Release Notes +=================================== + +.. release-notes:: + :branch: origin/stable/ocata diff --git a/releasenotes/source/pike.rst b/releasenotes/source/pike.rst new file mode 100644 index 000000000..e43bfc0ce --- /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 000000000..36ac6160c --- /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 000000000..40dd517b7 --- /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 000000000..efaceb667 --- /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 000000000..583900393 --- /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/ussuri.rst b/releasenotes/source/ussuri.rst new file mode 100644 index 000000000..e21e50e0c --- /dev/null +++ b/releasenotes/source/ussuri.rst @@ -0,0 +1,6 @@ +=========================== +Ussuri Series Release Notes +=========================== + +.. release-notes:: + :branch: stable/ussuri diff --git a/releasenotes/source/victoria.rst b/releasenotes/source/victoria.rst new file mode 100644 index 000000000..8ce933419 --- /dev/null +++ b/releasenotes/source/victoria.rst @@ -0,0 +1,6 @@ +============================= +Victoria Series Release Notes +============================= + +.. release-notes:: + :branch: unmaintained/victoria diff --git a/releasenotes/source/wallaby.rst b/releasenotes/source/wallaby.rst new file mode 100644 index 000000000..bcf35c5f8 --- /dev/null +++ b/releasenotes/source/wallaby.rst @@ -0,0 +1,6 @@ +============================ +Wallaby Series Release Notes +============================ + +.. release-notes:: + :branch: unmaintained/wallaby diff --git a/releasenotes/source/xena.rst b/releasenotes/source/xena.rst new file mode 100644 index 000000000..d19eda488 --- /dev/null +++ b/releasenotes/source/xena.rst @@ -0,0 +1,6 @@ +========================= +Xena Series Release Notes +========================= + +.. release-notes:: + :branch: unmaintained/xena diff --git a/releasenotes/source/yoga.rst b/releasenotes/source/yoga.rst new file mode 100644 index 000000000..43cafdea8 --- /dev/null +++ b/releasenotes/source/yoga.rst @@ -0,0 +1,6 @@ +========================= +Yoga Series Release Notes +========================= + +.. release-notes:: + :branch: unmaintained/yoga diff --git a/releasenotes/source/zed.rst b/releasenotes/source/zed.rst new file mode 100644 index 000000000..6cc2b1554 --- /dev/null +++ b/releasenotes/source/zed.rst @@ -0,0 +1,6 @@ +======================== +Zed Series Release Notes +======================== + +.. release-notes:: + :branch: unmaintained/zed diff --git a/requirements.txt b/requirements.txt index 3ce9a2314..41ca9d7ef 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,18 +1,18 @@ -# 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>=1.6 # Apache-2.0 -cliff!=1.16.0,!=1.17.0,>=1.15.0 # Apache-2.0 +# Requirements lower bounds listed here are our best effort to keep them up to +# date but we do not test them so no guarantee of having them all correct. If +# you find any incorrect lower bounds, let us know or propose a fix. +pbr!=2.1.0,>=2.0.0 # Apache-2.0 +cliff>=3.4.0 # Apache-2.0 debtcollector>=1.2.0 # Apache-2.0 -iso8601>=0.1.11 # MIT -netaddr!=0.7.16,>=0.7.12 # BSD -osc-lib>=0.4.0 # Apache-2.0 -oslo.i18n>=2.1.0 # Apache-2.0 -oslo.serialization>=1.10.0 # Apache-2.0 -oslo.utils>=3.16.0 # Apache-2.0 -os-client-config>=1.13.1 # Apache-2.0 -keystoneauth1>=2.10.0 # Apache-2.0 -requests>=2.10.0 # Apache-2.0 -simplejson>=2.2.0 # MIT -six>=1.9.0 # MIT -Babel>=2.3.4 # BSD +netaddr>=0.7.18 # BSD +openstacksdk>=1.5.0 # Apache-2.0 +osc-lib>=1.12.0 # Apache-2.0 +oslo.i18n>=3.15.3 # Apache-2.0 +oslo.log>=3.36.0 # Apache-2.0 +oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0 +oslo.utils>=3.33.0 # Apache-2.0 +keystoneauth1>=3.8.0 # Apache-2.0 +# keystoneclient is used only by neutronclient.osc.utils +# TODO(amotoki): Drop this after osc.utils has no dependency on keystoneclient +python-keystoneclient>=3.8.0 # Apache-2.0 +requests>=2.14.2 # Apache-2.0 diff --git a/setup.cfg b/setup.cfg index 73c1e65ee..a995bae67 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,11 +1,12 @@ [metadata] name = python-neutronclient summary = CLI and Client Library for OpenStack Networking -description-file = +description_file = README.rst author = OpenStack Networking Project -author-email = openstack-dev@lists.openstack.org -home-page = http://docs.openstack.org/developer/python-neutronclient +author_email = openstack-discuss@lists.openstack.org +home_page = https://docs.openstack.org/python-neutronclient/latest/ +python_requires = >=3.9 classifier = Environment :: OpenStack Intended Audience :: Developers @@ -14,54 +15,106 @@ classifier = License :: OSI Approved :: Apache Software License Operating System :: POSIX :: Linux Programming Language :: Python - Programming Language :: Python :: 2 - Programming Language :: Python :: 2.7 + Programming Language :: Python :: Implementation :: CPython + Programming Language :: Python :: 3 :: Only Programming Language :: Python :: 3 - Programming Language :: Python :: 3.4 - Programming Language :: Python :: 3.5 + Programming Language :: Python :: 3.9 + Programming Language :: Python :: 3.10 + Programming Language :: Python :: 3.11 + Programming Language :: Python :: 3.12 [files] packages = neutronclient -[global] -setup-hooks = - pbr.hooks.setup_hook - [entry_points] -console_scripts = - neutron = neutronclient.shell:main - openstack.cli.extension = neutronclient = neutronclient.osc.plugin openstack.neutronclient.v2 = - network_subport_list = neutronclient.osc.v2.trunk.network_trunk:ListNetworkSubport - network_trunk_create = neutronclient.osc.v2.trunk.network_trunk:CreateNetworkTrunk - network_trunk_delete = neutronclient.osc.v2.trunk.network_trunk:DeleteNetworkTrunk - network_trunk_list = neutronclient.osc.v2.trunk.network_trunk:ListNetworkTrunk - network_trunk_set = neutronclient.osc.v2.trunk.network_trunk:SetNetworkTrunk - network_trunk_show = neutronclient.osc.v2.trunk.network_trunk:ShowNetworkTrunk - network_trunk_unset = neutronclient.osc.v2.trunk.network_trunk:UnsetNetworkTrunk + sfc_flow_classifier_create = neutronclient.osc.v2.sfc.sfc_flow_classifier:CreateSfcFlowClassifier + sfc_flow_classifier_delete = neutronclient.osc.v2.sfc.sfc_flow_classifier:DeleteSfcFlowClassifier + sfc_flow_classifier_list = neutronclient.osc.v2.sfc.sfc_flow_classifier:ListSfcFlowClassifier + sfc_flow_classifier_set = neutronclient.osc.v2.sfc.sfc_flow_classifier:SetSfcFlowClassifier + sfc_flow_classifier_show = neutronclient.osc.v2.sfc.sfc_flow_classifier:ShowSfcFlowClassifier + sfc_port_chain_create = neutronclient.osc.v2.sfc.sfc_port_chain:CreateSfcPortChain + sfc_port_chain_delete = neutronclient.osc.v2.sfc.sfc_port_chain:DeleteSfcPortChain + sfc_port_chain_list = neutronclient.osc.v2.sfc.sfc_port_chain:ListSfcPortChain + sfc_port_chain_set = neutronclient.osc.v2.sfc.sfc_port_chain:SetSfcPortChain + sfc_port_chain_show = neutronclient.osc.v2.sfc.sfc_port_chain:ShowSfcPortChain + sfc_port_chain_unset = neutronclient.osc.v2.sfc.sfc_port_chain:UnsetSfcPortChain + sfc_port_pair_create = neutronclient.osc.v2.sfc.sfc_port_pair:CreateSfcPortPair + sfc_port_pair_delete = neutronclient.osc.v2.sfc.sfc_port_pair:DeleteSfcPortPair + sfc_port_pair_list = neutronclient.osc.v2.sfc.sfc_port_pair:ListSfcPortPair + sfc_port_pair_set = neutronclient.osc.v2.sfc.sfc_port_pair:SetSfcPortPair + sfc_port_pair_show = neutronclient.osc.v2.sfc.sfc_port_pair:ShowSfcPortPair + sfc_port_pair_group_create = neutronclient.osc.v2.sfc.sfc_port_pair_group:CreateSfcPortPairGroup + sfc_port_pair_group_delete = neutronclient.osc.v2.sfc.sfc_port_pair_group:DeleteSfcPortPairGroup + sfc_port_pair_group_list = neutronclient.osc.v2.sfc.sfc_port_pair_group:ListSfcPortPairGroup + sfc_port_pair_group_set = neutronclient.osc.v2.sfc.sfc_port_pair_group:SetSfcPortPairGroup + sfc_port_pair_group_show = neutronclient.osc.v2.sfc.sfc_port_pair_group:ShowSfcPortPairGroup + sfc_port_pair_group_unset = neutronclient.osc.v2.sfc.sfc_port_pair_group:UnsetSfcPortPairGroup + sfc_service_graph_create = neutronclient.osc.v2.sfc.sfc_service_graph:CreateSfcServiceGraph + sfc_service_graph_delete = neutronclient.osc.v2.sfc.sfc_service_graph:DeleteSfcServiceGraph + sfc_service_graph_set = neutronclient.osc.v2.sfc.sfc_service_graph:SetSfcServiceGraph + sfc_service_graph_list = neutronclient.osc.v2.sfc.sfc_service_graph:ListSfcServiceGraph + sfc_service_graph_show = neutronclient.osc.v2.sfc.sfc_service_graph:ShowSfcServiceGraph + + bgp_dragent_add_speaker = neutronclient.osc.v2.dynamic_routing.bgp_dragent:AddBgpSpeakerToDRAgent + bgp_dragent_list = neutronclient.osc.v2.dynamic_routing.bgp_dragent:ListDRAgent + bgp_dragent_remove_speaker = neutronclient.osc.v2.dynamic_routing.bgp_dragent:RemoveBgpSpeakerFromDRAgent + bgp_peer_create = neutronclient.osc.v2.dynamic_routing.bgp_peer:CreateBgpPeer + bgp_peer_delete = neutronclient.osc.v2.dynamic_routing.bgp_peer:DeleteBgpPeer + bgp_peer_list = neutronclient.osc.v2.dynamic_routing.bgp_peer:ListBgpPeer + bgp_peer_show = neutronclient.osc.v2.dynamic_routing.bgp_peer:ShowBgpPeer + bgp_peer_set = neutronclient.osc.v2.dynamic_routing.bgp_peer:SetBgpPeer + bgp_speaker_list_advertised_routes = neutronclient.osc.v2.dynamic_routing.bgp_speaker:ListRoutesAdvertisedBySpeaker + bgp_speaker_create = neutronclient.osc.v2.dynamic_routing.bgp_speaker:CreateBgpSpeaker + bgp_speaker_delete = neutronclient.osc.v2.dynamic_routing.bgp_speaker:DeleteBgpSpeaker + bgp_speaker_list = neutronclient.osc.v2.dynamic_routing.bgp_speaker:ListBgpSpeaker + bgp_speaker_add_network = neutronclient.osc.v2.dynamic_routing.bgp_speaker:AddNetworkToSpeaker + bgp_speaker_remove_network = neutronclient.osc.v2.dynamic_routing.bgp_speaker:RemoveNetworkFromSpeaker + bgp_speaker_add_peer = neutronclient.osc.v2.dynamic_routing.bgp_speaker:AddPeerToSpeaker + bgp_speaker_remove_peer = neutronclient.osc.v2.dynamic_routing.bgp_speaker:RemovePeerFromSpeaker + bgp_speaker_set = neutronclient.osc.v2.dynamic_routing.bgp_speaker:SetBgpSpeaker + bgp_speaker_show = neutronclient.osc.v2.dynamic_routing.bgp_speaker:ShowBgpSpeaker + + + network_loggable_resources_list = neutronclient.osc.v2.logging.network_log:ListLoggableResource + network_log_create = neutronclient.osc.v2.logging.network_log:CreateNetworkLog + network_log_delete = neutronclient.osc.v2.logging.network_log:DeleteNetworkLog + network_log_list = neutronclient.osc.v2.logging.network_log:ListNetworkLog + network_log_set = neutronclient.osc.v2.logging.network_log:SetNetworkLog + network_log_show = neutronclient.osc.v2.logging.network_log:ShowNetworkLog + + vpn_endpoint_group_create = neutronclient.osc.v2.vpnaas.endpoint_group:CreateEndpointGroup + vpn_endpoint_group_delete = neutronclient.osc.v2.vpnaas.endpoint_group:DeleteEndpointGroup + vpn_endpoint_group_list = neutronclient.osc.v2.vpnaas.endpoint_group:ListEndpointGroup + vpn_endpoint_group_set = neutronclient.osc.v2.vpnaas.endpoint_group:SetEndpointGroup + vpn_endpoint_group_show = neutronclient.osc.v2.vpnaas.endpoint_group:ShowEndpointGroup -[build_sphinx] -all_files = 1 -build-dir = doc/build -source-dir = doc/source + vpn_ike_policy_create = neutronclient.osc.v2.vpnaas.ikepolicy:CreateIKEPolicy + vpn_ike_policy_delete = neutronclient.osc.v2.vpnaas.ikepolicy:DeleteIKEPolicy + vpn_ike_policy_list = neutronclient.osc.v2.vpnaas.ikepolicy:ListIKEPolicy + vpn_ike_policy_set = neutronclient.osc.v2.vpnaas.ikepolicy:SetIKEPolicy + vpn_ike_policy_show = neutronclient.osc.v2.vpnaas.ikepolicy:ShowIKEPolicy -[wheel] -universal = 1 + vpn_ipsec_policy_create = neutronclient.osc.v2.vpnaas.ipsecpolicy:CreateIPsecPolicy + vpn_ipsec_policy_delete = neutronclient.osc.v2.vpnaas.ipsecpolicy:DeleteIPsecPolicy + vpn_ipsec_policy_list = neutronclient.osc.v2.vpnaas.ipsecpolicy:ListIPsecPolicy + vpn_ipsec_policy_set = neutronclient.osc.v2.vpnaas.ipsecpolicy:SetIPsecPolicy + vpn_ipsec_policy_show = neutronclient.osc.v2.vpnaas.ipsecpolicy:ShowIPsecPolicy -[extract_messages] -keywords = _ gettext ngettext l_ lazy_gettext -mapping_file = babel.cfg -output_file = neutronclient/locale/neutronclient.pot + vpn_service_create = neutronclient.osc.v2.vpnaas.vpnservice:CreateVPNService + vpn_service_delete = neutronclient.osc.v2.vpnaas.vpnservice:DeleteVPNService + vpn_service_list = neutronclient.osc.v2.vpnaas.vpnservice:ListVPNService + vpn_service_set = neutronclient.osc.v2.vpnaas.vpnservice:SetVPNSercice + vpn_service_show = neutronclient.osc.v2.vpnaas.vpnservice:ShowVPNService -[compile_catalog] -directory = neutronclient/locale -domain = neutronclient + vpn_ipsec_site_connection_create = neutronclient.osc.v2.vpnaas.ipsec_site_connection:CreateIPsecSiteConnection + vpn_ipsec_site_connection_delete = neutronclient.osc.v2.vpnaas.ipsec_site_connection:DeleteIPsecSiteConnection + vpn_ipsec_site_connection_list = neutronclient.osc.v2.vpnaas.ipsec_site_connection:ListIPsecSiteConnection + vpn_ipsec_site_connection_set = neutronclient.osc.v2.vpnaas.ipsec_site_connection:SetIPsecSiteConnection + vpn_ipsec_site_connection_show = neutronclient.osc.v2.vpnaas.ipsec_site_connection:ShowIPsecSiteConnection -[update_catalog] -domain = neutronclient -output_dir = neutronclient/locale -input_file = neutronclient/locale/neutronclient.pot + network_onboard_subnets = neutronclient.osc.v2.subnet_onboard.subnet_onboard:NetworkOnboardSubnets diff --git a/setup.py b/setup.py index 782bb21f0..cd35c3c35 100644 --- a/setup.py +++ b/setup.py @@ -13,17 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT import setuptools -# In python < 2.7.4, a lazy loading of package `pbr` will break -# setuptools if some other modules registered functions in `atexit`. -# solution from: http://bugs.python.org/issue15881#msg170215 -try: - import multiprocessing # noqa -except ImportError: - pass - setuptools.setup( - setup_requires=['pbr>=1.8'], + setup_requires=['pbr>=2.0.0'], pbr=True) diff --git a/test-requirements.txt b/test-requirements.txt index 0fa7f50cf..d6a160c26 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,20 +1,12 @@ -# The order of packages is significant, because pip processes them in the order -# of appearance. Changing the order has an impact on the overall integration -# process, which may cause wedges in the gate later. -hacking<0.11,>=0.10.0 +hacking>=6.1.0,<6.2.0 # Apache-2.0 -coverage>=3.6 # Apache-2.0 +bandit!=1.6.0,>=1.1.0 # Apache-2.0 +coverage!=4.4,>=4.0 # Apache-2.0 fixtures>=3.0.0 # Apache-2.0/BSD -mox3>=0.7.0 # Apache-2.0 -mock>=2.0 # BSD -oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0 -oslotest>=1.10.0 # Apache-2.0 -python-openstackclient>=2.1.0 # Apache-2.0 -python-subunit>=0.0.18 # Apache-2.0/BSD -reno>=1.8.0 # Apache2 -requests-mock>=1.0 # Apache-2.0 -sphinx!=1.3b1,<1.3,>=1.2.1 # BSD -testrepository>=0.0.18 # Apache-2.0/BSD -testtools>=1.4.0 # MIT -testscenarios>=0.4 # Apache-2.0/BSD -tempest>=12.1.0 # Apache-2.0 +flake8-import-order>=0.19.0,<0.20.0 # LGPLv3 +oslotest>=3.2.0 # Apache-2.0 +osprofiler>=2.3.0 # Apache-2.0 +python-openstackclient>=3.12.0 # Apache-2.0 +requests-mock>=1.2.0 # Apache-2.0 +stestr>=2.0.0 # Apache-2.0 +testtools>=2.2.0 # MIT diff --git a/tools/neutron.bash_completion b/tools/neutron.bash_completion deleted file mode 100644 index 15ed41776..000000000 --- a/tools/neutron.bash_completion +++ /dev/null @@ -1,27 +0,0 @@ -_neutron_opts="" # lazy init -_neutron_flags="" # lazy init -_neutron_opts_exp="" # lazy init -_neutron() -{ - local cur prev nbc cflags - COMPREPLY=() - cur="${COMP_WORDS[COMP_CWORD]}" - prev="${COMP_WORDS[COMP_CWORD-1]}" - - if [ "x$_neutron_opts" == "x" ] ; then - nbc="`neutron bash-completion`" - _neutron_opts="`echo "$nbc" | sed -e "s/--[a-z0-9_-]*//g" -e "s/[ ][ ]*/ /g"`" - _neutron_flags="`echo " $nbc" | sed -e "s/ [^-][^-][a-z0-9_-]*//g" -e "s/[ ][ ]*/ /g"`" - _neutron_opts_exp="`echo "$_neutron_opts" | sed -e "s/[ ]/|/g"`" - fi - - if [[ " ${COMP_WORDS[@]} " =~ " "($_neutron_opts_exp)" " && "$prev" != "help" ]] ; then - COMPLETION_CACHE=~/.neutronclient/*/*-cache - cflags="$_neutron_flags "$(cat $COMPLETION_CACHE 2> /dev/null | tr '\n' ' ') - COMPREPLY=($(compgen -W "${cflags}" -- ${cur})) - else - COMPREPLY=($(compgen -W "${_neutron_opts}" -- ${cur})) - fi - return 0 -} -complete -F _neutron neutron diff --git a/tools/tox_install.sh b/tools/tox_install.sh deleted file mode 100755 index c800a4141..000000000 --- a/tools/tox_install.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env bash - -# Client constraint file contains this client version pin that is in conflict -# with installing the client from source. We should replace the version pin in -# the constraints file before applying it for from-source installation. - -ZUUL_CLONER=/usr/zuul-env/bin/zuul-cloner -BRANCH_NAME=master -CLIENT_NAME=python-neutronclient -requirements_installed=$(echo "import openstack_requirements" | python 2>/dev/null ; echo $?) - -set -e - -CONSTRAINTS_FILE=$1 -shift - -install_cmd="pip install" -if [ $CONSTRAINTS_FILE != "unconstrained" ]; then - - mydir=$(mktemp -dt "$CLIENT_NAME-tox_install-XXXXXXX") - localfile=$mydir/upper-constraints.txt - if [[ $CONSTRAINTS_FILE != http* ]]; then - CONSTRAINTS_FILE=file://$CONSTRAINTS_FILE - fi - curl $CONSTRAINTS_FILE -k -o $localfile - install_cmd="$install_cmd -c$localfile" - - if [ $requirements_installed -eq 0 ]; then - echo "ALREADY INSTALLED" > /tmp/tox_install.txt - echo "Requirements already installed; using existing package" - elif [ -x "$ZUUL_CLONER" ]; then - export ZUUL_BRANCH=${ZUUL_BRANCH-$BRANCH} - echo "ZUUL CLONER" > /tmp/tox_install.txt - pushd $mydir - $ZUUL_CLONER --cache-dir \ - /opt/git \ - --branch $BRANCH_NAME \ - git://git.openstack.org \ - openstack/requirements - cd openstack/requirements - $install_cmd -e . - popd - else - echo "PIP HARDCODE" > /tmp/tox_install.txt - if [ -z "$REQUIREMENTS_PIP_LOCATION" ]; then - REQUIREMENTS_PIP_LOCATION="git+https://git.openstack.org/openstack/requirements@$BRANCH_NAME#egg=requirements" - fi - $install_cmd -U -e ${REQUIREMENTS_PIP_LOCATION} - fi - - edit-constraints $localfile -- $CLIENT_NAME "-e file://$PWD#egg=$CLIENT_NAME" -fi - -$install_cmd -U $* -exit $? diff --git a/tox.ini b/tox.ini index 062ca6724..a939f8b65 100644 --- a/tox.ini +++ b/tox.ini @@ -1,62 +1,94 @@ [tox] -# py3 first to avoid .testrepository incompatibility -envlist = py35,py34,py27,pypy,pep8 -minversion = 1.6 -skipsdist = True +envlist = py39,pep8 +minversion = 3.18.0 +skipsdist = False +ignore_basepython_conflict = True [testenv] +basepython = python3 setenv = VIRTUAL_ENV={envdir} LANG=en_US.UTF-8 LANGUAGE=en_US:en LC_ALL=C + PYTHONWARNINGS=default::DeprecationWarning usedevelop = True -install_command = - {toxinidir}/tools/tox_install.sh {env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages} -deps = -r{toxinidir}/requirements.txt +deps = -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} + -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt # Delete bytecodes from normal directories before running tests. # Note that bytecodes in dot directories will not be deleted # to keep bytecodes of python modules installed into virtualenvs. -commands = sh -c "find . -type d -name '.?*' -prune -o \ +commands = bash -c "find . -type d -name '.?*' -prune -o \ \( -type d -name '__pycache__' -o -type f -name '*.py[co]' \) \ -print0 | xargs -0 rm -rf" - python setup.py testr --testr-args='{posargs}' -whitelist_externals = sh + stestr run {posargs} +allowlist_externals = bash [testenv:pep8] -commands = flake8 +commands = + flake8 + {[testenv:bandit]commands} distribute = false [testenv:venv] -# TODO(ihrachys): remove once infra supports constraints for this target -install_command = pip install -U {opts} {packages} commands = {posargs} -[testenv:functional] -setenv = - OS_TEST_PATH = ./neutronclient/tests/functional/core - OS_NEUTRONCLIENT_EXEC_DIR = {envdir}/bin - -[testenv:functional-adv-svcs] -setenv = - OS_TEST_PATH = ./neutronclient/tests/functional/adv-svcs - OS_NEUTRONCLIENT_EXEC_DIR = {envdir}/bin - [testenv:cover] -# TODO(ihrachys): remove once infra supports constraints for this target -install_command = pip install -U {opts} {packages} +setenv = + {[testenv]setenv} + PYTHON=coverage run --source neutronclient --parallel-mode commands = - python setup.py test --coverage --coverage-package-name=neutronclient --testr-args='{posargs}' - coverage report + stestr run {posargs} + coverage combine + coverage html -d cover + coverage xml -o cover/coverage.xml + coverage report [testenv:docs] +deps = + -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} + -r{toxinidir}/doc/requirements.txt commands = sphinx-build -W -b html doc/source doc/build/html +[testenv:pdf-docs] +deps = {[testenv:docs]deps} +allowlist_externals = + make +commands = + sphinx-build -W -b latex doc/source doc/build/pdf + make -C doc/build/pdf + [testenv:releasenotes] -# TODO(ihrachys): remove once infra supports constraints for this target -install_command = pip install -U {opts} {packages} +deps = + -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} + -r{toxinidir}/doc/requirements.txt commands = sphinx-build -a -E -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html [flake8] +# E126 continuation line over-indented for hanging indent +# E128 continuation line under-indented for visual indent +# H405 multi line docstring summary not separated with an empty line +# I202 Additional newline in a group of imports +# N530 direct neutron imports not allowed +# TODO(amotoki) check the following new rules should be fixed or ignored +# E731 do not assign a lambda expression, use a def +# W504 line break after binary operator +ignore = E126,E128,E731,I202,H405,N530,W504 show-source = true exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,tools +import-order-style = pep8 + +# H106: Don't put vim configuration in source files +# H203: Use assertIs(Not)None to check for None +# H204: Use assert(Not)Equal to check for equality +# H205: Use assert(Greater|Less)(Equal) for comparison +# H904: Delay string interpolations at logging calls +enable-extensions=H106,H203,H204,H205,H904 + +[testenv:bandit] +# B303: blacklist calls: md5, sha1 +# B105: The software contains a hard-coded password, which it uses for its own +# inbound authentication or for outbound communication to external +# components. +deps = -r{toxinidir}/test-requirements.txt +commands = bandit -r neutronclient -x tests -n5 -s B303,B105