diff --git a/.coveragerc b/.coveragerc
index 83f3102c1..94e40afbb 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -1,7 +1,7 @@
[run]
branch = True
source = neutronclient
-omit = neutronclient/openstack/*,neutronclient/tests/*
+omit = neutronclient/tests/*
[report]
-ignore-errors = True
+ignore_errors = True
diff --git a/.gitignore b/.gitignore
index 42921e017..abdf7b022 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,7 @@ build-stamp
cover/*
doc/build/
doc/source/api/
+releasenotes/build/
python_neutronclient.egg-info/*
neutron/vcsversion.py
neutronclient/versioninfo
@@ -16,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 d152a5aa9..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 ./ . $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/CONTRIBUTING.rst b/CONTRIBUTING.rst
new file mode 100644
index 000000000..b726f1d6b
--- /dev/null
+++ b/CONTRIBUTING.rst
@@ -0,0 +1,16 @@
+If you would like to contribute to the development of OpenStack,
+you must follow the steps documented at:
+
+ http://docs.openstack.org/infra/manual/developers.html#development-workflow
+
+Once those steps have been completed, changes to OpenStack
+should be submitted for review via the Gerrit tool, following
+the workflow documented at:
+
+ http://docs.openstack.org/infra/manual/developers.html#development-workflow
+
+Pull requests submitted through GitHub will be ignored.
+
+Bugs should be filed on Launchpad, not GitHub:
+
+ https://bugs.launchpad.net/python-neutronclient
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 ea8e44c88..eaac15c2c 100644
--- a/README.rst
+++ b/README.rst
@@ -1 +1,43 @@
-This is the client API library for Neutron.
+========================
+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.org/project/python-neutronclient/
+ :alt: Latest Version
+
+This is a client library for Neutron built on the Neutron API. It
+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
+* `Online Documentation`_
+* `Launchpad project`_ - release management
+* `Blueprints`_ - feature specifications
+* `Bugs`_ - issue tracking
+* `Source`_
+* `Developer's Guide`_
+
+.. _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://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/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 b3afe8db9..70fd0de2f 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -1,16 +1,22 @@
# -*- coding: utf-8 -*-
#
-import sys
-import os
-
-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', 'sphinx.ext.intersphinx']
+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,21 +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'),
]
-# Example configuration for intersphinx: refer to the Python standard library.
-intersphinx_mapping = {'http://docs.python.org/': None}
+# 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/contributor/index.rst b/doc/source/contributor/index.rst
new file mode 100644
index 000000000..fae3f6b82
--- /dev/null
+++ b/doc/source/contributor/index.rst
@@ -0,0 +1,34 @@
+..
+ Licensed under the Apache License, Version 2.0 (the "License"); you may
+ not use this file except in compliance with the License. You may obtain
+ a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ 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.)
+
+=================
+Contributor Guide
+=================
+
+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::
+ :maxdepth: 2
+
+ transition_to_osc
diff --git a/doc/source/contributor/transition_to_osc.rst b/doc/source/contributor/transition_to_osc.rst
new file mode 100644
index 000000000..9bb952902
--- /dev/null
+++ b/doc/source/contributor/transition_to_osc.rst
@@ -0,0 +1,222 @@
+..
+ Licensed under the Apache License, Version 2.0 (the "License"); you may
+ not use this file except in compliance with the License. You may obtain
+ a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ 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.)
+
+Transition to OpenStack Client
+==============================
+
+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 `_.
+This transition is being guided by the
+`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.
+
+Overview
+--------
+
+This transition will result in the neutron client's ``neutron`` CLI being
+deprecated and then eventually removed. The ``neutron`` CLI will be replaced
+by OSC's networking support available via the ``openstack`` CLI. This is
+similar to the deprecation and removal process for the
+`keystone client's `_
+``keystone`` CLI. The neutron client's Python library won't be deprecated.
+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 `_
+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.
+
+Transition Steps
+----------------
+
+1. **Done:** OSC adds OpenStack Python SDK as a dependency. See the following
+ patch set: https://review.opendev.org/#/c/138745/
+
+2. **Done:** OSC switches its networking support for the
+ `network `_
+ command object to use the OpenStack Python SDK instead of the neutron
+ client's Python library. See the following patch set:
+ https://review.opendev.org/#/c/253348/
+
+3. **Done:** OSC removes its python-neutronclient dependency.
+ 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 `_
+ 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
+ be used. See the following OSC bugs:
+
+ * **Done** `Floating IP CRUD `_
+
+ * **Done** `Port CRUD `_
+
+ * **Done** `Security Group CRUD `_
+
+ * **Done** `Security Group Rule CRUD `_
+
+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 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 `_
+ 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.
+
+Developer Guide
+---------------
+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 |
++---------------------------+-------------------+-------------------------------------------------+
+| Extension | Yes | python-neutronclient |
+| (i.e. neutron stadium) | | (``neutronclient/osc/v2/``) |
++---------------------------+-------------------+-------------------------------------------------+
+| Other | Yes | Applicable project owning networking resource |
++---------------------------+-------------------+-------------------------------------------------+
+
+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. 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
+OpenStack Python SDK to support the underlying networking resource object,
+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.
+
+When adding an ``openstack`` networking command to python-openstackclient,
+you can optionally propose an
+`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 `_
+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.
+
+Developer References
+--------------------
+
+* See `OSC neutron support etherpad `_
+ to determine if an ``openstack`` command is in progress.
+* See `OSC command list `_
+ to determine if an ``openstack`` command exists.
+* See `OSC command spec list `_
+ to determine if an ``openstack`` command spec exists.
+* See `OSC plugin command list `_
+ to determine if an ``openstack`` plugin command exists.
+* See `OSC command structure `_
+ to determine the current ``openstack`` command objects, plugin objects and actions.
+* See `OSC human interface guide `_
+ for guidance on creating new OSC command interfaces.
+* 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
+* Report an OpenStack Python SDK bug: https://bugs.launchpad.net/python-openstacksdk/+filebug
diff --git a/doc/source/index.rst b/doc/source/index.rst
index 612a41ee1..c36d482ed 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -1,63 +1,65 @@
-Python bindings to the OpenStack Network API
-============================================
-
-In order to use the python neutron client directly, you must first obtain an auth token and identify which endpoint you wish to speak to. Once you have done so, you can use the API like so::
-
- >>> import logging
- >>> from neutronclient.neutron import client
- >>> logging.basicConfig(level=logging.DEBUG)
- >>> neutron = client.Client('2.0', endpoint_url=OS_URL, token=OS_TOKEN)
- >>> neutron.format = 'json'
- >>> 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)
-
-
-Command-line Tool
-=================
-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``) or set them in environment variables::
-
- export OS_USERNAME=user
- export OS_PASSWORD=pass
- export OS_TENANT_NAME=tenant
- export OS_AUTH_URL=http://auth.example.com:5000/v2.0
-
-The command line tool will attempt to reauthenticate 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::
-
- export OS_URL=http://neutron.example.org:9696/
- export OS_TOKEN=3bcc3d3a03f44e3d8377f9247b0ad155
-
-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::
-
- export OS_AUTH_STRATEGY=noauth
-
-Once you've configured your authentication parameters, you can run ``neutron -h`` to see a complete listing of available commands.
-
-Release Notes
-=============
-
-2.0
------
-* support Neutron API 2.0
-
-2.2.0
------
-* add security group commands
-* add Lbaas commands
-* allow options put after positional arguments
-* add NVP queue and net gateway commands
-* add commands for agent management extensions
-* add commands for DHCP and L3 agents scheduling
-* support XML request format
-* support pagination options
-
-2.2.2
------
-* improved support for listing a large number of filtered subnets
-* add --endpoint-type and OS_ENDPOINT_TYPE to shell client
-* made the publicURL the default endpoint instead of adminURL
-* add ability to update security group name (requires 2013.2-Havana or later)
-* add flake8 and pbr support for testing and building
+..
+ Licensed under the Apache License, Version 2.0 (the "License"); you may
+ not use this file except in compliance with the License. You may obtain
+ a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ 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.)
+
+==================================
+python-neutronclient documentation
+==================================
+
+This is a client for OpenStack Networking API. It provides
+:doc:`Python API bindings ` (the neutronclient module).
+
+There is
+`OpenStack Client (OSC) `__.
+CLI which support the Networking API.
+
+User Documentation
+------------------
+
+.. toctree::
+ :maxdepth: 2
+
+ cli/index
+ reference/index
+
+Contributor Guide
+-----------------
+
+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
+
+ 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
+-------
+
+Release notes is available at
+http://docs.openstack.org/releasenotes/python-neutronclient/.
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/neutron_test.sh b/neutron_test.sh
deleted file mode 100755
index 53aee7cf4..000000000
--- a/neutron_test.sh
+++ /dev/null
@@ -1,128 +0,0 @@
-#!/bin/bash
-set -x
-function die() {
- local exitcode=$?
- set +o xtrace
- echo $@
- exit $exitcode
-}
-
-noauth_tenant_id=me
-if [ $1 == 'noauth' ]; then
- NOAUTH="--tenant_id $noauth_tenant_id"
-else
- NOAUTH=
-fi
-
-FORMAT=" --request-format xml"
-
-# test the CRUD of network
-network=mynet1
-neutron net-create $FORMAT $NOAUTH $network || die "fail to create network $network"
-temp=`neutron net-list $FORMAT -- --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 $FORMAT $network || die "fail to show network $network"
-neutron net-show $FORMAT $network_id || die "fail to show network $network_id"
-
-neutron net-update $FORMAT $network --admin_state_up False || die "fail to update network $network"
-neutron net-update $FORMAT $network_id --admin_state_up True || die "fail to update network $network_id"
-
-neutron net-list $FORMAT -c id -- --id fakeid || die "fail to list networks with column selection on empty list"
-
-# test the CRUD of subnet
-subnet=mysubnet1
-cidr=10.0.1.3/24
-neutron subnet-create $FORMAT $NOAUTH $network $cidr --name $subnet || die "fail to create subnet $subnet"
-tempsubnet=`neutron subnet-list $FORMAT -- --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 $FORMAT -- --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 $FORMAT $subnet || die "fail to show subnet $subnet"
-neutron subnet-show $FORMAT $subnet_id || die "fail to show subnet $subnet_id"
-
-neutron subnet-update $FORMAT $subnet --dns_namesevers host1 || die "fail to update subnet $subnet"
-neutron subnet-update $FORMAT $subnet_id --dns_namesevers host2 || die "fail to update subnet $subnet_id"
-
-# test the crud of ports
-port=myport1
-neutron port-create $FORMAT $NOAUTH $network --name $port || die "fail to create port $port"
-tempport=`neutron port-list $FORMAT -- --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 $FORMAT -- --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 $FORMAT $port || die "fail to show port $port"
-neutron port-show $FORMAT $port_id || die "fail to show port $port_id"
-
-neutron port-update $FORMAT $port --device_id deviceid1 || die "fail to update port $port"
-neutron port-update $FORMAT $port_id --device_id deviceid2 || die "fail to update port $port_id"
-
-# test quota commands RUD
-DEFAULT_NETWORKS=10
-DEFAULT_PORTS=50
-tenant_id=tenant_a
-tenant_id_b=tenant_b
-neutron quota-update $FORMAT --tenant_id $tenant_id --network 30 || die "fail to update quota for tenant $tenant_id"
-neutron quota-update $FORMAT --tenant_id $tenant_id_b --network 20 || die "fail to update quota for tenant $tenant_id"
-networks=`neutron quota-list $FORMAT -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 $FORMAT -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 $FORMAT --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 $FORMAT --tenant_id $tenant_id || die "fail to delete quota for tenant $tenant_id"
-networks=`neutron quota-show $FORMAT --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 $FORMAT --port 99 || die "fail to update quota for self"
- ports=`neutron quota-show $FORMAT | grep port | awk -F'|' '{print $3}'`
- if [ $ports -ne 99 ]; then
- die "ports quota should be 99"
- fi
-
- ports=`neutron quota-list $FORMAT -c port | grep 99 | awk '{print $2}'`
- if [ $ports -ne 99 ]; then
- die "ports quota should be 99"
- fi
- neutron quota-delete $FORMAT || die "fail to delete quota for tenant self"
- ports=`neutron quota-show $FORMAT | 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 $FORMAT --port 100
- if [ $? -eq 0 ]; then
- die "without valid context on server, quota update command should fail."
- fi
- neutron quota-show $FORMAT
- if [ $? -eq 0 ]; then
- die "without valid context on server, quota show command should fail."
- fi
- neutron quota-delete $FORMAT
- if [ $? -eq 0 ]; then
- die "without valid context on server, quota delete command should fail."
- fi
- neutron quota-list $FORMAT || die "fail to update quota for self"
-fi
diff --git a/neutronclient/_i18n.py b/neutronclient/_i18n.py
new file mode 100644
index 000000000..50ac3c5bf
--- /dev/null
+++ b/neutronclient/_i18n.py
@@ -0,0 +1,31 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import oslo_i18n
+
+
+DOMAIN = 'neutronclient'
+
+_translators = oslo_i18n.TranslatorFactory(domain=DOMAIN)
+
+# The translation function using the well-known name "_"
+_ = _translators.primary
+
+# The contextual translation function using the name "_C"
+_C = _translators.contextual_form
+
+# The plural translation function using the name "_P"
+_P = _translators.plural_form
+
+
+def get_available_languages():
+ return oslo_i18n.get_available_languages(DOMAIN)
diff --git a/neutronclient/client.py b/neutronclient/client.py
index a8f8d78d7..e25f575f3 100644
--- a/neutronclient/client.py
+++ b/neutronclient/client.py
@@ -14,129 +14,87 @@
# under the License.
#
-try:
- import json
-except ImportError:
- import simplejson as json
import logging
import os
-import httplib2
+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
-from neutronclient.openstack.common.gettextutils import _
-_logger = logging.getLogger(__name__)
+osprofiler_web = importutils.try_import("osprofiler.web")
-# httplib2 retries requests on socket.timeout which
-# is not idempotent and can lead to orhan objects.
-# See: https://code.google.com/p/httplib2/issues/detail?id=124
-httplib2.RETRIES = 1
+_logger = logging.getLogger(__name__)
if os.environ.get('NEUTRONCLIENT_DEBUG'):
ch = logging.StreamHandler()
_logger.setLevel(logging.DEBUG)
_logger.addHandler(ch)
+ _requests_log_level = logging.DEBUG
+else:
+ _requests_log_level = logging.WARNING
-
-class ServiceCatalog(object):
- """Helper methods for dealing with a Keystone Service Catalog."""
-
- def __init__(self, resource_dict):
- self.catalog = resource_dict
-
- def get_token(self):
- """Fetch token details from service catalog."""
- token = {'id': self.catalog['access']['token']['id'],
- 'expires': self.catalog['access']['token']['expires'], }
- try:
- token['user_id'] = self.catalog['access']['user']['id']
- token['tenant_id'] = (
- self.catalog['access']['token']['tenant']['id'])
- except Exception:
- # just leave the tenant and user out if it doesn't exist
- pass
- return token
-
- def url_for(self, attr=None, filter_value=None,
- service_type='network', endpoint_type='publicURL'):
- """Fetch the URL from the Neutron service for
- a particular endpoint type. If none given, return
- publicURL.
- """
-
- catalog = self.catalog['access'].get('serviceCatalog', [])
- matching_endpoints = []
- for service in catalog:
- if service['type'] != service_type:
- continue
-
- endpoints = service['endpoints']
- for endpoint in endpoints:
- if not filter_value or endpoint.get(attr) == filter_value:
- matching_endpoints.append(endpoint)
-
- if not matching_endpoints:
- raise exceptions.EndpointNotFound()
- elif len(matching_endpoints) > 1:
- raise exceptions.AmbiguousEndpoints(message=matching_endpoints)
- else:
- if endpoint_type not in matching_endpoints[0]:
- raise exceptions.EndpointTypeNotFound(message=endpoint_type)
-
- return matching_endpoints[0][endpoint_type]
+logging.getLogger("requests").setLevel(_requests_log_level)
+MAX_URI_LEN = 8192
+USER_AGENT = 'python-neutronclient'
+REQ_ID_HEADER = 'X-OpenStack-Request-ID'
-class HTTPClient(httplib2.Http):
+class HTTPClient(object):
"""Handles the REST calls and responses, include authn."""
- USER_AGENT = 'python-neutronclient'
+ CONTENT_TYPE = 'application/json'
- def __init__(self, username=None, tenant_name=None, tenant_id=None,
+ @debtcollector.renames.renamed_kwarg(
+ 'tenant_id', 'project_id', replace=True)
+ @debtcollector.renames.renamed_kwarg(
+ 'tenant_name', 'project_name', replace=True)
+ def __init__(self, username=None, user_id=None,
+ project_name=None, project_id=None,
password=None, auth_url=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):
- super(HTTPClient, self).__init__(timeout=timeout, ca_certs=ca_cert)
+ auth_strategy='keystone', ca_cert=None, cert=None,
+ log_credentials=False, service_type='network',
+ global_request_id=None, **kwargs):
self.username = username
- self.tenant_name = tenant_name
- self.tenant_id = tenant_id
+ self.user_id = user_id
+ self.project_name = project_name
+ self.project_id = project_id
self.password = password
self.auth_url = auth_url.rstrip('/') if auth_url else None
self.service_type = service_type
self.endpoint_type = endpoint_type
self.region_name = region_name
+ self.timeout = timeout
self.auth_token = token
self.auth_tenant_id = None
self.auth_user_id = None
- self.content_type = 'application/json'
self.endpoint_url = endpoint_url
self.auth_strategy = auth_strategy
self.log_credentials = log_credentials
- # httplib2 overrides
- self.disable_ssl_certificate_validation = insecure
+ self.global_request_id = global_request_id
+ self.cert = cert
+ if insecure:
+ self.verify_cert = False
+ else:
+ self.verify_cert = ca_cert if ca_cert else True
def _cs_request(self, *args, **kwargs):
kargs = {}
kargs.setdefault('headers', kwargs.get('headers', {}))
- kargs['headers']['User-Agent'] = self.USER_AGENT
-
- if 'content_type' in kwargs:
- kargs['headers']['Content-Type'] = kwargs['content_type']
- kargs['headers']['Accept'] = kwargs['content_type']
- else:
- kargs['headers']['Content-Type'] = self.content_type
- kargs['headers']['Accept'] = self.content_type
+ kargs['headers']['User-Agent'] = USER_AGENT
if 'body' in kwargs:
kargs['body'] = kwargs['body']
- args = utils.safe_encode_list(args)
- kargs = utils.safe_encode_dict(kargs)
if self.log_credentials:
log_kargs = kargs
@@ -146,22 +104,27 @@ def _cs_request(self, *args, **kwargs):
utils.http_log_req(_logger, args, log_kargs)
try:
resp, body = self.request(*args, **kargs)
- except httplib2.SSLHandshakeError as e:
- raise exceptions.SslCertificateValidationError(reason=e)
+ except requests.exceptions.SSLError as 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)
- finally:
- # Temporary Fix for gate failures. RPC calls and HTTP requests
- # seem to be stepping on each other resulting in bogus fd's being
- # picked up for making http requests
- self.connections.clear()
+ raise exceptions.ConnectionFailed(reason=str(e))
utils.http_log_resp(_logger, resp, body)
- status_code = self.get_status_code(resp)
- if status_code == 401:
+
+ # 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
@@ -180,20 +143,62 @@ def authenticate_and_fetch_endpoint_url(self):
elif not self.endpoint_url:
self.endpoint_url = self._get_endpoint_url()
+ def request(self, url, method, body=None, headers=None, **kwargs):
+ """Request without authentication."""
+
+ content_type = kwargs.pop('content_type', None) or 'application/json'
+ headers = headers or {}
+ headers.setdefault('Accept', content_type)
+
+ 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,
+ url,
+ data=body,
+ headers=headers,
+ verify=self.verify_cert,
+ cert=self.cert,
+ timeout=self.timeout,
+ **kwargs)
+
+ return resp, resp.text
+
+ def _check_uri_length(self, action):
+ uri_len = len(self.endpoint_url) + len(action)
+ if uri_len > MAX_URI_LEN:
+ raise exceptions.RequestURITooLong(
+ excess=uri_len - MAX_URI_LEN)
+
def do_request(self, url, method, **kwargs):
+ # Ensure client always has correct uri - do not guesstimate anything
self.authenticate_and_fetch_endpoint_url()
+ self._check_uri_length(url)
+
# Perform the request once. If we get a 401 back then it
# 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
resp, body = self._cs_request(self.endpoint_url + 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)
@@ -201,60 +206,68 @@ def do_request(self, url, method, **kwargs):
def _extract_service_catalog(self, body):
"""Set the client's service catalog from the response data."""
- self.service_catalog = ServiceCatalog(body)
- try:
- sc = self.service_catalog.get_token()
- self.auth_token = sc['id']
- self.auth_tenant_id = sc.get('tenant_id')
- self.auth_user_id = sc.get('user_id')
- except KeyError:
- raise exceptions.Unauthorized()
+ self.auth_ref = access.create(body=body)
+ self.service_catalog = self.auth_ref.service_catalog
+ self.auth_token = self.auth_ref.auth_token
+ self.auth_tenant_id = self.auth_ref.tenant_id
+ self.auth_user_id = self.auth_ref.user_id
+
if not self.endpoint_url:
self.endpoint_url = self.service_catalog.url_for(
- attr='region', filter_value=self.region_name,
+ region_name=self.region_name,
service_type=self.service_type,
- endpoint_type=self.endpoint_type)
+ interface=self.endpoint_type)
- def authenticate(self):
- if self.auth_strategy != 'keystone':
- raise exceptions.Unauthorized(message=_('Unknown auth strategy'))
- if self.tenant_id:
- body = {'auth': {'passwordCredentials':
- {'username': self.username,
- 'password': self.password, },
- 'tenantId': self.tenant_id, }, }
+ def _authenticate_keystone(self):
+ if self.user_id:
+ creds = {'userId': self.user_id,
+ 'password': self.password}
+ else:
+ creds = {'username': self.username,
+ 'password': self.password}
+
+ if self.project_id:
+ body = {'auth': {'passwordCredentials': creds,
+ 'tenantId': self.project_id, }, }
else:
- body = {'auth': {'passwordCredentials':
- {'username': self.username,
- 'password': self.password, },
- 'tenantName': self.tenant_name, }, }
+ body = {'auth': {'passwordCredentials': creds,
+ 'tenantName': self.project_name, }, }
if self.auth_url is None:
raise exceptions.NoAuthURLProvided()
token_url = self.auth_url + "/tokens"
-
- # Make sure we follow redirects when trying to reach Keystone
- tmp_follow_all_redirects = self.follow_all_redirects
- self.follow_all_redirects = True
- try:
- resp, resp_body = self._cs_request(token_url, "POST",
- body=json.dumps(body),
- content_type="application/json")
- finally:
- self.follow_all_redirects = tmp_follow_all_redirects
- status_code = self.get_status_code(resp)
- if status_code != 200:
+ resp, resp_body = self._cs_request(token_url, "POST",
+ 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:
resp_body = None
self._extract_service_catalog(resp_body)
+ def _authenticate_noauth(self):
+ if not self.endpoint_url:
+ message = _('For "noauth" authentication strategy, the endpoint '
+ 'must be specified either in the constructor or '
+ 'using --os-url')
+ raise exceptions.Unauthorized(message=message)
+
+ def authenticate(self):
+ if self.auth_strategy == 'keystone':
+ self._authenticate_keystone()
+ elif self.auth_strategy == 'noauth':
+ self._authenticate_noauth()
+ else:
+ err_msg = _('Unknown auth strategy: %s') % self.auth_strategy
+ raise exceptions.Unauthorized(message=err_msg)
+
def _get_endpoint_url(self):
if self.auth_url is None:
raise exceptions.NoAuthURLProvided()
@@ -268,13 +281,13 @@ 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):
+ endpoint.get('region') == self.region_name):
if self.endpoint_type not in endpoint:
raise exceptions.EndpointTypeNotFound(
- message=self.endpoint_type)
+ type_=self.endpoint_type)
return endpoint[self.endpoint_type]
raise exceptions.EndpointNotFound()
@@ -285,13 +298,142 @@ def get_auth_info(self):
'auth_user_id': self.auth_user_id,
'endpoint_url': self.endpoint_url}
- def get_status_code(self, response):
- """Returns the integer status code from the response.
+ def get_auth_ref(self):
+ return getattr(self, 'auth_ref', None)
+
+
+class SessionClient(adapter.Adapter):
+
+ def request(self, *args, **kwargs):
+ kwargs.setdefault('authenticated', False)
+ kwargs.setdefault('raise_exc', False)
+
+ content_type = kwargs.pop('content_type', None) or 'application/json'
+
+ 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:
+ pass
+
+ if kwargs.get('data'):
+ headers.setdefault('Content-Type', content_type)
+
+ kwargs['headers'] = headers
+ resp = super(SessionClient, self).request(*args, **kwargs)
+ return resp, resp.text
+
+ def _check_uri_length(self, url):
+ uri_len = len(self.endpoint_url) + len(url)
+ if uri_len > MAX_URI_LEN:
+ raise exceptions.RequestURITooLong(
+ excess=uri_len - MAX_URI_LEN)
+
+ def do_request(self, url, method, **kwargs):
+ kwargs.setdefault('authenticated', True)
+ self._check_uri_length(url)
+ return self.request(url, method, **kwargs)
+
+ @property
+ def endpoint_url(self):
+ # NOTE(jamielennox): This is used purely by the CLI and should be
+ # removed when the CLI gets smarter.
+ return self.get_endpoint()
+
+ @property
+ def auth_token(self):
+ # NOTE(jamielennox): This is used purely by the CLI and should be
+ # removed when the CLI gets smarter.
+ return self.get_token()
+
+ def authenticate(self):
+ # NOTE(jamielennox): This is used purely by the CLI and should be
+ # removed when the CLI gets smarter.
+ self.get_token()
- Either a Webob.Response (used in testing) or httplib.Response
- is returned.
- """
- if hasattr(response, 'status_int'):
- return response.status_int
+ def get_auth_info(self):
+ auth_info = {'auth_token': self.auth_token,
+ 'endpoint_url': self.endpoint_url}
+
+ # NOTE(jamielennox): This is the best we can do here. It will work
+ # with identity plugins which is the primary case but we should
+ # deprecate it's usage as much as possible.
+ try:
+ get_access = (self.auth or self.session.auth).get_access
+ except AttributeError:
+ pass
else:
- return response.status
+ auth_ref = get_access(self.session)
+
+ auth_info['auth_tenant_id'] = auth_ref.project_id
+ auth_info['auth_user_id'] = auth_ref.user_id
+
+ 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.
+@debtcollector.renames.renamed_kwarg('tenant_id', 'project_id', replace=True)
+@debtcollector.renames.renamed_kwarg(
+ 'tenant_name', 'project_name', replace=True)
+def construct_http_client(username=None,
+ user_id=None,
+ project_name=None,
+ project_id=None,
+ password=None,
+ auth_url=None,
+ token=None,
+ region_name=None,
+ timeout=None,
+ endpoint_url=None,
+ insecure=False,
+ endpoint_type='public',
+ log_credentials=None,
+ auth_strategy='keystone',
+ ca_cert=None,
+ cert=None,
+ service_type='network',
+ session=None,
+ global_request_id=None,
+ **kwargs):
+
+ if session:
+ kwargs.setdefault('user_agent', USER_AGENT)
+ kwargs.setdefault('interface', endpoint_type)
+ 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
+ # to test that they were provided in this mode. Should also
+ # refactor to use kwargs.
+ return HTTPClient(username=username,
+ password=password,
+ project_id=project_id,
+ project_name=project_name,
+ user_id=user_id,
+ auth_url=auth_url,
+ token=token,
+ endpoint_url=endpoint_url,
+ insecure=insecure,
+ timeout=timeout,
+ region_name=region_name,
+ endpoint_type=endpoint_type,
+ service_type=service_type,
+ ca_cert=ca_cert,
+ cert=cert,
+ log_credentials=log_credentials,
+ auth_strategy=auth_strategy,
+ global_request_id=global_request_id)
diff --git a/neutronclient/common/__init__.py b/neutronclient/common/__init__.py
index fb715cf41..e69de29bb 100644
--- a/neutronclient/common/__init__.py
+++ b/neutronclient/common/__init__.py
@@ -1,26 +0,0 @@
-# Copyright 2011 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 gettext
-
-t = gettext.translation('neutronclient', fallback=True)
-try:
- ugettext = t.ugettext # Python 2
-except AttributeError:
- ugettext = t.gettext # Python 3
-
-
-def _(msg):
- return ugettext(msg)
diff --git a/neutronclient/common/clientmanager.py b/neutronclient/common/clientmanager.py
index b36d21e5a..56d3d3c12 100644
--- a/neutronclient/common/clientmanager.py
+++ b/neutronclient/common/clientmanager.py
@@ -17,18 +17,14 @@
"""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.
- """
+ """Descriptor class for caching created client handles."""
def __init__(self, factory):
self.factory = factory
@@ -42,18 +38,24 @@ def __get__(self, instance, owner):
class ClientManager(object):
- """Manages access to API clients, including authentication.
- """
+ """Manages access to API clients, including authentication."""
neutron = ClientCache(neutron_client.make_client)
# Provide support for old quantum commands (for example
# in stable versions)
quantum = neutron
+ @debtcollector.renames.renamed_kwarg(
+ 'tenant_id', 'project_id', replace=True)
+ @debtcollector.renames.renamed_kwarg(
+ 'tenant_name', 'project_name', replace=True)
def __init__(self, token=None, url=None,
auth_url=None,
endpoint_type=None,
- tenant_name=None, tenant_id=None,
- username=None, password=None,
+ project_name=None,
+ project_id=None,
+ username=None,
+ user_id=None,
+ password=None,
region_name=None,
api_version=None,
auth_strategy=None,
@@ -61,15 +63,23 @@ def __init__(self, token=None, url=None,
ca_cert=None,
log_credentials=False,
service_type=None,
+ service_name=None,
+ timeout=None,
+ retries=0,
+ raise_errors=True,
+ session=None,
+ auth=None,
):
self._token = token
self._url = url
self._auth_url = auth_url
self._service_type = service_type
+ self._service_name = service_name
self._endpoint_type = endpoint_type
- self._tenant_name = tenant_name
- self._tenant_id = tenant_id
+ self._project_name = project_name
+ self._project_id = project_id
self._username = username
+ self._user_id = user_id
self._password = password
self._region_name = region_name
self._api_version = api_version
@@ -78,21 +88,31 @@ def __init__(self, token=None, url=None,
self._insecure = insecure
self._ca_cert = ca_cert
self._log_credentials = log_credentials
+ self._timeout = timeout
+ self._retries = retries
+ self._raise_errors = raise_errors
+ self._session = session
+ self._auth = auth
return
def initialize(self):
if not self._url:
- httpclient = client.HTTPClient(
+ httpclient = client.construct_http_client(
username=self._username,
- tenant_name=self._tenant_name,
- tenant_id=self._tenant_id,
+ user_id=self._user_id,
+ project_name=self._project_name,
+ project_id=self._project_id,
password=self._password,
region_name=self._region_name,
auth_url=self._auth_url,
service_type=self._service_type,
+ service_name=self._service_name,
endpoint_type=self._endpoint_type,
insecure=self._insecure,
ca_cert=self._ca_cert,
+ timeout=self._timeout,
+ session=self._session,
+ auth=self._auth,
log_credentials=self._log_credentials)
httpclient.authenticate()
# Populate other password flow attributes
diff --git a/neutronclient/common/constants.py b/neutronclient/common/constants.py
index 305b5df05..ccb22cd80 100644
--- a/neutronclient/common/constants.py
+++ b/neutronclient/common/constants.py
@@ -14,18 +14,6 @@
# limitations under the License.
-EXT_NS = '_extension_ns'
-XML_NS_V20 = 'http://openstack.org/quantum/api/v2.0'
-XSI_NAMESPACE = "http://www.w3.org/2001/XMLSchema-instance"
-XSI_ATTR = "xsi:nil"
-XSI_NIL_ATTR = "xmlns:xsi"
-TYPE_XMLNS = "xmlns:quantum"
-TYPE_ATTR = "quantum:type"
-VIRTUAL_ROOT_KEY = "_v_root"
-ATOM_NAMESPACE = "http://www.w3.org/2005/Atom"
-ATOM_XMLNS = "xmlns:atom"
-ATOM_LINK_NOTATION = "{%s}link" % ATOM_NAMESPACE
-
TYPE_BOOL = "bool"
TYPE_INT = "int"
TYPE_LONG = "long"
@@ -36,6 +24,7 @@
PLURALS = {'networks': 'network',
'ports': 'port',
'subnets': 'subnet',
+ 'subnetpools': 'subnetpool',
'dns_nameservers': 'dns_nameserver',
'host_routes': 'host_route',
'allocation_pools': 'allocation_pool',
diff --git a/neutronclient/common/exceptions.py b/neutronclient/common/exceptions.py
index d7e3848be..443f78155 100644
--- a/neutronclient/common/exceptions.py
+++ b/neutronclient/common/exceptions.py
@@ -13,28 +13,46 @@
# License for the specific language governing permissions and limitations
# under the License.
-from neutronclient.common import _
+from oslo_utils import encodeutils
+
+from neutronclient._i18n import _
"""
Neutron base exception handling.
+
+Exceptions are classified into three categories:
+* Exceptions corresponding to exceptions from neutron server:
+ This type of exceptions should inherit one of exceptions
+ in HTTP_EXCEPTION_MAP.
+* Exceptions from client library:
+ This type of exceptions should inherit NeutronClientException.
+* Exceptions from CLI code:
+ This type of exceptions should inherit NeutronCLIError.
"""
+# 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
+ """Base Neutron Exception.
- Taken from nova.exception.NovaException
To correctly use this class, inherit from it and define
a 'message' property. That message will get printf'd
with the keyword arguments provided to the constructor.
-
"""
message = _("An unknown exception occurred.")
- def __init__(self, **kwargs):
+ 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
@@ -43,95 +61,154 @@ def __str__(self):
return self._error_string
-class NotFound(NeutronException):
+class NeutronClientException(NeutronException):
+ """Base exception which exceptions from Neutron are mapped into.
+
+ NOTE: on the client side, we use different exception types in order
+ to allow client library users to handle server exceptions in try...except
+ blocks. The actual error message is the one generated on the server side.
+ """
+
+ status_code = 0
+ req_ids_msg = _("Neutron server returns request_ids: %s")
+ request_ids = []
+
+ def __init__(self, message=None, **kwargs):
+ self.request_ids = kwargs.get('request_ids')
+ if 'status_code' in kwargs:
+ self.status_code = kwargs['status_code']
+ if self.request_ids:
+ req_ids_msg = self.req_ids_msg % self.request_ids
+ if message:
+ message = _('%(msg)s\n%(id)s') % {'msg': message,
+ 'id': req_ids_msg}
+ else:
+ message = req_ids_msg
+ super(NeutronClientException, self).__init__(message, **kwargs)
+
+
+# Base exceptions from Neutron
+
+class BadRequest(NeutronClientException):
+ status_code = 400
+
+
+class Unauthorized(NeutronClientException):
+ status_code = 401
+ message = _("Unauthorized: bad credentials.")
+
+
+class Forbidden(NeutronClientException):
+ status_code = 403
+ message = _("Forbidden: your credentials don't give you access to this "
+ "resource.")
+
+
+class NotFound(NeutronClientException):
+ status_code = 404
+
+
+class Conflict(NeutronClientException):
+ status_code = 409
+
+
+class InternalServerError(NeutronClientException):
+ status_code = 500
+
+
+class ServiceUnavailable(NeutronClientException):
+ status_code = 503
+
+
+HTTP_EXCEPTION_MAP = {
+ 400: BadRequest,
+ 401: Unauthorized,
+ 403: Forbidden,
+ 404: NotFound,
+ 409: Conflict,
+ 500: InternalServerError,
+ 503: ServiceUnavailable,
+}
+
+
+# Exceptions mapped to Neutron server exceptions
+# These are defined if a user of client library needs specific exception.
+# Exception name should be + 'Client'
+# e.g., NetworkNotFound -> NetworkNotFoundClient
+
+class NetworkNotFoundClient(NotFound):
pass
-class NeutronClientException(NeutronException):
+class PortNotFoundClient(NotFound):
+ pass
- def __init__(self, **kwargs):
- message = kwargs.get('message')
- self.status_code = kwargs.get('status_code', 0)
- if message:
- self.message = message
- super(NeutronClientException, self).__init__(**kwargs)
+
+class StateInvalidClient(BadRequest):
+ pass
-# NOTE: on the client side, we use different exception types in order
-# to allow client library users to handle server exceptions in try...except
-# blocks. The actual error message is the one generated on the server side
-class NetworkNotFoundClient(NeutronClientException):
+class NetworkInUseClient(Conflict):
pass
-class PortNotFoundClient(NeutronClientException):
+class PortInUseClient(Conflict):
pass
-class MalformedResponseBody(NeutronException):
- message = _("Malformed response body: %(reason)s")
+class IpAddressInUseClient(Conflict):
+ pass
-class StateInvalidClient(NeutronClientException):
+class IpAddressAlreadyAllocatedClient(Conflict):
pass
-class NetworkInUseClient(NeutronClientException):
+class InvalidIpForNetworkClient(BadRequest):
pass
-class PortInUseClient(NeutronClientException):
+class InvalidIpForSubnetClient(BadRequest):
pass
-class IpAddressInUseClient(NeutronClientException):
+class OverQuotaClient(Conflict):
pass
-class AlreadyAttachedClient(NeutronClientException):
+class IpAddressGenerationFailureClient(Conflict):
pass
-class IpAddressGenerationFailureClient(NeutronClientException):
+class MacAddressInUseClient(Conflict):
pass
-class ExternalIpAddressExhaustedClient(NeutronClientException):
+class HostNotCompatibleWithFixedIpsClient(Conflict):
pass
-class Unauthorized(NeutronClientException):
- message = _("Unauthorized: bad credentials.")
+class ExternalIpAddressExhaustedClient(BadRequest):
+ pass
-class Forbidden(NeutronClientException):
- message = _("Forbidden: your credentials don't give you access to this "
- "resource.")
+# Exceptions from client library
+
+class NoAuthURLProvided(Unauthorized):
+ message = _("auth_url was not provided to the Neutron client")
class EndpointNotFound(NeutronClientException):
- """Could not find Service or Region in Service Catalog."""
message = _("Could not find Service or Region in Service Catalog.")
class EndpointTypeNotFound(NeutronClientException):
- """Could not find endpoint type in Service Catalog."""
-
- def __str__(self):
- msg = _("Could not find endpoint type %s in Service Catalog.")
- return msg % repr(self.message)
+ message = _("Could not find endpoint type %(type_)s in Service Catalog.")
class AmbiguousEndpoints(NeutronClientException):
- """Found more than one matching endpoint in Service Catalog."""
-
- def __str__(self):
- return _("AmbiguousEndpoints: %s") % repr(self.message)
-
-
-class NeutronCLIError(NeutronClientException):
- """Exception raised when command line parsing fails."""
- pass
+ message = _("Found more than one matching endpoint in Service Catalog: "
+ "%(matching_endpoints)")
class RequestURITooLong(NeutronClientException):
@@ -146,47 +223,38 @@ class ConnectionFailed(NeutronClientException):
message = _("Connection to neutron failed: %(reason)s")
-class BadInputError(Exception):
- """Error resulting from a client sending bad input to a server."""
- pass
-
+class SslCertificateValidationError(NeutronClientException):
+ message = _("SSL certificate validation has failed: %(reason)s")
-class NoAuthURLProvided(BadInputError):
- message = _("auth_url was not provided to the Neutron client")
+class MalformedResponseBody(NeutronClientException):
+ message = _("Malformed response body: %(reason)s")
-class Error(Exception):
- def __init__(self, message=None):
- super(Error, self).__init__(message)
+class InvalidContentType(NeutronClientException):
+ message = _("Invalid content type %(content_type)s.")
-class MalformedRequestBody(NeutronException):
- message = _("Malformed request body: %(reason)s")
+# Command line exceptions
-class Invalid(Error):
+class NeutronCLIError(NeutronException):
+ """Exception raised when command line parsing fails."""
pass
-class InvalidContentType(Invalid):
- message = _("Invalid content type %(content_type)s.")
-
-
-class UnsupportedVersion(Exception):
- """Indicates that the user is trying to use an unsupported
- version of the API
- """
+class CommandError(NeutronCLIError):
pass
-class CommandError(Exception):
+class UnsupportedVersion(NeutronCLIError):
+ """Indicates usage of an unsupported API version
+
+ Indicates that the user is trying to use an unsupported version of
+ the API.
+ """
pass
-class NeutronClientNoUniqueMatch(NeutronClientException):
+class NeutronClientNoUniqueMatch(NeutronCLIError):
message = _("Multiple %(resource)s matches found for name '%(name)s',"
" use an ID to be more specific.")
-
-
-class SslCertificateValidationError(NeutronClientException):
- message = _("SSL certificate validation has failed: %(reason)s")
diff --git a/neutronclient/common/extension.py b/neutronclient/common/extension.py
new file mode 100644
index 000000000..00a035d6c
--- /dev/null
+++ b/neutronclient/common/extension.py
@@ -0,0 +1,85 @@
+# 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.
+#
+from stevedore import extension
+
+from neutronclient.neutron import v2_0 as neutronV20
+
+
+def _discover_via_entry_points():
+ emgr = extension.ExtensionManager('neutronclient.extension',
+ invoke_on_load=False)
+ return ((ext.name, ext.plugin) for ext in emgr)
+
+
+class NeutronClientExtension(neutronV20.NeutronCommand):
+ pagination_support = False
+ _formatters = {}
+ sorting_support = False
+
+
+class ClientExtensionShow(NeutronClientExtension, neutronV20.ShowCommand):
+ def take_action(self, parsed_args):
+ # NOTE(mdietz): Calls 'execute' to provide a consistent pattern
+ # for any implementers adding extensions with
+ # regard to any other extension verb.
+ return self.execute(parsed_args)
+
+ def execute(self, parsed_args):
+ return super(ClientExtensionShow, self).take_action(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
+ # regard to any other extension verb.
+ return self.execute(parsed_args)
+
+ def execute(self, parsed_args):
+ return super(ClientExtensionList, self).take_action(parsed_args)
+
+
+class ClientExtensionDelete(NeutronClientExtension, neutronV20.DeleteCommand):
+ def take_action(self, parsed_args):
+ # NOTE(mdietz): Calls 'execute' to provide a consistent pattern
+ # for any implementers adding extensions with
+ # regard to any other extension verb.
+ return self.execute(parsed_args)
+
+ def execute(self, parsed_args):
+ return super(ClientExtensionDelete, self).take_action(parsed_args)
+
+
+class ClientExtensionCreate(NeutronClientExtension, neutronV20.CreateCommand):
+ def take_action(self, parsed_args):
+ # NOTE(mdietz): Calls 'execute' to provide a consistent pattern
+ # for any implementers adding extensions with
+ # regard to any other extension verb.
+ return self.execute(parsed_args)
+
+ def execute(self, parsed_args):
+ return super(ClientExtensionCreate, self).take_action(parsed_args)
+
+
+class ClientExtensionUpdate(NeutronClientExtension, neutronV20.UpdateCommand):
+ def take_action(self, parsed_args):
+ # NOTE(mdietz): Calls 'execute' to provide a consistent pattern
+ # for any implementers adding extensions with
+ # regard to any other extension verb.
+ return self.execute(parsed_args)
+
+ def execute(self, parsed_args):
+ return super(ClientExtensionUpdate, self).take_action(parsed_args)
diff --git a/neutronclient/common/serializer.py b/neutronclient/common/serializer.py
index b8d6d81ed..d4c8bbbb6 100644
--- a/neutronclient/common/serializer.py
+++ b/neutronclient/common/serializer.py
@@ -12,22 +12,11 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
-#
-###
-### Codes from neutron wsgi
-###
-
-import logging
-from xml.etree import ElementTree as etree
-from xml.parsers import expat
+from oslo_serialization import jsonutils
-from neutronclient.common import constants
+from neutronclient._i18n import _
from neutronclient.common import exceptions as exception
-from neutronclient.openstack.common.gettextutils import _
-from neutronclient.openstack.common import jsonutils
-
-LOG = logging.getLogger(__name__)
class ActionDispatcher(object):
@@ -58,155 +47,10 @@ class JSONDictSerializer(DictSerializer):
def default(self, data):
def sanitizer(obj):
- return unicode(obj)
+ return str(obj)
return jsonutils.dumps(data, default=sanitizer)
-class XMLDictSerializer(DictSerializer):
-
- def __init__(self, metadata=None, xmlns=None):
- """XMLDictSerializer constructor.
-
- :param metadata: information needed to deserialize xml into
- a dictionary.
- :param xmlns: XML namespace to include with serialized xml
- """
- super(XMLDictSerializer, self).__init__()
- self.metadata = metadata or {}
- if not xmlns:
- xmlns = self.metadata.get('xmlns')
- if not xmlns:
- xmlns = constants.XML_NS_V20
- self.xmlns = xmlns
-
- def default(self, data):
- """Default serializer of XMLDictSerializer.
-
- :param data: expect data to contain a single key as XML root, or
- contain another '*_links' key as atom links. Other
- case will use 'VIRTUAL_ROOT_KEY' as XML root.
- """
- try:
- links = None
- has_atom = False
- if data is None:
- root_key = constants.VIRTUAL_ROOT_KEY
- root_value = None
- else:
- link_keys = [k for k in data.iterkeys() or []
- if k.endswith('_links')]
- if link_keys:
- links = data.pop(link_keys[0], None)
- has_atom = True
- root_key = (len(data) == 1 and
- data.keys()[0] or constants.VIRTUAL_ROOT_KEY)
- root_value = data.get(root_key, data)
- doc = etree.Element("_temp_root")
- used_prefixes = []
- self._to_xml_node(doc, self.metadata, root_key,
- root_value, used_prefixes)
- if links:
- self._create_link_nodes(list(doc)[0], links)
- return self.to_xml_string(list(doc)[0], used_prefixes, has_atom)
- except AttributeError as e:
- LOG.exception(str(e))
- return ''
-
- def __call__(self, data):
- # Provides a migration path to a cleaner WSGI layer, this
- # "default" stuff and extreme extensibility isn't being used
- # like originally intended
- return self.default(data)
-
- def to_xml_string(self, node, used_prefixes, has_atom=False):
- self._add_xmlns(node, used_prefixes, has_atom)
- return etree.tostring(node, encoding='UTF-8')
-
- #NOTE (ameade): the has_atom should be removed after all of the
- # xml serializers and view builders have been updated to the current
- # spec that required all responses include the xmlns:atom, the has_atom
- # flag is to prevent current tests from breaking
- def _add_xmlns(self, node, used_prefixes, has_atom=False):
- node.set('xmlns', self.xmlns)
- node.set(constants.TYPE_XMLNS, self.xmlns)
- if has_atom:
- node.set(constants.ATOM_XMLNS, constants.ATOM_NAMESPACE)
- node.set(constants.XSI_NIL_ATTR, constants.XSI_NAMESPACE)
- ext_ns = self.metadata.get(constants.EXT_NS, {})
- for prefix in used_prefixes:
- if prefix in ext_ns:
- node.set('xmlns:' + prefix, ext_ns[prefix])
-
- def _to_xml_node(self, parent, metadata, nodename, data, used_prefixes):
- """Recursive method to convert data members to XML nodes."""
- result = etree.SubElement(parent, nodename)
- if ":" in nodename:
- used_prefixes.append(nodename.split(":", 1)[0])
- #TODO(bcwaldon): accomplish this without a type-check
- if isinstance(data, list):
- if not data:
- result.set(
- constants.TYPE_ATTR,
- constants.TYPE_LIST)
- return result
- singular = metadata.get('plurals', {}).get(nodename, None)
- if singular is None:
- if nodename.endswith('s'):
- singular = nodename[:-1]
- else:
- singular = 'item'
- for item in data:
- self._to_xml_node(result, metadata, singular, item,
- used_prefixes)
- #TODO(bcwaldon): accomplish this without a type-check
- elif isinstance(data, dict):
- if not data:
- result.set(
- constants.TYPE_ATTR,
- constants.TYPE_DICT)
- return result
- attrs = metadata.get('attributes', {}).get(nodename, {})
- for k, v in sorted(data.items()):
- if k in attrs:
- result.set(k, str(v))
- else:
- self._to_xml_node(result, metadata, k, v,
- used_prefixes)
- elif data is None:
- result.set(constants.XSI_ATTR, 'true')
- else:
- if isinstance(data, bool):
- result.set(
- constants.TYPE_ATTR,
- constants.TYPE_BOOL)
- elif isinstance(data, int):
- result.set(
- constants.TYPE_ATTR,
- constants.TYPE_INT)
- elif isinstance(data, long):
- result.set(
- constants.TYPE_ATTR,
- constants.TYPE_LONG)
- elif isinstance(data, float):
- result.set(
- constants.TYPE_ATTR,
- constants.TYPE_FLOAT)
- LOG.debug(_("Data %(data)s type is %(type)s"),
- {'data': data,
- 'type': type(data)})
- if isinstance(data, str):
- result.text = unicode(data, 'utf-8')
- else:
- result.text = unicode(data)
- return result
-
- def _create_link_nodes(self, xml_doc, links):
- for link in links:
- link_node = etree.SubElement(xml_doc, 'atom:link')
- link_node.set('rel', link['rel'])
- link_node.set('href', link['href'])
-
-
class TextDeserializer(ActionDispatcher):
"""Default request body deserialization."""
@@ -224,146 +68,17 @@ def _from_json(self, datastring):
return jsonutils.loads(datastring)
except ValueError:
msg = _("Cannot understand JSON")
- raise exception.MalformedRequestBody(reason=msg)
+ raise exception.MalformedResponseBody(reason=msg)
def default(self, datastring):
return {'body': self._from_json(datastring)}
-class XMLDeserializer(TextDeserializer):
-
- def __init__(self, metadata=None):
- """XMLDeserializer constructor.
-
- :param metadata: information needed to deserialize xml into
- a dictionary.
- """
- super(XMLDeserializer, self).__init__()
- self.metadata = metadata or {}
- xmlns = self.metadata.get('xmlns')
- if not xmlns:
- xmlns = constants.XML_NS_V20
- self.xmlns = xmlns
-
- def _get_key(self, tag):
- tags = tag.split("}", 1)
- if len(tags) == 2:
- ns = tags[0][1:]
- bare_tag = tags[1]
- ext_ns = self.metadata.get(constants.EXT_NS, {})
- if ns == self.xmlns:
- return bare_tag
- for prefix, _ns in ext_ns.items():
- if ns == _ns:
- return prefix + ":" + bare_tag
- else:
- return tag
-
- def _get_links(self, root_tag, node):
- link_nodes = node.findall(constants.ATOM_LINK_NOTATION)
- root_tag = self._get_key(node.tag)
- link_key = "%s_links" % root_tag
- link_list = []
- for link in link_nodes:
- link_list.append({'rel': link.get('rel'),
- 'href': link.get('href')})
- # Remove link node in order to avoid link node being
- # processed as an item in _from_xml_node
- node.remove(link)
- return link_list and {link_key: link_list} or {}
-
- def _from_xml(self, datastring):
- if datastring is None:
- return None
- plurals = set(self.metadata.get('plurals', {}))
- try:
- node = etree.fromstring(datastring)
- root_tag = self._get_key(node.tag)
- links = self._get_links(root_tag, node)
- result = self._from_xml_node(node, plurals)
- # There is no case where root_tag = constants.VIRTUAL_ROOT_KEY
- # and links is not None because of the way data are serialized
- if root_tag == constants.VIRTUAL_ROOT_KEY:
- return result
- return dict({root_tag: result}, **links)
- except Exception as e:
- parseError = False
- # Python2.7
- if (hasattr(etree, 'ParseError') and
- isinstance(e, getattr(etree, 'ParseError'))):
- parseError = True
- # Python2.6
- elif isinstance(e, expat.ExpatError):
- parseError = True
- if parseError:
- msg = _("Cannot understand XML")
- raise exception.MalformedRequestBody(reason=msg)
- else:
- raise
-
- def _from_xml_node(self, node, listnames):
- """Convert a minidom node to a simple Python type.
-
- :param node: minidom node name
- :param listnames: list of XML node names whose subnodes should
- be considered list items.
-
- """
- attrNil = node.get(str(etree.QName(constants.XSI_NAMESPACE, "nil")))
- attrType = node.get(str(etree.QName(
- self.metadata.get('xmlns'), "type")))
- if (attrNil and attrNil.lower() == 'true'):
- return None
- elif not len(node) and not node.text:
- if (attrType and attrType == constants.TYPE_DICT):
- return {}
- elif (attrType and attrType == constants.TYPE_LIST):
- return []
- else:
- return ''
- elif (len(node) == 0 and node.text):
- converters = {constants.TYPE_BOOL:
- lambda x: x.lower() == 'true',
- constants.TYPE_INT:
- lambda x: int(x),
- constants.TYPE_LONG:
- lambda x: long(x),
- constants.TYPE_FLOAT:
- lambda x: float(x)}
- if attrType and attrType in converters:
- return converters[attrType](node.text)
- else:
- return node.text
- elif self._get_key(node.tag) in listnames:
- return [self._from_xml_node(n, listnames) for n in node]
- else:
- result = dict()
- for attr in node.keys():
- if (attr == 'xmlns' or
- attr.startswith('xmlns:') or
- attr == constants.XSI_ATTR or
- attr == constants.TYPE_ATTR):
- continue
- result[self._get_key(attr)] = node.get(attr)
- children = list(node)
- for child in children:
- result[self._get_key(child.tag)] = self._from_xml_node(
- child, listnames)
- return result
-
- def default(self, datastring):
- return {'body': self._from_xml(datastring)}
-
- def __call__(self, datastring):
- # Adding a migration path to allow us to remove unncessary classes
- return self.default(datastring)
-
-
# NOTE(maru): this class is duplicated from neutron.wsgi
class Serializer(object):
"""Serializes and deserializes dictionaries to certain MIME types."""
- def __init__(self, metadata=None, default_xmlns=None):
+ def __init__(self, metadata=None):
"""Create a serializer based on the given WSGI environment.
'metadata' is an optional dict mapping MIME types to information
@@ -371,12 +86,10 @@ def __init__(self, metadata=None, default_xmlns=None):
"""
self.metadata = metadata or {}
- self.default_xmlns = default_xmlns
def _get_serialize_handler(self, content_type):
handlers = {
'application/json': JSONDictSerializer(),
- 'application/xml': XMLDictSerializer(self.metadata),
}
try:
@@ -384,23 +97,21 @@ def _get_serialize_handler(self, content_type):
except Exception:
raise exception.InvalidContentType(content_type=content_type)
- def serialize(self, data, content_type):
+ def serialize(self, data):
"""Serialize a dictionary into the specified content type."""
- return self._get_serialize_handler(content_type).serialize(data)
+ return self._get_serialize_handler("application/json").serialize(data)
- def deserialize(self, datastring, content_type):
+ def deserialize(self, datastring):
"""Deserialize a string to a dictionary.
The string must be in the format of a supported MIME type.
-
"""
- return self.get_deserialize_handler(content_type).deserialize(
+ return self.get_deserialize_handler("application/json").deserialize(
datastring)
def get_deserialize_handler(self, content_type):
handlers = {
'application/json': JSONDeserializer(),
- 'application/xml': XMLDeserializer(self.metadata),
}
try:
diff --git a/neutronclient/common/utils.py b/neutronclient/common/utils.py
index 532d7ea7d..93f92fd19 100644
--- a/neutronclient/common/utils.py
+++ b/neutronclient/common/utils.py
@@ -17,21 +17,25 @@
"""Utilities and helper functions."""
-import datetime
-import json
+import argparse
+import functools
+import hashlib
import logging
import os
-import sys
-from neutronclient.common import _
+from oslo_utils import encodeutils
+from oslo_utils import importutils
+
+from neutronclient._i18n import _
from neutronclient.common import exceptions
-from neutronclient.openstack.common import strutils
+
+SENSITIVE_HEADERS = ('X-Auth-Token',)
def env(*vars, **kwargs):
"""Returns the first environment variable set.
- if none are non-empty, defaults to '' or keyword arg default.
+ If none are non-empty, defaults to '' or keyword arg default.
"""
for v in vars:
value = os.environ.get(v)
@@ -40,52 +44,16 @@ def env(*vars, **kwargs):
return kwargs.get('default', '')
-def to_primitive(value):
- if isinstance(value, list) or isinstance(value, tuple):
- o = []
- for v in value:
- o.append(to_primitive(v))
- return o
- elif isinstance(value, dict):
- o = {}
- for k, v in value.iteritems():
- o[k] = to_primitive(v)
- return o
- elif isinstance(value, datetime.datetime):
- return str(value)
- elif hasattr(value, 'iteritems'):
- return to_primitive(dict(value.iteritems()))
- elif hasattr(value, '__iter__'):
- return to_primitive(list(value))
- else:
- return value
-
-
-def dumps(value, indent=None):
- try:
- return json.dumps(value, indent=indent)
- except TypeError:
- pass
- return json.dumps(to_primitive(value))
-
-
-def loads(s):
- return json.loads(s)
-
+def convert_to_uppercase(string):
+ return string.upper()
-def import_class(import_str):
- """Returns a class from a string including module and class.
- :param import_str: a string representation of the class name
- :rtype: the requested class
- """
- mod_str, _sep, class_str = import_str.rpartition('.')
- __import__(mod_str)
- return getattr(sys.modules[mod_str], class_str)
+def convert_to_lowercase(string):
+ return string.lower()
def get_client_class(api_name, version, version_map):
- """Returns the client class for the requested API version
+ """Returns the client class for the requested API version.
:param api_name: the name of the API, e.g. 'compute', 'image', etc
:param version: the requested API version
@@ -101,10 +69,10 @@ def get_client_class(api_name, version, version_map):
'map_keys': ', '.join(version_map.keys())}
raise exceptions.UnsupportedVersion(msg)
- return import_class(client_path)
+ return importutils.import_class(client_path)
-def get_item_properties(item, fields, mixed_case_fields=[], formatters={}):
+def get_item_properties(item, fields, mixed_case_fields=(), formatters=None):
"""Return a tuple containing the item properties.
:param item: a single item resource (e.g. Server, Tenant, etc)
@@ -113,6 +81,9 @@ def get_item_properties(item, fields, mixed_case_fields=[], formatters={}):
:param formatters: dictionary mapping field names to callables
to format the values
"""
+ if formatters is None:
+ formatters = {}
+
row = []
for field in fields:
@@ -136,20 +107,65 @@ def get_item_properties(item, fields, mixed_case_fields=[], formatters={}):
def str2bool(strbool):
if strbool is None:
return None
- else:
- return strbool.lower() == 'true'
-
-
-def str2dict(strdict):
- '''Convert key1=value1,key2=value2,... string into dictionary.
-
- :param strdict: key1=value1,key2=value2
- '''
- _info = {}
- for kv_str in strdict.split(","):
- k, v = kv_str.split("=", 1)
- _info.update({k: v})
- return _info
+ return strbool.lower() == 'true'
+
+
+def str2dict(strdict, required_keys=None, optional_keys=None):
+ """Convert key1=value1,key2=value2,... string into dictionary.
+
+ :param strdict: string in the form of key1=value1,key2=value2
+ :param required_keys: list of required keys. All keys in this list must be
+ specified. Otherwise ArgumentTypeError will be raised.
+ If this parameter is unspecified, no required key check
+ will be done.
+ :param optional_keys: list of optional keys.
+ This parameter is used for valid key check.
+ When at least one of required_keys and optional_keys,
+ a key must be a member of either of required_keys or
+ optional_keys. Otherwise, ArgumentTypeError will be
+ raised. When both required_keys and optional_keys are
+ unspecified, no valid key check will be done.
+ """
+ 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")
+ raise argparse.ArgumentTypeError(msg % kv)
+ result[key] = value
+ valid_keys = set(required_keys or []) | set(optional_keys or [])
+ if valid_keys:
+ invalid_keys = [k for k in result if k not in valid_keys]
+ if invalid_keys:
+ msg = _("Invalid key(s) '%(invalid_keys)s' specified. "
+ "Valid key(s): '%(valid_keys)s'.")
+ raise argparse.ArgumentTypeError(
+ msg % {'invalid_keys': ', '.join(sorted(invalid_keys)),
+ 'valid_keys': ', '.join(sorted(valid_keys))})
+ if required_keys:
+ not_found_keys = [k for k in required_keys if k not in result]
+ if not_found_keys:
+ msg = _("Required key(s) '%s' not specified.")
+ raise argparse.ArgumentTypeError(msg % ', '.join(not_found_keys))
+ return result
+
+
+def str2dict_type(optional_keys=None, required_keys=None):
+ return functools.partial(str2dict,
+ optional_keys=optional_keys,
+ required_keys=required_keys)
def http_log_req(_logger, args, kwargs):
@@ -163,38 +179,59 @@ def http_log_req(_logger, args, kwargs):
else:
string_parts.append(' %s' % element)
- for element in kwargs['headers']:
- header = ' -H "%s: %s"' % (element, kwargs['headers'][element])
+ for (key, value) in kwargs['headers'].items():
+ if key in SENSITIVE_HEADERS:
+ v = value.encode('utf-8')
+ h = hashlib.sha256(v)
+ d = h.hexdigest()
+ value = "{SHA256}%s" % d
+ header = ' -H "%s: %s"' % (key, value)
string_parts.append(header)
if 'body' in kwargs and kwargs['body']:
string_parts.append(" -d '%s'" % (kwargs['body']))
- string_parts = safe_encode_list(string_parts)
- _logger.debug(_("\nREQ: %s\n"), "".join(string_parts))
+ req = encodeutils.safe_encode("".join(string_parts))
+ _logger.debug("REQ: %s", req)
def http_log_resp(_logger, resp, body):
if not _logger.isEnabledFor(logging.DEBUG):
return
- _logger.debug(_("RESP:%(resp)s %(body)s\n"), {'resp': resp, 'body': body})
+ _logger.debug("RESP: %(code)s %(headers)s %(body)s",
+ {'code': resp.status_code,
+ 'headers': resp.headers,
+ 'body': body})
def _safe_encode_without_obj(data):
- if isinstance(data, basestring):
- return strutils.safe_encode(data)
+ if isinstance(data, str):
+ return encodeutils.safe_encode(data)
return data
def safe_encode_list(data):
- return map(_safe_encode_without_obj, data)
+ return list(map(_safe_encode_without_obj, data))
def safe_encode_dict(data):
- def _encode_item((k, v)):
+ def _encode_item(item):
+ k, v = item
if isinstance(v, list):
return (k, safe_encode_list(v))
elif isinstance(v, dict):
return (k, safe_encode_dict(v))
return (k, _safe_encode_without_obj(v))
- return dict(map(_encode_item, data.items()))
+ return dict(list(map(_encode_item, data.items())))
+
+
+def add_boolean_argument(parser, name, **kwargs):
+ for keyword in ('metavar', 'choices'):
+ kwargs.pop(keyword, None)
+ default = kwargs.pop('default', argparse.SUPPRESS)
+ parser.add_argument(
+ name,
+ metavar='{True,False}',
+ choices=['True', 'true', 'False', 'false'],
+ default=default,
+ **kwargs)
diff --git a/neutronclient/common/validators.py b/neutronclient/common/validators.py
new file mode 100644
index 000000000..831d68e8f
--- /dev/null
+++ b/neutronclient/common/validators.py
@@ -0,0 +1,69 @@
+# Copyright 2014 NEC Corporation
+# All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import netaddr
+
+from neutronclient._i18n import _
+from neutronclient.common import exceptions
+
+
+def validate_int_range(parsed_args, attr_name, min_value=None, max_value=None):
+ val = getattr(parsed_args, attr_name, None)
+ if val is None:
+ return
+ try:
+ if not isinstance(val, int):
+ int_val = int(val, 0)
+ else:
+ int_val = val
+ if ((min_value is None or min_value <= int_val) and
+ (max_value is None or int_val <= max_value)):
+ return
+ except (ValueError, TypeError):
+ pass
+
+ if min_value is not None and max_value is not None:
+ msg = (_('%(attr_name)s "%(val)s" should be an integer '
+ '[%(min)i:%(max)i].') %
+ {'attr_name': attr_name.replace('_', '-'),
+ 'val': val, 'min': min_value, 'max': max_value})
+ elif min_value is not None:
+ msg = (_('%(attr_name)s "%(val)s" should be an integer '
+ 'greater than or equal to %(min)i.') %
+ {'attr_name': attr_name.replace('_', '-'),
+ 'val': val, 'min': min_value})
+ elif max_value is not None:
+ msg = (_('%(attr_name)s "%(val)s" should be an integer '
+ 'smaller than or equal to %(max)i.') %
+ {'attr_name': attr_name.replace('_', '-'),
+ 'val': val, 'max': max_value})
+ else:
+ msg = (_('%(attr_name)s "%(val)s" should be an integer.') %
+ {'attr_name': attr_name.replace('_', '-'),
+ 'val': val})
+
+ raise exceptions.CommandError(msg)
+
+
+def validate_ip_subnet(parsed_args, attr_name):
+ val = getattr(parsed_args, attr_name)
+ if not val:
+ return
+ try:
+ netaddr.IPNetwork(val)
+ except (netaddr.AddrFormatError, ValueError):
+ raise exceptions.CommandError(
+ (_('%(attr_name)s "%(val)s" is not a valid CIDR.') %
+ {'attr_name': attr_name.replace('_', '-'), 'val': val}))
diff --git a/neutronclient/neutron/client.py b/neutronclient/neutron/client.py
index f87c8311b..d859d815d 100644
--- a/neutronclient/neutron/client.py
+++ b/neutronclient/neutron/client.py
@@ -14,48 +14,47 @@
# under the License.
#
-from neutronclient.common import exceptions
from neutronclient.common import utils
-from neutronclient.openstack.common.gettextutils import _
API_NAME = 'network'
API_VERSIONS = {
'2.0': 'neutronclient.v2_0.client.Client',
+ '2': 'neutronclient.v2_0.client.Client',
}
def make_client(instance):
- """Returns an neutron client.
- """
+ """Returns an neutron client."""
neutron_client = utils.get_client_class(
API_NAME,
- instance._api_version[API_NAME],
+ instance._api_version,
API_VERSIONS,
)
instance.initialize()
url = instance._url
url = url.rstrip("/")
- if '2.0' == instance._api_version[API_NAME]:
- client = neutron_client(username=instance._username,
- tenant_name=instance._tenant_name,
- password=instance._password,
- region_name=instance._region_name,
- auth_url=instance._auth_url,
- endpoint_url=url,
- token=instance._token,
- auth_strategy=instance._auth_strategy,
- insecure=instance._insecure,
- ca_cert=instance._ca_cert)
- return client
- else:
- raise exceptions.UnsupportedVersion(_("API version %s is not "
- "supported") %
- instance._api_version[API_NAME])
+ client = neutron_client(username=instance._username,
+ project_name=instance._project_name,
+ password=instance._password,
+ region_name=instance._region_name,
+ auth_url=instance._auth_url,
+ endpoint_url=url,
+ endpoint_type=instance._endpoint_type,
+ token=instance._token,
+ auth_strategy=instance._auth_strategy,
+ insecure=instance._insecure,
+ ca_cert=instance._ca_cert,
+ retries=instance._retries,
+ raise_errors=instance._raise_errors,
+ session=instance._session,
+ auth=instance._auth)
+ return client
def Client(api_version, *args, **kwargs):
"""Return an neutron client.
+
@param api_version: only 2.0 is supported now
"""
neutron_client = utils.get_client_class(
diff --git a/neutronclient/neutron/v2_0/__init__.py b/neutronclient/neutron/v2_0/__init__.py
index dd0cab3b9..849a93d9e 100644
--- a/neutronclient/neutron/v2_0/__init__.py
+++ b/neutronclient/neutron/v2_0/__init__.py
@@ -14,84 +14,54 @@
# under the License.
#
-from __future__ import print_function
-
+import abc
import argparse
+import functools
import logging
-import re
-from cliff.formatters import table
+from cliff import command
from cliff import lister
from cliff import show
+from oslo_serialization import jsonutils
-from neutronclient.common import command
+from neutronclient._i18n import _
from neutronclient.common import exceptions
from neutronclient.common import utils
-from neutronclient.openstack.common.gettextutils import _
-
-HEX_ELEM = '[0-9A-Fa-f]'
-UUID_PATTERN = '-'.join([HEX_ELEM + '{8}', HEX_ELEM + '{4}',
- HEX_ELEM + '{4}', HEX_ELEM + '{4}',
- HEX_ELEM + '{12}'])
-
-
-def _get_resource_plural(resource, client):
- plurals = getattr(client, 'EXTED_PLURALS', [])
- for k in plurals:
- if plurals[k] == resource:
- return k
- return resource + 's'
-
-
-def find_resourceid_by_id(client, resource, resource_id):
- resource_plural = _get_resource_plural(resource, client)
- obj_lister = getattr(client, "list_%s" % resource_plural)
- # perform search by id only if we are passing a valid UUID
- match = re.match(UUID_PATTERN, resource_id)
- collection = resource_plural
- if match:
- data = obj_lister(id=resource_id, fields='id')
- if data and data[collection]:
- return data[collection][0]['id']
- not_found_message = (_("Unable to find %(resource)s with id "
- "'%(id)s'") %
- {'resource': resource, 'id': resource_id})
- # 404 is used to simulate server side behavior
- raise exceptions.NeutronClientException(
- message=not_found_message, status_code=404)
-
-
-def _find_resourceid_by_name(client, resource, name):
- resource_plural = _get_resource_plural(resource, client)
- obj_lister = getattr(client, "list_%s" % resource_plural)
- data = obj_lister(name=name, fields='id')
- collection = resource_plural
- info = data[collection]
- if len(info) > 1:
- raise exceptions.NeutronClientNoUniqueMatch(resource=resource,
- name=name)
- elif len(info) == 0:
- not_found_message = (_("Unable to find %(resource)s with name "
- "'%(name)s'") %
- {'resource': resource, 'name': name})
- # 404 is used to simulate server side behavior
- raise exceptions.NeutronClientException(
- message=not_found_message, status_code=404)
- else:
- return info[0]['id']
-
-
-def find_resourceid_by_name_or_id(client, resource, name_or_id):
- try:
- return find_resourceid_by_id(client, resource, name_or_id)
- except exceptions.NeutronClientException:
- return _find_resourceid_by_name(client, resource, name_or_id)
+
+HYPHEN_OPTS = ['tags_any', 'not_tags', 'not_tags_any']
+
+
+def find_resource_by_id(client, resource, resource_id, cmd_resource=None,
+ parent_id=None, fields=None):
+ return client.find_resource_by_id(resource, resource_id, cmd_resource,
+ parent_id, fields)
+
+
+def find_resourceid_by_id(client, resource, resource_id, cmd_resource=None,
+ parent_id=None):
+ return find_resource_by_id(client, resource, resource_id, cmd_resource,
+ parent_id, fields='id')['id']
+
+
+def find_resource_by_name_or_id(client, resource, name_or_id,
+ project_id=None, cmd_resource=None,
+ parent_id=None, fields=None):
+ return client.find_resource(resource, name_or_id, project_id,
+ cmd_resource, parent_id, fields)
+
+
+def find_resourceid_by_name_or_id(client, resource, name_or_id,
+ project_id=None, cmd_resource=None,
+ parent_id=None):
+ return find_resource_by_name_or_id(client, resource, name_or_id,
+ project_id, cmd_resource,
+ parent_id, fields='id')['id']
def add_show_list_common_argument(parser):
parser.add_argument(
'-D', '--show-details',
- help=_('Show detailed info'),
+ help=_('Show detailed information.'),
action='store_true',
default=False, )
parser.add_argument(
@@ -106,8 +76,8 @@ def add_show_list_common_argument(parser):
parser.add_argument(
'-F', '--field',
dest='fields', metavar='FIELD',
- help=_('Specify the field(s) to be returned by server,'
- ' can be repeated'),
+ help=_('Specify the field(s) to be returned by server. You can '
+ 'repeat this option.'),
action='append',
default=[])
@@ -117,7 +87,7 @@ def add_pagination_argument(parser):
'-P', '--page-size',
dest='page_size', metavar='SIZE', type=int,
help=_("Specify retrieve unit of each request, then split one request "
- "to several requests"),
+ "to several requests."),
default=None)
@@ -126,16 +96,17 @@ def add_sorting_argument(parser):
'--sort-key',
dest='sort_key', metavar='FIELD',
action='append',
- help=_("Sort list by specified fields (This option can be repeated), "
- "The number of sort_dir and sort_key should match each other, "
- "more sort_dir specified will be omitted, less will be filled "
- "with asc as default direction "),
+ help=_("Sorts the list by the specified fields in the specified "
+ "directions. You can repeat this option, but you must "
+ "specify an equal number of sort_dir and sort_key values. "
+ "Extra sort_dir options are ignored. Missing sort_dir options "
+ "use the default asc value."),
default=[])
parser.add_argument(
'--sort-dir',
dest='sort_dir', metavar='{asc,desc}',
- help=_("Sort list in specified directions "
- "(This option can be repeated)"),
+ help=_("Sorts the list in the specified direction. You can repeat "
+ "this option."),
action='append',
default=[],
choices=['asc', 'desc'])
@@ -158,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:
@@ -174,7 +145,7 @@ def _process_previous_argument(current_arg, _value_number, current_type_str,
def parse_args_to_dict(values_specs):
- '''It is used to analyze the extra command options to command.
+ """It is used to analyze the extra command options to command.
Besides known options and arguments, our commands also support user to
put more options to the end of command line. For example,
@@ -186,8 +157,7 @@ def parse_args_to_dict(values_specs):
value spec is: --key type=int|bool|... value. Type is one of Python
built-in types. By default, type is string. The key without value is
a bool option. Key with two values will be a list option.
-
- '''
+ """
# values_specs for example: '-- --tag x y --key1 type=int value1'
# -- is a pseudo argument
@@ -212,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
@@ -242,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
@@ -259,11 +241,14 @@ def parse_args_to_dict(values_specs):
# All others are value items
# Make sure '--' occurs first and allow minus value
if (not current_item or '=' in current_item or
- _item.startswith('-') and not is_number(_item)):
+ _item.startswith('-') and not is_number(_item)):
raise exceptions.CommandError(
_("Invalid values_specs %s") % ' '.join(values_specs))
_value_number += 1
+ if _item.startswith('---'):
+ raise exceptions.CommandError(
+ _("Invalid values_specs %s") % ' '.join(values_specs))
_values_specs.append(_item)
# Deal with last one argument
@@ -271,14 +256,14 @@ def parse_args_to_dict(values_specs):
current_arg, _value_number, current_type_str,
_list_flag, _values_specs, _clear_flag, values_specs)
- # populate the parser with arguments
+ # Populate the parser with arguments
_parser = argparse.ArgumentParser(add_help=False)
- for opt, optspec in _options.iteritems():
+ for opt, optspec in _options.items():
_parser.add_argument(opt, **optspec)
_args = _parser.parse_args(_values_specs)
result_dict = {}
- for opt in _options.iterkeys():
+ for opt in _options.keys():
_opt = opt.split('--', 2)[1]
_opt = _opt.replace('-', '_')
_value = getattr(_args, _opt)
@@ -297,20 +282,20 @@ 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 temp_values.iteritems():
+ 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:
if isinstance(arg_value, list):
if value and isinstance(value, list):
if (not arg_value or
- type(arg_value[0]) == type(value[0])):
+ isinstance(arg_value[0], type(value[0]))):
arg_value.extend(value)
_extra_values.pop(key)
def update_dict(obj, dict, attributes):
- """Update dict with fields from obj.attributes
+ """Update dict with fields from obj.attributes.
:param obj: the object updated into dict
:param dict: the result dictionary
@@ -321,32 +306,35 @@ def update_dict(obj, dict, attributes):
dict[attribute] = getattr(obj, attribute)
-class TableFormater(table.TableFormatter):
- """This class is used to keep consistency with prettytable 0.6.
+# cliff.command.Command is abstract class so that metaclass of
+# subclass must be subclass of metaclass of all its base.
+# otherwise metaclass conflict exception is raised.
+class NeutronCommandMeta(abc.ABCMeta):
+ def __new__(cls, name, bases, cls_dict):
+ if 'log' not in cls_dict:
+ cls_dict['log'] = logging.getLogger(
+ cls_dict['__module__'] + '.' + name)
+ return super(NeutronCommandMeta, cls).__new__(cls,
+ name, bases, cls_dict)
- https://bugs.launchpad.net/python-neutronclient/+bug/1165962
- """
- def emit_list(self, column_names, data, stdout, parsed_args):
- if column_names:
- super(TableFormater, self).emit_list(column_names, data, stdout,
- parsed_args)
- else:
- stdout.write('\n')
+class NeutronCommand(command.Command, metaclass=NeutronCommandMeta):
-class NeutronCommand(command.OpenStackCommand):
- api = 'network'
- log = logging.getLogger(__name__ + '.NeutronCommand')
values_specs = []
json_indent = None
+ resource = None
+ shadow_resource = None
+ parent_id = None
- def __init__(self, app, app_args):
- super(NeutronCommand, self).__init__(app, app_args)
- # NOTE(markmcclain): This is no longer supported in cliff version 1.5.2
- # see https://bugs.launchpad.net/python-neutronclient/+bug/1265926
+ def run(self, parsed_args):
+ self.log.debug('run(%s)', parsed_args)
+ return super(NeutronCommand, self).run(parsed_args)
- #if hasattr(self, 'formatters'):
- #self.formatters['table'] = TableFormater()
+ @property
+ def cmd_resource(self):
+ if self.shadow_resource:
+ return self.shadow_resource
+ return self.resource
def get_client(self):
return self.app.client_manager.neutron
@@ -355,27 +343,30 @@ def get_parser(self, prog_name):
parser = super(NeutronCommand, self).get_parser(prog_name)
parser.add_argument(
'--request-format',
- help=_('The xml or json request format'),
+ help=argparse.SUPPRESS,
default='json',
- choices=['json', 'xml', ], )
+ choices=['json', ], )
parser.add_argument(
'--request_format',
- choices=['json', 'xml', ],
+ choices=['json', ],
help=argparse.SUPPRESS)
return parser
+ def cleanup_output_data(self, data):
+ pass
+
def format_output_data(self, data):
# Modify data to make it more readable
if self.resource in data:
- for k, v in data[self.resource].iteritems():
+ for k, v in data[self.resource].items():
if isinstance(v, list):
- value = '\n'.join(utils.dumps(
+ value = '\n'.join(jsonutils.dumps(
i, indent=self.json_indent) if isinstance(i, dict)
else str(i) for i in v)
data[self.resource][k] = value
elif isinstance(v, dict):
- value = utils.dumps(v, indent=self.json_indent)
+ value = jsonutils.dumps(v, indent=self.json_indent)
data[self.resource][k] = value
elif v is None:
data[self.resource][k] = ''
@@ -383,74 +374,80 @@ def format_output_data(self, data):
def add_known_arguments(self, parser):
pass
+ def set_extra_attrs(self, parsed_args):
+ pass
+
def args2body(self, parsed_args):
return {}
class CreateCommand(NeutronCommand, show.ShowOne):
- """Create a resource for a given tenant
-
- """
+ """Create a resource for a given tenant."""
- api = 'network'
- resource = None
log = None
def get_parser(self, prog_name):
parser = super(CreateCommand, self).get_parser(prog_name)
parser.add_argument(
'--tenant-id', metavar='TENANT_ID',
- help=_('The owner tenant ID'), )
+ help=_('The owner tenant ID.'), )
parser.add_argument(
'--tenant_id',
help=argparse.SUPPRESS)
self.add_known_arguments(parser)
return parser
- def get_data(self, parsed_args):
- self.log.debug('get_data(%s)' % parsed_args)
+ def take_action(self, parsed_args):
+ self.set_extra_attrs(parsed_args)
neutron_client = self.get_client()
- neutron_client.format = parsed_args.request_format
_extra_values = parse_args_to_dict(self.values_specs)
_merge_args(self, parsed_args, _extra_values,
self.values_specs)
body = self.args2body(parsed_args)
body[self.resource].update(_extra_values)
obj_creator = getattr(neutron_client,
- "create_%s" % self.resource)
- data = obj_creator(body)
- self.format_output_data(data)
- # {u'network': {u'id': u'e9424a76-6db4-4c93-97b6-ec311cd51f19'}}
+ "create_%s" % self.cmd_resource)
+ if self.parent_id:
+ data = obj_creator(self.parent_id, body)
+ else:
+ data = obj_creator(body)
+ 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:
- print(_('Created a new %s:') % self.resource,
- file=self.app.stdout)
+ if parsed_args.formatter == 'table':
+ print(_('Created a new %s:') % self.resource,
+ file=self.app.stdout)
else:
info = {'': ''}
- return zip(*sorted(info.iteritems()))
+ return zip(*sorted(info.items()))
class UpdateCommand(NeutronCommand):
- """Update resource's information
- """
+ """Update resource's information."""
- api = 'network'
- resource = None
log = None
allow_names = True
+ help_resource = None
def get_parser(self, prog_name):
parser = super(UpdateCommand, self).get_parser(prog_name)
+ if self.allow_names:
+ help_str = _('ID or name of %s to update.')
+ else:
+ help_str = _('ID of %s to update.')
+ if not self.help_resource:
+ self.help_resource = self.resource
parser.add_argument(
'id', metavar=self.resource.upper(),
- help=_('ID or name of %s to update') % self.resource)
+ help=help_str % self.help_resource)
self.add_known_arguments(parser)
return parser
- def run(self, parsed_args):
- self.log.debug('run(%s)', parsed_args)
+ def take_action(self, parsed_args):
+ self.set_extra_attrs(parsed_args)
neutron_client = self.get_client()
- neutron_client.format = parsed_args.request_format
_extra_values = parse_args_to_dict(self.values_specs)
_merge_args(self, parsed_args, _extra_values,
self.values_specs)
@@ -461,16 +458,22 @@ def run(self, parsed_args):
body[self.resource] = _extra_values
if not body[self.resource]:
raise exceptions.CommandError(
- _("Must specify new values to update %s") % self.resource)
+ _("Must specify new values to update %s") %
+ self.cmd_resource)
if self.allow_names:
_id = find_resourceid_by_name_or_id(
- neutron_client, self.resource, parsed_args.id)
+ neutron_client, self.resource, parsed_args.id,
+ cmd_resource=self.cmd_resource, parent_id=self.parent_id)
else:
_id = find_resourceid_by_id(
- neutron_client, self.resource, parsed_args.id)
- obj_updator = getattr(neutron_client,
- "update_%s" % self.resource)
- obj_updator(_id, body)
+ neutron_client, self.resource, parsed_args.id,
+ self.cmd_resource, self.parent_id)
+ obj_updater = getattr(neutron_client,
+ "update_%s" % self.cmd_resource)
+ if self.parent_id:
+ obj_updater(_id, self.parent_id, body)
+ else:
+ obj_updater(_id, body)
print((_('Updated %(resource)s: %(id)s') %
{'id': parsed_args.id, 'resource': self.resource}),
file=self.app.stdout)
@@ -478,58 +481,134 @@ def run(self, parsed_args):
class DeleteCommand(NeutronCommand):
- """Delete a given resource
-
- """
+ """Delete a given resource."""
- api = 'network'
- resource = None
log = None
allow_names = True
+ help_resource = None
+ bulk_delete = True
def get_parser(self, prog_name):
parser = super(DeleteCommand, self).get_parser(prog_name)
+ if not self.help_resource:
+ self.help_resource = self.resource
if self.allow_names:
- help_str = _('ID or name of %s to delete')
+ help_str = _('ID(s) or name(s) of %s to delete.')
else:
- help_str = _('ID of %s to delete')
+ help_str = _('ID(s) of %s to delete.')
parser.add_argument(
'id', metavar=self.resource.upper(),
- help=help_str % self.resource)
+ nargs='+' if self.bulk_delete else 1,
+ help=help_str % self.help_resource)
+ self.add_known_arguments(parser)
return parser
- def run(self, parsed_args):
- self.log.debug('run(%s)', parsed_args)
+ def take_action(self, parsed_args):
+ self.set_extra_attrs(parsed_args)
neutron_client = self.get_client()
- neutron_client.format = parsed_args.request_format
obj_deleter = getattr(neutron_client,
- "delete_%s" % self.resource)
+ "delete_%s" % self.cmd_resource)
+
+ if self.bulk_delete:
+ self._bulk_delete(obj_deleter, neutron_client, parsed_args.id)
+ else:
+ self.delete_item(obj_deleter, neutron_client, parsed_args.id)
+ print((_('Deleted %(resource)s: %(id)s')
+ % {'id': parsed_args.id,
+ 'resource': self.resource}),
+ file=self.app.stdout)
+ return
+
+ def _bulk_delete(self, obj_deleter, neutron_client, parsed_args_ids):
+ successful_delete = []
+ non_existent = []
+ multiple_ids = []
+ for item_id in parsed_args_ids:
+ try:
+ self.delete_item(obj_deleter, neutron_client, item_id)
+ successful_delete.append(item_id)
+ except exceptions.NotFound:
+ non_existent.append(item_id)
+ except exceptions.NeutronClientNoUniqueMatch:
+ multiple_ids.append(item_id)
+ if successful_delete:
+ print((_('Deleted %(resource)s(s): %(id)s'))
+ % {'id': ", ".join(successful_delete),
+ 'resource': self.cmd_resource},
+ 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:
- _id = find_resourceid_by_name_or_id(neutron_client, self.resource,
- parsed_args.id)
+ params = {'cmd_resource': self.cmd_resource,
+ 'parent_id': self.parent_id}
+ _id = find_resourceid_by_name_or_id(neutron_client,
+ self.resource,
+ item_id,
+ **params)
else:
- _id = parsed_args.id
- obj_deleter(_id)
- print((_('Deleted %(resource)s: %(id)s')
- % {'id': parsed_args.id,
- 'resource': self.resource}),
- file=self.app.stdout)
+ _id = item_id
+
+ if self.parent_id:
+ obj_deleter(_id, self.parent_id)
+ else:
+ obj_deleter(_id)
return
class ListCommand(NeutronCommand, lister.Lister):
- """List resources that belong to a given tenant
-
- """
+ """List resources that belong to a given tenant."""
- api = 'network'
- resource = None
log = None
_formatters = {}
list_columns = []
unknown_parts_flag = True
pagination_support = False
sorting_support = False
+ resource_plural = None
+
+ # A list to define arguments for filtering by attribute value
+ # CLI arguments are shown in the order of this list.
+ # Each element must be either of a string of an attribute name
+ # or a dict of a full attribute definitions whose format is:
+ # {'name': attribute name, (mandatory)
+ # 'help': help message for CLI (mandatory)
+ # 'boolean': boolean parameter or not. (Default: False) (optional)
+ # 'argparse_kwargs': a dict of parameters passed to
+ # argparse add_argument()
+ # (Default: {}) (optional)
+ # }
+ # For more details, see ListNetworks.filter_attrs.
+ filter_attrs = []
+
+ default_attr_defs = {
+ 'name': {
+ 'help': _("Filter %s according to their name."),
+ 'boolean': False,
+ },
+ 'tenant_id': {
+ 'help': _('Filter %s belonging to the given tenant.'),
+ 'boolean': False,
+ },
+ 'admin_state_up': {
+ 'help': _('Filter and list the %s whose administrative '
+ 'state is active'),
+ 'boolean': True,
+ },
+ }
def get_parser(self, prog_name):
parser = super(ListCommand, self).get_parser(prog_name)
@@ -538,8 +617,37 @@ def get_parser(self, prog_name):
add_pagination_argument(parser)
if self.sorting_support:
add_sorting_argument(parser)
+ self.add_known_arguments(parser)
+ self.add_filtering_arguments(parser)
return parser
+ def add_filtering_arguments(self, parser):
+ if not self.filter_attrs:
+ return
+
+ group_parser = parser.add_argument_group('filtering arguments')
+ collection = self.resource_plural or '%ss' % self.resource
+ for attr in self.filter_attrs:
+ if isinstance(attr, str):
+ # Use detail defined in default_attr_defs
+ attr_name = attr
+ attr_defs = self.default_attr_defs[attr]
+ else:
+ attr_name = attr['name']
+ attr_defs = attr
+ option_name = '--%s' % attr_name.replace('_', '-')
+ params = attr_defs.get('argparse_kwargs', {})
+ try:
+ help_msg = attr_defs['help'] % collection
+ except TypeError:
+ help_msg = attr_defs['help']
+ if attr_defs.get('boolean', False):
+ add_arg_func = functools.partial(utils.add_boolean_argument,
+ group_parser)
+ else:
+ add_arg_func = group_parser.add_argument
+ add_arg_func(option_name, help=help_msg, **params)
+
def args2search_opts(self, parsed_args):
search_opts = {}
fields = parsed_args.fields
@@ -547,18 +655,28 @@ def args2search_opts(self, parsed_args):
search_opts.update({'fields': fields})
if parsed_args.show_details:
search_opts.update({'verbose': 'True'})
+ filter_attrs = [field if isinstance(field, str) else field['name']
+ for field in self.filter_attrs]
+ for attr in filter_attrs:
+ val = getattr(parsed_args, attr, None)
+ if attr in HYPHEN_OPTS:
+ attr = attr.replace('_', '-')
+ if val:
+ search_opts[attr] = val
return search_opts
def call_server(self, neutron_client, search_opts, parsed_args):
- resource_plural = _get_resource_plural(self.resource, neutron_client)
+ resource_plural = neutron_client.get_resource_plural(self.cmd_resource)
obj_lister = getattr(neutron_client, "list_%s" % resource_plural)
- data = obj_lister(**search_opts)
+ if self.parent_id:
+ data = obj_lister(self.parent_id, **search_opts)
+ else:
+ data = obj_lister(**search_opts)
return data
def retrieve_list(self, parsed_args):
- """Retrieve a list of resources from Neutron server"""
+ """Retrieve a list of resources from Neutron server."""
neutron_client = self.get_client()
- neutron_client.format = parsed_args.request_format
_extra_values = parse_args_to_dict(self.values_specs)
_merge_args(self, parsed_args, _extra_values,
self.values_specs)
@@ -581,15 +699,15 @@ def retrieve_list(self, parsed_args):
if dirs:
search_opts.update({'sort_dir': dirs})
data = self.call_server(neutron_client, search_opts, parsed_args)
- collection = _get_resource_plural(self.resource, neutron_client)
+ collection = neutron_client.get_resource_plural(self.resource)
return data.get(collection, [])
def extend_list(self, data, parsed_args):
"""Update a retrieved list.
- This method provides a way to modify a original list returned from
+ This method provides a way to modify an original list returned from
the neutron server. For example, you can add subnet cidr information
- to a list network.
+ to a network list.
"""
pass
@@ -604,44 +722,80 @@ 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)
+
+ 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=self._formatters, )
+ s, _columns, formatters=formatters, )
for s in info), )
- def get_data(self, parsed_args):
- self.log.debug('get_data(%s)', parsed_args)
+ 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)
self.extend_list(data, parsed_args)
return self.setup_columns(data, parsed_args)
class ShowCommand(NeutronCommand, show.ShowOne):
- """Show information of a given resource
+ """Show information of a given resource."""
- """
-
- api = 'network'
- resource = None
log = None
allow_names = True
+ help_resource = None
def get_parser(self, prog_name):
parser = super(ShowCommand, self).get_parser(prog_name)
add_show_list_common_argument(parser)
if self.allow_names:
- help_str = _('ID or name of %s to look up')
+ help_str = _('ID or name of %s to look up.')
else:
- help_str = _('ID of %s to look up')
+ help_str = _('ID of %s to look up.')
+ if not self.help_resource:
+ self.help_resource = self.resource
parser.add_argument(
'id', metavar=self.resource.upper(),
- help=help_str % self.resource)
+ help=help_str % self.help_resource)
+ self.add_known_arguments(parser)
return parser
- def get_data(self, parsed_args):
- self.log.debug('get_data(%s)', parsed_args)
+ def take_action(self, parsed_args):
+ self.set_extra_attrs(parsed_args)
neutron_client = self.get_client()
- neutron_client.format = parsed_args.request_format
params = {}
if parsed_args.show_details:
@@ -649,16 +803,24 @@ def get_data(self, parsed_args):
if parsed_args.fields:
params = {'fields': parsed_args.fields}
if self.allow_names:
- _id = find_resourceid_by_name_or_id(neutron_client, self.resource,
- parsed_args.id)
+ _id = find_resourceid_by_name_or_id(neutron_client,
+ self.resource,
+ parsed_args.id,
+ cmd_resource=self.cmd_resource,
+ parent_id=self.parent_id)
else:
_id = parsed_args.id
- obj_shower = getattr(neutron_client, "show_%s" % self.resource)
- data = obj_shower(_id, **params)
- self.format_output_data(data)
+ obj_shower = getattr(neutron_client, "show_%s" % self.cmd_resource)
+ if self.parent_id:
+ data = obj_shower(_id, self.parent_id, **params)
+ else:
+ data = obj_shower(_id, **params)
+ 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(resource.iteritems()))
+ return zip(*sorted(resource.items()))
else:
return None
diff --git a/neutronclient/neutron/v2_0/address_scope.py b/neutronclient/neutron/v2_0/address_scope.py
new file mode 100644
index 000000000..73eba80f5
--- /dev/null
+++ b/neutronclient/neutron/v2_0/address_scope.py
@@ -0,0 +1,89 @@
+# 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.
+#
+
+from neutronclient._i18n import _
+from neutronclient.common import utils
+from neutronclient.neutron import v2_0 as neutronV20
+
+
+class ListAddressScope(neutronV20.ListCommand):
+ """List address scopes that belong to a given tenant."""
+
+ resource = 'address_scope'
+ list_columns = ['id', 'name', 'ip_version']
+ pagination_support = True
+ sorting_support = True
+
+
+class ShowAddressScope(neutronV20.ShowCommand):
+ """Show information about an address scope."""
+
+ resource = 'address_scope'
+
+
+class CreateAddressScope(neutronV20.CreateCommand):
+ """Create an address scope for a given tenant."""
+
+ resource = 'address_scope'
+
+ def add_known_arguments(self, parser):
+ parser.add_argument(
+ '--shared',
+ action='store_true',
+ help=_('Set the address scope as shared.'))
+ parser.add_argument(
+ 'name',
+ metavar='NAME',
+ help=_('Specify the name of the address scope.'))
+ parser.add_argument(
+ 'ip_version',
+ metavar='IP_VERSION',
+ type=int,
+ choices=[4, 6],
+ help=_('Specify the address family of the address scope.'))
+
+ def args2body(self, parsed_args):
+ body = {'name': parsed_args.name,
+ 'ip_version': parsed_args.ip_version}
+ if parsed_args.shared:
+ body['shared'] = True
+ neutronV20.update_dict(parsed_args, body, ['tenant_id'])
+ return {self.resource: body}
+
+
+class DeleteAddressScope(neutronV20.DeleteCommand):
+ """Delete an address scope."""
+
+ resource = 'address_scope'
+
+
+class UpdateAddressScope(neutronV20.UpdateCommand):
+ """Update an address scope."""
+
+ resource = 'address_scope'
+
+ def add_known_arguments(self, parser):
+ parser.add_argument('--name',
+ help=_('Updated name of the address scope.'))
+ utils.add_boolean_argument(
+ parser, '--shared',
+ help=_('Set sharing of address scope. '
+ '(True means shared)'))
+
+ def args2body(self, parsed_args):
+ body = {}
+ neutronV20.update_dict(parsed_args, body, ['name', 'shared'])
+ return {self.resource: body}
diff --git a/neutronclient/neutron/v2_0/agent.py b/neutronclient/neutron/v2_0/agent.py
index af7b6cfbf..36167ff66 100644
--- a/neutronclient/neutron/v2_0/agent.py
+++ b/neutronclient/neutron/v2_0/agent.py
@@ -14,15 +14,14 @@
# under the License.
#
-import logging
-
+from neutronclient._i18n import _
from neutronclient.neutron import v2_0 as neutronV20
def _format_timestamp(component):
try:
return component['heartbeat_timestamp'].split(".", 2)[0]
- except Exception:
+ except (TypeError, KeyError):
return ''
@@ -30,9 +29,10 @@ class ListAgent(neutronV20.ListCommand):
"""List agents."""
resource = 'agent'
- log = logging.getLogger(__name__ + '.ListAgent')
- list_columns = ['id', 'agent_type', 'host', 'alive', 'admin_state_up']
+ list_columns = ['id', 'agent_type', 'host', 'availability_zone', 'alive',
+ 'admin_state_up', 'binary']
_formatters = {'heartbeat_timestamp': _format_timestamp}
+ sorting_support = True
def extend_list(self, data, parsed_args):
for agent in data:
@@ -44,7 +44,6 @@ class ShowAgent(neutronV20.ShowCommand):
"""Show information of a given agent."""
resource = 'agent'
- log = logging.getLogger(__name__ + '.ShowAgent')
allow_names = False
json_indent = 5
@@ -52,14 +51,28 @@ class ShowAgent(neutronV20.ShowCommand):
class DeleteAgent(neutronV20.DeleteCommand):
"""Delete a given agent."""
- log = logging.getLogger(__name__ + '.DeleteAgent')
resource = 'agent'
allow_names = False
class UpdateAgent(neutronV20.UpdateCommand):
- """Update a given agent."""
+ """Updates the admin status and description for a specified agent."""
- log = logging.getLogger(__name__ + '.UpdateAgent')
resource = 'agent'
allow_names = False
+
+ def add_known_arguments(self, parser):
+ parser.add_argument(
+ '--admin-state-down',
+ dest='admin_state',
+ action='store_false',
+ help=_('Set admin state up of the agent to false.'))
+ parser.add_argument(
+ '--description',
+ help=_('Description for the agent.'))
+
+ def args2body(self, parsed_args):
+ body = {'admin_state_up': parsed_args.admin_state}
+ neutronV20.update_dict(parsed_args, body,
+ ['description'])
+ return {self.resource: body}
diff --git a/neutronclient/neutron/v2_0/agentscheduler.py b/neutronclient/neutron/v2_0/agentscheduler.py
index d202a105b..e92b10be1 100644
--- a/neutronclient/neutron/v2_0/agentscheduler.py
+++ b/neutronclient/neutron/v2_0/agentscheduler.py
@@ -14,14 +14,10 @@
# under the License.
#
-from __future__ import print_function
-
-import logging
-
+from neutronclient._i18n import _
from neutronclient.neutron import v2_0 as neutronV20
from neutronclient.neutron.v2_0 import network
from neutronclient.neutron.v2_0 import router
-from neutronclient.openstack.common.gettextutils import _
PERFECT_TIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%f"
@@ -30,22 +26,20 @@
class AddNetworkToDhcpAgent(neutronV20.NeutronCommand):
"""Add a network to a DHCP agent."""
- log = logging.getLogger(__name__ + '.AddNetworkToDhcpAgent')
-
def get_parser(self, prog_name):
parser = super(AddNetworkToDhcpAgent, self).get_parser(prog_name)
parser.add_argument(
'dhcp_agent',
- help=_('ID of the DHCP agent'))
+ metavar='DHCP_AGENT',
+ help=_('ID of the DHCP agent.'))
parser.add_argument(
'network',
- help=_('Network to add'))
+ metavar='NETWORK',
+ help=_('Network to add.'))
return parser
- def run(self, parsed_args):
- self.log.debug('run(%s)' % parsed_args)
+ def take_action(self, parsed_args):
neutron_client = self.get_client()
- neutron_client.format = parsed_args.request_format
_net_id = neutronV20.find_resourceid_by_name_or_id(
neutron_client, 'network', parsed_args.network)
neutron_client.add_network_to_dhcp_agent(parsed_args.dhcp_agent,
@@ -56,34 +50,32 @@ def run(self, parsed_args):
class RemoveNetworkFromDhcpAgent(neutronV20.NeutronCommand):
"""Remove a network from a DHCP agent."""
- log = logging.getLogger(__name__ + '.RemoveNetworkFromDhcpAgent')
def get_parser(self, prog_name):
parser = super(RemoveNetworkFromDhcpAgent, self).get_parser(prog_name)
parser.add_argument(
'dhcp_agent',
- help=_('ID of the DHCP agent'))
+ metavar='DHCP_AGENT',
+ help=_('ID of the DHCP agent.'))
parser.add_argument(
'network',
- help=_('Network to remove'))
+ metavar='NETWORK',
+ help=_('Network to remove.'))
return parser
- def run(self, parsed_args):
- self.log.debug('run(%s)' % parsed_args)
+ def take_action(self, parsed_args):
neutron_client = self.get_client()
- neutron_client.format = parsed_args.request_format
_net_id = neutronV20.find_resourceid_by_name_or_id(
neutron_client, 'network', parsed_args.network)
neutron_client.remove_network_from_dhcp_agent(
parsed_args.dhcp_agent, _net_id)
- print(_('Removed network %s to DHCP agent') % parsed_args.network,
+ print(_('Removed network %s from DHCP agent') % parsed_args.network,
file=self.app.stdout)
class ListNetworksOnDhcpAgent(network.ListNetwork):
"""List the networks on a DHCP agent."""
- log = logging.getLogger(__name__ + '.ListNetworksOnDhcpAgent')
unknown_parts_flag = False
def get_parser(self, prog_name):
@@ -91,7 +83,8 @@ def get_parser(self, prog_name):
self).get_parser(prog_name)
parser.add_argument(
'dhcp_agent',
- help=_('ID of the DHCP agent'))
+ metavar='DHCP_AGENT',
+ help=_('ID of the DHCP agent.'))
return parser
def call_server(self, neutron_client, search_opts, parsed_args):
@@ -105,7 +98,6 @@ class ListDhcpAgentsHostingNetwork(neutronV20.ListCommand):
resource = 'agent'
_formatters = {}
- log = logging.getLogger(__name__ + '.ListDhcpAgentsHostingNetwork')
list_columns = ['id', 'host', 'admin_state_up', 'alive']
unknown_parts_flag = False
@@ -114,7 +106,8 @@ def get_parser(self, prog_name):
self).get_parser(prog_name)
parser.add_argument(
'network',
- help=_('Network to query'))
+ metavar='NETWORK',
+ help=_('Network to query.'))
return parser
def extend_list(self, data, parsed_args):
@@ -133,22 +126,20 @@ def call_server(self, neutron_client, search_opts, parsed_args):
class AddRouterToL3Agent(neutronV20.NeutronCommand):
"""Add a router to a L3 agent."""
- log = logging.getLogger(__name__ + '.AddRouterToL3Agent')
-
def get_parser(self, prog_name):
parser = super(AddRouterToL3Agent, self).get_parser(prog_name)
parser.add_argument(
'l3_agent',
- help=_('ID of the L3 agent'))
+ metavar='L3_AGENT',
+ help=_('ID of the L3 agent.'))
parser.add_argument(
'router',
- help=_('Router to add'))
+ metavar='ROUTER',
+ help=_('Router to add.'))
return parser
- def run(self, parsed_args):
- self.log.debug('run(%s)' % parsed_args)
+ def take_action(self, parsed_args):
neutron_client = self.get_client()
- neutron_client.format = parsed_args.request_format
_id = neutronV20.find_resourceid_by_name_or_id(
neutron_client, 'router', parsed_args.router)
neutron_client.add_router_to_l3_agent(parsed_args.l3_agent,
@@ -160,34 +151,31 @@ def run(self, parsed_args):
class RemoveRouterFromL3Agent(neutronV20.NeutronCommand):
"""Remove a router from a L3 agent."""
- log = logging.getLogger(__name__ + '.RemoveRouterFromL3Agent')
-
def get_parser(self, prog_name):
parser = super(RemoveRouterFromL3Agent, self).get_parser(prog_name)
parser.add_argument(
'l3_agent',
- help=_('ID of the L3 agent'))
+ metavar='L3_AGENT',
+ help=_('ID of the L3 agent.'))
parser.add_argument(
'router',
- help=_('Router to remove'))
+ metavar='ROUTER',
+ help=_('Router to remove.'))
return parser
- def run(self, parsed_args):
- self.log.debug('run(%s)' % parsed_args)
+ def take_action(self, parsed_args):
neutron_client = self.get_client()
- neutron_client.format = parsed_args.request_format
_id = neutronV20.find_resourceid_by_name_or_id(
neutron_client, 'router', parsed_args.router)
neutron_client.remove_router_from_l3_agent(
parsed_args.l3_agent, _id)
- print(_('Removed Router %s to L3 agent') % parsed_args.router,
+ print(_('Removed router %s from L3 agent') % parsed_args.router,
file=self.app.stdout)
class ListRoutersOnL3Agent(neutronV20.ListCommand):
"""List the routers on a L3 agent."""
- log = logging.getLogger(__name__ + '.ListRoutersOnL3Agent')
_formatters = {'external_gateway_info':
router._format_external_gateway_info}
list_columns = ['id', 'name', 'external_gateway_info']
@@ -199,7 +187,8 @@ def get_parser(self, prog_name):
self).get_parser(prog_name)
parser.add_argument(
'l3_agent',
- help=_('ID of the L3 agent to query'))
+ metavar='L3_AGENT',
+ help=_('ID of the L3 agent to query.'))
return parser
def call_server(self, neutron_client, search_opts, parsed_args):
@@ -213,7 +202,6 @@ class ListL3AgentsHostingRouter(neutronV20.ListCommand):
resource = 'agent'
_formatters = {}
- log = logging.getLogger(__name__ + '.ListL3AgentsHostingRouter')
list_columns = ['id', 'host', 'admin_state_up', 'alive']
unknown_parts_flag = False
@@ -221,10 +209,16 @@ def get_parser(self, prog_name):
parser = super(ListL3AgentsHostingRouter,
self).get_parser(prog_name)
parser.add_argument('router',
- help=_('Router to query'))
+ metavar='ROUTER',
+ help=_('Router to query.'))
return parser
def extend_list(self, data, parsed_args):
+ # Show the ha_state column only if the server responds with it,
+ # as some plugins do not support HA routers.
+ if any('ha_state' in agent for agent in data):
+ if 'ha_state' not in self.list_columns:
+ self.list_columns.append('ha_state')
for agent in data:
agent['alive'] = ":-)" if agent['alive'] else 'xxx'
@@ -240,7 +234,6 @@ def call_server(self, neutron_client, search_opts, parsed_args):
class ListPoolsOnLbaasAgent(neutronV20.ListCommand):
"""List the pools on a loadbalancer agent."""
- log = logging.getLogger(__name__ + '.ListPoolsOnLbaasAgent')
list_columns = ['id', 'name', 'lb_method', 'protocol',
'admin_state_up', 'status']
resource = 'pool'
@@ -250,7 +243,8 @@ def get_parser(self, prog_name):
parser = super(ListPoolsOnLbaasAgent, self).get_parser(prog_name)
parser.add_argument(
'lbaas_agent',
- help=_('ID of the loadbalancer agent to query'))
+ metavar='LBAAS_AGENT',
+ help=_('ID of the loadbalancer agent to query.'))
return parser
def call_server(self, neutron_client, search_opts, parsed_args):
@@ -267,7 +261,6 @@ class GetLbaasAgentHostingPool(neutronV20.ListCommand):
"""
resource = 'agent'
- log = logging.getLogger(__name__ + '.GetLbaasAgentHostingPool')
list_columns = ['id', 'host', 'admin_state_up', 'alive']
unknown_parts_flag = False
@@ -275,7 +268,8 @@ def get_parser(self, prog_name):
parser = super(GetLbaasAgentHostingPool,
self).get_parser(prog_name)
parser.add_argument('pool',
- help=_('Pool to query'))
+ metavar='POOL',
+ help=_('Pool to query.'))
return parser
def extend_list(self, data, parsed_args):
@@ -290,3 +284,58 @@ def call_server(self, neutron_client, search_opts, parsed_args):
agent = neutron_client.get_lbaas_agent_hosting_pool(**search_opts)
data = {'agents': [agent['agent']]}
return data
+
+
+class ListLoadBalancersOnLbaasAgent(neutronV20.ListCommand):
+ """List the loadbalancers on a loadbalancer v2 agent."""
+
+ list_columns = ['id', 'name', 'admin_state_up', 'provisioning_status']
+ resource = 'loadbalancer'
+ unknown_parts_flag = False
+
+ def get_parser(self, prog_name):
+ parser = super(ListLoadBalancersOnLbaasAgent, self).get_parser(
+ prog_name)
+ parser.add_argument(
+ 'lbaas_agent',
+ metavar='LBAAS_AGENT',
+ help=_('ID of the loadbalancer agent to query.'))
+ return parser
+
+ def call_server(self, neutron_client, search_opts, parsed_args):
+ data = neutron_client.list_loadbalancers_on_lbaas_agent(
+ parsed_args.lbaas_agent, **search_opts)
+ return data
+
+
+class GetLbaasAgentHostingLoadBalancer(neutronV20.ListCommand):
+ """Get lbaas v2 agent hosting a loadbalancer.
+
+ Deriving from ListCommand though server will return only one agent
+ to keep common output format for all agent schedulers
+ """
+
+ resource = 'agent'
+ list_columns = ['id', 'host', 'admin_state_up', 'alive']
+ unknown_parts_flag = False
+
+ def get_parser(self, prog_name):
+ parser = super(GetLbaasAgentHostingLoadBalancer,
+ self).get_parser(prog_name)
+ parser.add_argument('loadbalancer',
+ metavar='LOADBALANCER',
+ help=_('LoadBalancer to query.'))
+ return parser
+
+ def extend_list(self, data, parsed_args):
+ for agent in data:
+ agent['alive'] = ":-)" if agent['alive'] else 'xxx'
+
+ def call_server(self, neutron_client, search_opts, parsed_args):
+ _id = neutronV20.find_resourceid_by_name_or_id(
+ neutron_client, 'loadbalancer', parsed_args.loadbalancer)
+ search_opts['loadbalancer'] = _id
+ agent = neutron_client.get_lbaas_agent_hosting_loadbalancer(
+ **search_opts)
+ data = {'agents': [agent['agent']]}
+ return data
diff --git a/neutronclient/neutron/v2_0/auto_allocated_topology.py b/neutronclient/neutron/v2_0/auto_allocated_topology.py
new file mode 100644
index 000000000..a24959e16
--- /dev/null
+++ b/neutronclient/neutron/v2_0/auto_allocated_topology.py
@@ -0,0 +1,112 @@
+# 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 argparse
+
+from cliff import show
+from oslo_serialization import jsonutils
+
+from neutronclient._i18n import _
+from neutronclient.common import exceptions
+from neutronclient.neutron import v2_0
+
+
+class ShowAutoAllocatedTopology(v2_0.NeutronCommand, show.ShowOne):
+ """Show the auto-allocated topology of a given tenant."""
+
+ resource = 'auto_allocated_topology'
+
+ def get_parser(self, prog_name):
+ parser = super(ShowAutoAllocatedTopology, self).get_parser(prog_name)
+ parser.add_argument(
+ '--dry-run',
+ help=_('Validate the requirements for auto-allocated-topology. '
+ '(Does not return a topology.)'),
+ action='store_true')
+ parser.add_argument(
+ '--tenant-id', metavar='tenant-id',
+ help=_('The owner tenant ID.'))
+ # Allow people to do
+ # neutron auto-allocated-topology-show
+ # (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()
+ extra_values = v2_0.parse_args_to_dict(self.values_specs)
+ if extra_values:
+ raise exceptions.CommandError(
+ _("Invalid argument(s): --%s") % ', --'.join(extra_values))
+ tenant_id = parsed_args.tenant_id or parsed_args.pos_tenant_id
+ if parsed_args.dry_run:
+ data = client.validate_auto_allocated_topology_requirements(
+ tenant_id)
+ else:
+ data = client.get_auto_allocated_topology(tenant_id)
+ if self.resource in data:
+ for k, v in data[self.resource].items():
+ if isinstance(v, list):
+ value = ""
+ for _item in v:
+ if value:
+ value += "\n"
+ if isinstance(_item, dict):
+ value += jsonutils.dumps(_item)
+ else:
+ value += str(_item)
+ data[self.resource][k] = value
+ elif v == "dry-run=pass":
+ return ("dry-run",), ("pass",)
+ elif v is None:
+ data[self.resource][k] = ''
+ 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/availability_zone.py b/neutronclient/neutron/v2_0/availability_zone.py
new file mode 100644
index 000000000..2081d0990
--- /dev/null
+++ b/neutronclient/neutron/v2_0/availability_zone.py
@@ -0,0 +1,38 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# 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.neutron import v2_0 as neutronv20
+
+
+def add_az_hint_argument(parser, resource):
+ parser.add_argument(
+ '--availability-zone-hint', metavar='AVAILABILITY_ZONE',
+ action='append', dest='availability_zone_hints',
+ help=_('Availability Zone for the %s '
+ '(requires availability zone extension, '
+ 'this option can be repeated).') % resource)
+
+
+def args2body_az_hint(parsed_args, resource):
+ if parsed_args.availability_zone_hints:
+ resource['availability_zone_hints'] = (
+ parsed_args.availability_zone_hints)
+
+
+class ListAvailabilityZone(neutronv20.ListCommand):
+ """List availability zones."""
+
+ resource = 'availability_zone'
+ list_columns = ['name', 'resource', 'state']
+ pagination_support = True
+ sorting_support = True
diff --git a/neutronclient/neutron/v2_0/nsx/__init__.py b/neutronclient/neutron/v2_0/bgp/__init__.py
similarity index 100%
rename from neutronclient/neutron/v2_0/nsx/__init__.py
rename to neutronclient/neutron/v2_0/bgp/__init__.py
diff --git a/neutronclient/neutron/v2_0/bgp/dragentscheduler.py b/neutronclient/neutron/v2_0/bgp/dragentscheduler.py
new file mode 100644
index 000000000..d1e1de63f
--- /dev/null
+++ b/neutronclient/neutron/v2_0/bgp/dragentscheduler.py
@@ -0,0 +1,115 @@
+# 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.
+#
+
+from neutronclient._i18n import _
+from neutronclient.neutron import v2_0 as neutronV20
+from neutronclient.neutron.v2_0.bgp import speaker as bgp_speaker
+
+
+def add_common_args(parser):
+ parser.add_argument('dragent_id',
+ metavar='BGP_DRAGENT_ID',
+ help=_('ID of the Dynamic Routing agent.'))
+ parser.add_argument('bgp_speaker',
+ metavar='BGP_SPEAKER',
+ help=_('ID or name of the BGP speaker.'))
+
+
+class AddBGPSpeakerToDRAgent(neutronV20.NeutronCommand):
+ """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):
+ neutron_client = self.get_client()
+ _speaker_id = bgp_speaker.get_bgp_speaker_id(neutron_client,
+ parsed_args.bgp_speaker)
+ neutron_client.add_bgp_speaker_to_dragent(
+ parsed_args.dragent_id, {'bgp_speaker_id': _speaker_id})
+ print(_('Associated BGP speaker %s to the Dynamic Routing agent.')
+ % parsed_args.bgp_speaker, file=self.app.stdout)
+
+
+class RemoveBGPSpeakerFromDRAgent(neutronV20.NeutronCommand):
+ """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):
+ neutron_client = self.get_client()
+ _speaker_id = bgp_speaker.get_bgp_speaker_id(neutron_client,
+ parsed_args.bgp_speaker)
+ neutron_client.remove_bgp_speaker_from_dragent(parsed_args.dragent_id,
+ _speaker_id)
+ print(_('Disassociated BGP speaker %s from the '
+ 'Dynamic Routing agent.')
+ % parsed_args.bgp_speaker, file=self.app.stdout)
+
+
+class ListBGPSpeakersOnDRAgent(neutronV20.ListCommand):
+ """List BGP speakers hosted by a Dynamic Routing agent."""
+
+ list_columns = ['id', 'name', 'local_as', 'ip_version']
+ resource = 'bgp_speaker'
+
+ def get_parser(self, prog_name):
+ parser = super(ListBGPSpeakersOnDRAgent,
+ self).get_parser(prog_name)
+ parser.add_argument(
+ 'dragent_id',
+ metavar='BGP_DRAGENT_ID',
+ help=_('ID of the Dynamic Routing agent.'))
+ return parser
+
+ def call_server(self, neutron_client, search_opts, parsed_args):
+ data = neutron_client.list_bgp_speaker_on_dragent(
+ parsed_args.dragent_id, **search_opts)
+ return data
+
+
+class ListDRAgentsHostingBGPSpeaker(neutronV20.ListCommand):
+ """List Dynamic Routing agents hosting a BGP speaker."""
+
+ resource = 'agent'
+ _formatters = {}
+ list_columns = ['id', 'host', 'admin_state_up', 'alive']
+ unknown_parts_flag = False
+
+ def get_parser(self, prog_name):
+ parser = super(ListDRAgentsHostingBGPSpeaker,
+ self).get_parser(prog_name)
+ parser.add_argument('bgp_speaker',
+ metavar='BGP_SPEAKER',
+ help=_('ID or name of the BGP speaker.'))
+ return parser
+
+ def extend_list(self, data, parsed_args):
+ for agent in data:
+ agent['alive'] = ":-)" if agent['alive'] else 'xxx'
+
+ def call_server(self, neutron_client, search_opts, parsed_args):
+ _speaker_id = bgp_speaker.get_bgp_speaker_id(neutron_client,
+ parsed_args.bgp_speaker)
+ search_opts['bgp_speaker'] = _speaker_id
+ data = neutron_client.list_dragents_hosting_bgp_speaker(**search_opts)
+ return data
diff --git a/neutronclient/neutron/v2_0/bgp/peer.py b/neutronclient/neutron/v2_0/bgp/peer.py
new file mode 100644
index 000000000..8fefb660a
--- /dev/null
+++ b/neutronclient/neutron/v2_0/bgp/peer.py
@@ -0,0 +1,127 @@
+# 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.
+#
+
+from neutronclient._i18n import _
+from neutronclient.common import exceptions
+from neutronclient.common import utils
+from neutronclient.common import validators
+from neutronclient.neutron import v2_0 as neutronv20
+
+
+def get_bgp_peer_id(client, id_or_name):
+ return neutronv20.find_resourceid_by_name_or_id(client,
+ 'bgp_peer',
+ id_or_name)
+
+
+def validate_peer_attributes(parsed_args):
+ # Validate AS number
+ validators.validate_int_range(parsed_args, 'remote_as',
+ neutronv20.bgp.speaker.MIN_AS_NUM,
+ neutronv20.bgp.speaker.MAX_AS_NUM)
+ # Validate password
+ if parsed_args.auth_type != 'none' and 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:
+ raise exceptions.CommandError(_('Must provide auth-type if password '
+ 'is specified.'))
+
+
+class ListPeers(neutronv20.ListCommand):
+ """List BGP peers."""
+
+ resource = 'bgp_peer'
+ list_columns = ['id', 'name', 'peer_ip', 'remote_as']
+ pagination_support = True
+ sorting_support = True
+
+
+class ShowPeer(neutronv20.ShowCommand):
+ """Show information of a given BGP peer."""
+
+ resource = 'bgp_peer'
+
+
+class CreatePeer(neutronv20.CreateCommand):
+ """Create a BGP Peer."""
+
+ resource = 'bgp_peer'
+
+ def add_known_arguments(self, parser):
+ parser.add_argument(
+ 'name',
+ metavar='NAME',
+ help=_('Name of the BGP peer to create.'))
+ parser.add_argument(
+ '--peer-ip',
+ metavar='PEER_IP_ADDRESS',
+ required=True,
+ help=_('Peer IP address.'))
+ parser.add_argument(
+ '--remote-as',
+ required=True,
+ metavar='PEER_REMOTE_AS',
+ help=_('Peer AS number. (Integer in [%(min_val)s, %(max_val)s] '
+ 'is allowed.)') %
+ {'min_val': neutronv20.bgp.speaker.MIN_AS_NUM,
+ 'max_val': neutronv20.bgp.speaker.MAX_AS_NUM})
+ parser.add_argument(
+ '--auth-type',
+ metavar='PEER_AUTH_TYPE',
+ choices=['none', 'md5'],
+ default='none',
+ type=utils.convert_to_lowercase,
+ help=_('Authentication algorithm. Supported algorithms: '
+ 'none(default), md5'))
+ parser.add_argument(
+ '--password',
+ metavar='AUTH_PASSWORD',
+ help=_('Authentication password.'))
+
+ def args2body(self, parsed_args):
+ body = {}
+ validate_peer_attributes(parsed_args)
+ neutronv20.update_dict(parsed_args, body,
+ ['name', 'peer_ip',
+ 'remote_as', 'auth_type', 'password'])
+ return {self.resource: body}
+
+
+class UpdatePeer(neutronv20.UpdateCommand):
+ """Update BGP Peer's information."""
+
+ resource = 'bgp_peer'
+
+ def add_known_arguments(self, parser):
+ parser.add_argument(
+ '--name',
+ help=_('Updated name of the BGP peer.'))
+ parser.add_argument(
+ '--password',
+ metavar='AUTH_PASSWORD',
+ help=_('Updated authentication password.'))
+
+ def args2body(self, parsed_args):
+ body = {}
+ neutronv20.update_dict(parsed_args, body, ['name', 'password'])
+ return {self.resource: body}
+
+
+class DeletePeer(neutronv20.DeleteCommand):
+ """Delete a BGP peer."""
+
+ resource = 'bgp_peer'
diff --git a/neutronclient/neutron/v2_0/bgp/speaker.py b/neutronclient/neutron/v2_0/bgp/speaker.py
new file mode 100644
index 000000000..34ce22eb8
--- /dev/null
+++ b/neutronclient/neutron/v2_0/bgp/speaker.py
@@ -0,0 +1,275 @@
+# 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.
+#
+
+from neutronclient._i18n import _
+from neutronclient.common import utils
+from neutronclient.common import validators
+from neutronclient.neutron import v2_0 as neutronv20
+from neutronclient.neutron.v2_0.bgp import peer as bgp_peer
+
+# Allowed BGP Autonomous number range
+MIN_AS_NUM = 1
+MAX_AS_NUM = 4294967295
+
+
+def get_network_id(client, id_or_name):
+ return neutronv20.find_resourceid_by_name_or_id(client,
+ 'network',
+ id_or_name)
+
+
+def get_bgp_speaker_id(client, id_or_name):
+ return neutronv20.find_resourceid_by_name_or_id(client,
+ 'bgp_speaker',
+ id_or_name)
+
+
+def validate_speaker_attributes(parsed_args):
+ # Validate AS number
+ validators.validate_int_range(parsed_args, 'local_as',
+ MIN_AS_NUM, MAX_AS_NUM)
+
+
+def add_common_arguments(parser):
+ utils.add_boolean_argument(
+ parser, '--advertise-floating-ip-host-routes',
+ help=_('Whether to enable or disable the advertisement '
+ 'of floating-ip host routes by the BGP speaker. '
+ 'By default floating ip host routes will be '
+ 'advertised by the BGP speaker.'))
+ utils.add_boolean_argument(
+ parser, '--advertise-tenant-networks',
+ help=_('Whether to enable or disable the advertisement '
+ 'of tenant network routes by the BGP speaker. '
+ 'By default tenant network routes will be '
+ 'advertised by the BGP speaker.'))
+
+
+def args2body_common_arguments(body, parsed_args):
+ neutronv20.update_dict(parsed_args, body,
+ ['name',
+ 'advertise_floating_ip_host_routes',
+ 'advertise_tenant_networks'])
+
+
+class ListSpeakers(neutronv20.ListCommand):
+ """List BGP speakers."""
+
+ resource = 'bgp_speaker'
+ list_columns = ['id', 'name', 'local_as', 'ip_version']
+ pagination_support = True
+ sorting_support = True
+
+
+class ShowSpeaker(neutronv20.ShowCommand):
+ """Show information of a given BGP speaker."""
+
+ resource = 'bgp_speaker'
+
+
+class CreateSpeaker(neutronv20.CreateCommand):
+ """Create a BGP Speaker."""
+
+ resource = 'bgp_speaker'
+
+ def add_known_arguments(self, parser):
+ parser.add_argument(
+ 'name',
+ metavar='NAME',
+ help=_('Name of the BGP speaker to create.'))
+ parser.add_argument(
+ '--local-as',
+ metavar='LOCAL_AS',
+ required=True,
+ help=_('Local AS number. (Integer in [%(min_val)s, %(max_val)s] '
+ 'is allowed.)') % {'min_val': MIN_AS_NUM,
+ 'max_val': 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)
+
+ def args2body(self, parsed_args):
+ body = {}
+ validate_speaker_attributes(parsed_args)
+ body['local_as'] = parsed_args.local_as
+ body['ip_version'] = parsed_args.ip_version
+ args2body_common_arguments(body, parsed_args)
+ return {self.resource: body}
+
+
+class UpdateSpeaker(neutronv20.UpdateCommand):
+ """Update BGP Speaker's information."""
+
+ resource = 'bgp_speaker'
+
+ def add_known_arguments(self, parser):
+ parser.add_argument(
+ '--name',
+ help=_('Name of the BGP speaker to update.'))
+ add_common_arguments(parser)
+
+ def args2body(self, parsed_args):
+ body = {}
+ args2body_common_arguments(body, parsed_args)
+ return {self.resource: body}
+
+
+class DeleteSpeaker(neutronv20.DeleteCommand):
+ """Delete a BGP speaker."""
+
+ resource = 'bgp_speaker'
+
+
+class AddPeerToSpeaker(neutronv20.NeutronCommand):
+ """Add a peer to the BGP speaker."""
+
+ def get_parser(self, prog_name):
+ parser = super(AddPeerToSpeaker, self).get_parser(prog_name)
+ parser.add_argument(
+ 'bgp_speaker',
+ metavar='BGP_SPEAKER',
+ help=_('ID or name of the BGP speaker.'))
+ parser.add_argument(
+ 'bgp_peer',
+ metavar='BGP_PEER',
+ help=_('ID or name of the BGP peer to add.'))
+ return parser
+
+ def take_action(self, parsed_args):
+ neutron_client = self.get_client()
+ _speaker_id = get_bgp_speaker_id(neutron_client,
+ parsed_args.bgp_speaker)
+ _peer_id = bgp_peer.get_bgp_peer_id(neutron_client,
+ parsed_args.bgp_peer)
+ neutron_client.add_peer_to_bgp_speaker(_speaker_id,
+ {'bgp_peer_id': _peer_id})
+ print(_('Added BGP peer %(peer)s to BGP speaker %(speaker)s.') %
+ {'peer': parsed_args.bgp_peer,
+ 'speaker': parsed_args.bgp_speaker},
+ file=self.app.stdout)
+
+
+class RemovePeerFromSpeaker(neutronv20.NeutronCommand):
+ """Remove a peer from the BGP speaker."""
+
+ def get_parser(self, prog_name):
+ parser = super(RemovePeerFromSpeaker, self).get_parser(prog_name)
+ parser.add_argument(
+ 'bgp_speaker',
+ metavar='BGP_SPEAKER',
+ help=_('ID or name of the BGP speaker.'))
+ parser.add_argument(
+ 'bgp_peer',
+ metavar='BGP_PEER',
+ help=_('ID or name of the BGP peer to remove.'))
+ return parser
+
+ def take_action(self, parsed_args):
+ neutron_client = self.get_client()
+ _speaker_id = get_bgp_speaker_id(neutron_client,
+ parsed_args.bgp_speaker)
+ _peer_id = bgp_peer.get_bgp_peer_id(neutron_client,
+ parsed_args.bgp_peer)
+ 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,
+ 'speaker': parsed_args.bgp_speaker},
+ file=self.app.stdout)
+
+
+class AddNetworkToSpeaker(neutronv20.NeutronCommand):
+ """Add a network to the BGP speaker."""
+
+ def get_parser(self, prog_name):
+ parser = super(AddNetworkToSpeaker, self).get_parser(prog_name)
+ parser.add_argument(
+ 'bgp_speaker',
+ metavar='BGP_SPEAKER',
+ help=_('ID or name of the BGP speaker.'))
+ parser.add_argument(
+ 'network',
+ metavar='NETWORK',
+ help=_('ID or name of the network to add.'))
+ return parser
+
+ def take_action(self, parsed_args):
+ neutron_client = self.get_client()
+ _speaker_id = get_bgp_speaker_id(neutron_client,
+ parsed_args.bgp_speaker)
+ _net_id = get_network_id(neutron_client,
+ parsed_args.network)
+ neutron_client.add_network_to_bgp_speaker(_speaker_id,
+ {'network_id': _net_id})
+ print(_('Added network %(net)s to BGP speaker %(speaker)s.') %
+ {'net': parsed_args.network, 'speaker': parsed_args.bgp_speaker},
+ file=self.app.stdout)
+
+
+class RemoveNetworkFromSpeaker(neutronv20.NeutronCommand):
+ """Remove a network from the BGP speaker."""
+
+ def get_parser(self, prog_name):
+ parser = super(RemoveNetworkFromSpeaker, self).get_parser(prog_name)
+ parser.add_argument(
+ 'bgp_speaker',
+ metavar='BGP_SPEAKER',
+ help=_('ID or name of the BGP speaker.'))
+ parser.add_argument(
+ 'network',
+ metavar='NETWORK',
+ help=_('ID or name of the network to remove.'))
+ return parser
+
+ def take_action(self, parsed_args):
+ neutron_client = self.get_client()
+ _speaker_id = get_bgp_speaker_id(neutron_client,
+ parsed_args.bgp_speaker)
+ _net_id = get_network_id(neutron_client,
+ parsed_args.network)
+ neutron_client.remove_network_from_bgp_speaker(_speaker_id,
+ {'network_id': _net_id})
+ print(_('Removed network %(net)s from BGP speaker %(speaker)s.') %
+ {'net': parsed_args.network, 'speaker': parsed_args.bgp_speaker},
+ file=self.app.stdout)
+
+
+class ListRoutesAdvertisedBySpeaker(neutronv20.ListCommand):
+ """List routes advertised by a given BGP speaker."""
+
+ list_columns = ['id', 'destination', 'next_hop']
+ resource = 'advertised_route'
+ pagination_support = True
+ sorting_support = True
+
+ def get_parser(self, prog_name):
+ parser = super(ListRoutesAdvertisedBySpeaker,
+ self).get_parser(prog_name)
+ parser.add_argument(
+ 'bgp_speaker',
+ metavar='BGP_SPEAKER',
+ help=_('ID or name of the BGP speaker.'))
+ return parser
+
+ def call_server(self, neutron_client, search_opts, parsed_args):
+ _speaker_id = get_bgp_speaker_id(neutron_client,
+ parsed_args.bgp_speaker)
+ data = neutron_client.list_route_advertised_from_bgp_speaker(
+ _speaker_id, **search_opts)
+ return data
diff --git a/neutronclient/openstack/__init__.py b/neutronclient/neutron/v2_0/contrib/__init__.py
similarity index 100%
rename from neutronclient/openstack/__init__.py
rename to neutronclient/neutron/v2_0/contrib/__init__.py
diff --git a/neutronclient/neutron/v2_0/contrib/_fox_sockets.py b/neutronclient/neutron/v2_0/contrib/_fox_sockets.py
new file mode 100644
index 000000000..1ff5ffd2b
--- /dev/null
+++ b/neutronclient/neutron/v2_0/contrib/_fox_sockets.py
@@ -0,0 +1,93 @@
+# 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.
+#
+
+from neutronclient._i18n import _
+from neutronclient.common import extension
+
+
+def _add_updatable_args(parser):
+ parser.add_argument(
+ 'name',
+ help=_('Name of this fox socket.'))
+
+
+def _updatable_args2body(parsed_args, body, client):
+ if parsed_args.name:
+ body['name'] = parsed_args.name
+
+
+class FoxInSocket(extension.NeutronClientExtension):
+ """Define required variables for resource operations."""
+
+ resource = 'fox_socket'
+ resource_plural = '%ss' % resource
+ object_path = '/%s' % resource_plural
+ resource_path = '/%s/%%s' % resource_plural
+ versions = ['2.0']
+
+
+class FoxInSocketsList(extension.ClientExtensionList, FoxInSocket):
+ """List fox sockets."""
+
+ shell_command = 'fox-sockets-list'
+ list_columns = ['id', 'name']
+ pagination_support = True
+ sorting_support = True
+
+
+class FoxInSocketsCreate(extension.ClientExtensionCreate, FoxInSocket):
+ """Create a fox socket."""
+
+ shell_command = 'fox-sockets-create'
+ list_columns = ['id', 'name']
+
+ def add_known_arguments(self, parser):
+ _add_updatable_args(parser)
+
+ def args2body(self, parsed_args):
+ body = {}
+ client = self.get_client()
+ _updatable_args2body(parsed_args, body, client)
+ return {'fox_socket': body}
+
+
+class FoxInSocketsUpdate(extension.ClientExtensionUpdate, FoxInSocket):
+ """Update a fox socket."""
+
+ shell_command = 'fox-sockets-update'
+ list_columns = ['id', 'name']
+
+ def add_known_arguments(self, parser):
+ # _add_updatable_args(parser)
+ parser.add_argument(
+ '--name',
+ help=_('Name of this fox socket.'))
+
+ def args2body(self, parsed_args):
+ body = {'name': parsed_args.name}
+ return {'fox_socket': body}
+
+
+class FoxInSocketsDelete(extension.ClientExtensionDelete, FoxInSocket):
+ """Delete a fox socket."""
+
+ shell_command = 'fox-sockets-delete'
+
+
+class FoxInSocketsShow(extension.ClientExtensionShow, FoxInSocket):
+ """Show a fox socket."""
+
+ shell_command = 'fox-sockets-show'
diff --git a/neutronclient/neutron/v2_0/credential.py b/neutronclient/neutron/v2_0/credential.py
deleted file mode 100644
index 066215eb4..000000000
--- a/neutronclient/neutron/v2_0/credential.py
+++ /dev/null
@@ -1,79 +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 logging
-
-from neutronclient.neutron import v2_0 as neutronV20
-from neutronclient.openstack.common.gettextutils import _
-
-
-class ListCredential(neutronV20.ListCommand):
- """List credentials that belong to a given tenant."""
-
- resource = 'credential'
- log = logging.getLogger(__name__ + '.ListCredential')
- _formatters = {}
- list_columns = ['credential_id', 'credential_name', 'user_name',
- 'password', 'type']
-
-
-class ShowCredential(neutronV20.ShowCommand):
- """Show information of a given credential."""
-
- resource = 'credential'
- log = logging.getLogger(__name__ + '.ShowCredential')
- allow_names = False
-
-
-class CreateCredential(neutronV20.CreateCommand):
- """Creates a credential."""
-
- resource = 'credential'
- log = logging.getLogger(__name__ + '.CreateCredential')
-
- def add_known_arguments(self, parser):
- parser.add_argument(
- 'credential_name',
- help=_('Name/Ip address for Credential'))
- parser.add_argument(
- 'credential_type',
- help=_('Type of the Credential'))
- parser.add_argument(
- '--username',
- help=_('Username for the credential'))
- parser.add_argument(
- '--password',
- help=_('Password for the credential'))
-
- def args2body(self, parsed_args):
- body = {'credential': {
- 'credential_name': parsed_args.credential_name}}
-
- if parsed_args.credential_type:
- body['credential'].update({'type':
- parsed_args.credential_type})
- if parsed_args.username:
- body['credential'].update({'user_name':
- parsed_args.username})
- if parsed_args.password:
- body['credential'].update({'password':
- parsed_args.password})
- return body
-
-
-class DeleteCredential(neutronV20.DeleteCommand):
- """Delete a given credential."""
-
- log = logging.getLogger(__name__ + '.DeleteCredential')
- resource = 'credential'
- allow_names = False
diff --git a/neutronclient/neutron/v2_0/dns.py b/neutronclient/neutron/v2_0/dns.py
new file mode 100644
index 000000000..0cf7ec0b4
--- /dev/null
+++ b/neutronclient/neutron/v2_0/dns.py
@@ -0,0 +1,67 @@
+# Copyright (c) 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.
+
+from neutronclient._i18n import _
+
+
+def add_dns_argument_create(parser, resource, attribute):
+ # Add dns_name and dns_domain support to network, port and floatingip
+ # create
+ argument = '--dns-%s' % attribute
+ parser.add_argument(
+ argument,
+ help=_('Assign DNS %(attribute)s to the %(resource)s '
+ '(requires DNS integration '
+ 'extension)') % {'attribute': attribute, 'resource': resource})
+
+
+def args2body_dns_create(parsed_args, resource, attribute):
+ # Add dns_name and dns_domain support to network, port and floatingip
+ # create
+ destination = 'dns_%s' % attribute
+ argument = getattr(parsed_args, destination)
+ if argument:
+ resource[destination] = argument
+
+
+def add_dns_argument_update(parser, resource, attribute):
+ # Add dns_name and dns_domain support to network, port and floatingip
+ # update
+ argument = '--dns-%s' % attribute
+ no_argument = '--no-dns-%s' % attribute
+ dns_args = parser.add_mutually_exclusive_group()
+ dns_args.add_argument(
+ argument,
+ help=_('Assign DNS %(attribute)s to the %(resource)s '
+ '(requires DNS integration '
+ 'extension.)') % {'attribute': attribute, 'resource': resource})
+ dns_args.add_argument(
+ no_argument, action='store_true',
+ help=_('Unassign DNS %(attribute)s from the %(resource)s '
+ '(requires DNS integration '
+ 'extension.)') % {'attribute': attribute, 'resource': resource})
+
+
+def args2body_dns_update(parsed_args, resource, attribute):
+ # Add dns_name and dns_domain support to network, port and floatingip
+ # update
+ destination = 'dns_%s' % attribute
+ no_destination = 'no_dns_%s' % attribute
+ argument = getattr(parsed_args, destination)
+ no_argument = getattr(parsed_args, no_destination)
+ if argument:
+ resource[destination] = argument
+ if no_argument:
+ resource[destination] = ""
diff --git a/neutronclient/neutron/v2_0/extension.py b/neutronclient/neutron/v2_0/extension.py
index 599165955..24905d59d 100644
--- a/neutronclient/neutron/v2_0/extension.py
+++ b/neutronclient/neutron/v2_0/extension.py
@@ -14,17 +14,13 @@
# under the License.
#
-import logging
-
from neutronclient.neutron import v2_0 as cmd_base
-from neutronclient.openstack.common.gettextutils import _
class ListExt(cmd_base.ListCommand):
"""List all extensions."""
resource = 'extension'
- log = logging.getLogger(__name__ + '.ListExt')
list_columns = ['alias', 'name']
@@ -32,13 +28,4 @@ class ShowExt(cmd_base.ShowCommand):
"""Show information of a given resource."""
resource = "extension"
- log = logging.getLogger(__name__ + '.ShowExt')
allow_names = False
-
- def get_parser(self, prog_name):
- parser = super(cmd_base.ShowCommand, self).get_parser(prog_name)
- cmd_base.add_show_list_common_argument(parser)
- parser.add_argument(
- 'id', metavar='EXT-ALIAS',
- help=_('The extension alias'))
- return parser
diff --git a/neutronclient/openstack/common/__init__.py b/neutronclient/neutron/v2_0/flavor/__init__.py
similarity index 100%
rename from neutronclient/openstack/common/__init__.py
rename to neutronclient/neutron/v2_0/flavor/__init__.py
diff --git a/neutronclient/neutron/v2_0/flavor/flavor.py b/neutronclient/neutron/v2_0/flavor/flavor.py
new file mode 100644
index 000000000..36a052ef2
--- /dev/null
+++ b/neutronclient/neutron/v2_0/flavor/flavor.py
@@ -0,0 +1,163 @@
+# 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 argparse
+
+from neutronclient._i18n import _
+from neutronclient.common import utils
+from neutronclient.neutron import v2_0 as neutronV20
+
+
+class ListFlavor(neutronV20.ListCommand):
+ """List Neutron service flavors."""
+
+ resource = 'flavor'
+ list_columns = ['id', 'name', 'service_type', 'enabled']
+ pagination_support = True
+ sorting_support = True
+
+
+class ShowFlavor(neutronV20.ShowCommand):
+ """Show information about a given Neutron service flavor."""
+
+ resource = 'flavor'
+
+
+class CreateFlavor(neutronV20.CreateCommand):
+ """Create a Neutron service flavor."""
+
+ resource = 'flavor'
+
+ def add_known_arguments(self, parser):
+ parser.add_argument(
+ 'name',
+ metavar='NAME',
+ help=_('Name for the flavor.'))
+ parser.add_argument(
+ 'service_type',
+ metavar='SERVICE_TYPE',
+ help=_('Service type to which the flavor applies to: e.g. VPN. '
+ '(See service-provider-list for loaded examples.)'))
+ parser.add_argument(
+ '--description',
+ help=_('Description for the flavor.'))
+ utils.add_boolean_argument(
+ parser,
+ '--enabled',
+ default=argparse.SUPPRESS,
+ help=_('Sets enabled flag.'))
+
+ def args2body(self, parsed_args):
+ body = {}
+ neutronV20.update_dict(parsed_args, body,
+ ['name', 'description', 'service_type',
+ 'enabled'])
+ return {self.resource: body}
+
+
+class DeleteFlavor(neutronV20.DeleteCommand):
+ """Delete a given Neutron service flavor."""
+
+ resource = 'flavor'
+
+
+class UpdateFlavor(neutronV20.UpdateCommand):
+ """Update a Neutron service flavor."""
+
+ resource = 'flavor'
+
+ def add_known_arguments(self, parser):
+ parser.add_argument(
+ '--name',
+ help=_('Name for the flavor.'))
+ parser.add_argument(
+ '--description',
+ help=_('Description for the flavor.'))
+ utils.add_boolean_argument(
+ parser,
+ '--enabled',
+ default=argparse.SUPPRESS,
+ help=_('Sets enabled flag.'))
+
+ def args2body(self, parsed_args):
+ body = {}
+ neutronV20.update_dict(parsed_args, body,
+ ['name', 'description', 'enabled'])
+ return {self.resource: body}
+
+
+class AssociateFlavor(neutronV20.NeutronCommand):
+ """Associate a Neutron service flavor with a flavor profile."""
+
+ resource = 'flavor'
+
+ def get_parser(self, prog_name):
+ parser = super(AssociateFlavor, self).get_parser(prog_name)
+ parser.add_argument(
+ 'flavor',
+ metavar='FLAVOR',
+ help=_('ID or name of the flavor to associate.'))
+ parser.add_argument(
+ 'flavor_profile',
+ metavar='FLAVOR_PROFILE',
+ help=_('ID of the flavor profile to be associated with the '
+ 'flavor.'))
+ return parser
+
+ def take_action(self, parsed_args):
+ neutron_client = self.get_client()
+ flavor_id = neutronV20.find_resourceid_by_name_or_id(
+ neutron_client, 'flavor', parsed_args.flavor)
+ service_profile_id = neutronV20.find_resourceid_by_id(
+ neutron_client, 'service_profile', parsed_args.flavor_profile)
+ body = {'service_profile': {'id': service_profile_id}}
+ neutron_client.associate_flavor(flavor_id, body)
+ print((_('Associated flavor %(flavor)s with '
+ 'flavor_profile %(profile)s') %
+ {'flavor': parsed_args.flavor,
+ 'profile': parsed_args.flavor_profile}),
+ file=self.app.stdout)
+
+
+class DisassociateFlavor(neutronV20.NeutronCommand):
+ """Disassociate a Neutron service flavor from a flavor profile."""
+
+ resource = 'flavor'
+
+ def get_parser(self, prog_name):
+ parser = super(DisassociateFlavor, self).get_parser(prog_name)
+ parser.add_argument(
+ 'flavor',
+ metavar='FLAVOR',
+ help=_('ID or name of the flavor to be disassociated.'))
+ parser.add_argument(
+ 'flavor_profile',
+ metavar='FLAVOR_PROFILE',
+ help=_('ID of the flavor profile to be disassociated from the '
+ 'flavor.'))
+ return parser
+
+ def take_action(self, parsed_args):
+ neutron_client = self.get_client()
+ flavor_id = neutronV20.find_resourceid_by_name_or_id(
+ neutron_client, 'flavor', parsed_args.flavor)
+ service_profile_id = neutronV20.find_resourceid_by_id(
+ neutron_client, 'service_profile', parsed_args.flavor_profile)
+ neutron_client.disassociate_flavor(flavor_id, service_profile_id)
+ print((_('Disassociated flavor %(flavor)s from '
+ 'flavor_profile %(profile)s') %
+ {'flavor': parsed_args.flavor,
+ 'profile': parsed_args.flavor_profile}),
+ file=self.app.stdout)
diff --git a/neutronclient/neutron/v2_0/flavor/flavor_profile.py b/neutronclient/neutron/v2_0/flavor/flavor_profile.py
new file mode 100644
index 000000000..894a8a67c
--- /dev/null
+++ b/neutronclient/neutron/v2_0/flavor/flavor_profile.py
@@ -0,0 +1,99 @@
+# 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 argparse
+
+from neutronclient._i18n import _
+from neutronclient.common import utils
+from neutronclient.neutron import v2_0 as neutronV20
+
+
+class ListFlavorProfile(neutronV20.ListCommand):
+ """List Neutron service flavor profiles."""
+
+ resource = 'service_profile'
+ list_columns = ['id', 'description', 'enabled', 'metainfo']
+ pagination_support = True
+ sorting_support = True
+
+
+class ShowFlavorProfile(neutronV20.ShowCommand):
+ """Show information about a given Neutron service flavor profile."""
+
+ resource = 'service_profile'
+
+
+class CreateFlavorProfile(neutronV20.CreateCommand):
+ """Create a Neutron service flavor profile."""
+
+ resource = 'service_profile'
+
+ def add_known_arguments(self, parser):
+ parser.add_argument(
+ '--description',
+ help=_('Description for the flavor profile.'))
+ parser.add_argument(
+ '--driver',
+ help=_('Python module path to driver.'))
+ parser.add_argument(
+ '--metainfo',
+ help=_('Metainfo for the flavor profile.'))
+ utils.add_boolean_argument(
+ parser,
+ '--enabled',
+ default=argparse.SUPPRESS,
+ help=_('Sets enabled flag.'))
+
+ def args2body(self, parsed_args):
+ body = {}
+ neutronV20.update_dict(parsed_args, body,
+ ['description', 'driver', 'enabled',
+ 'metainfo'])
+ return {self.resource: body}
+
+
+class DeleteFlavorProfile(neutronV20.DeleteCommand):
+ """Delete a given Neutron service flavor profile."""
+
+ resource = 'service_profile'
+
+
+class UpdateFlavorProfile(neutronV20.UpdateCommand):
+ """Update a given Neutron service flavor profile."""
+
+ resource = 'service_profile'
+
+ def add_known_arguments(self, parser):
+ parser.add_argument(
+ '--description',
+ help=_('Description for the flavor profile.'))
+ parser.add_argument(
+ '--driver',
+ help=_('Python module path to driver.'))
+ parser.add_argument(
+ '--metainfo',
+ help=_('Metainfo for the flavor profile.'))
+ utils.add_boolean_argument(
+ parser,
+ '--enabled',
+ default=argparse.SUPPRESS,
+ help=_('Sets enabled flag.'))
+
+ def args2body(self, parsed_args):
+ body = {}
+ neutronV20.update_dict(parsed_args, body,
+ ['description', 'driver', 'enabled',
+ 'metainfo'])
+ return {self.resource: body}
diff --git a/neutronclient/neutron/v2_0/floatingip.py b/neutronclient/neutron/v2_0/floatingip.py
index fb2c2f759..5f4ced40a 100644
--- a/neutronclient/neutron/v2_0/floatingip.py
+++ b/neutronclient/neutron/v2_0/floatingip.py
@@ -14,20 +14,17 @@
# under the License.
#
-from __future__ import print_function
-
import argparse
-import logging
+from neutronclient._i18n import _
from neutronclient.neutron import v2_0 as neutronV20
-from neutronclient.openstack.common.gettextutils import _
+from neutronclient.neutron.v2_0 import dns
class ListFloatingIP(neutronV20.ListCommand):
- """List floating ips that belong to a given tenant."""
+ """List floating IPs that belong to a given tenant."""
resource = 'floatingip'
- log = logging.getLogger(__name__ + '.ListFloatingIP')
list_columns = ['id', 'fixed_ip_address', 'floating_ip_address',
'port_id']
pagination_support = True
@@ -35,119 +32,117 @@ class ListFloatingIP(neutronV20.ListCommand):
class ShowFloatingIP(neutronV20.ShowCommand):
- """Show information of a given floating ip."""
+ """Show information of a given floating IP."""
resource = 'floatingip'
- log = logging.getLogger(__name__ + '.ShowFloatingIP')
allow_names = False
class CreateFloatingIP(neutronV20.CreateCommand):
- """Create a floating ip for a given tenant."""
+ """Create a floating IP for a given tenant."""
resource = 'floatingip'
- log = logging.getLogger(__name__ + '.CreateFloatingIP')
def add_known_arguments(self, parser):
parser.add_argument(
'floating_network_id', metavar='FLOATING_NETWORK',
- help=_('Network name or id to allocate floating IP from'))
+ help=_('ID or name of the network from which '
+ 'the floating IP is allocated.'))
+ parser.add_argument(
+ '--description',
+ help=_('Description of the floating IP.'))
parser.add_argument(
'--port-id',
- help=_('ID of the port to be associated with the floatingip'))
+ help=_('ID of the port to be associated with the floating IP.'))
parser.add_argument(
'--port_id',
help=argparse.SUPPRESS)
parser.add_argument(
'--fixed-ip-address',
- help=_('IP address on the port (only required if port has multiple'
- 'IPs)'))
+ help=_('IP address on the port (only required if port has '
+ 'multiple IPs).'))
parser.add_argument(
'--fixed_ip_address',
help=argparse.SUPPRESS)
+ parser.add_argument(
+ '--floating-ip-address',
+ help=_('IP address of the floating IP'))
+ parser.add_argument(
+ '--subnet',
+ dest='subnet_id',
+ help=_('Subnet ID on which you want to create the floating IP.'))
+ dns.add_dns_argument_create(parser, self.resource, 'domain')
+ dns.add_dns_argument_create(parser, self.resource, 'name')
def args2body(self, parsed_args):
_network_id = neutronV20.find_resourceid_by_name_or_id(
self.get_client(), 'network', parsed_args.floating_network_id)
- body = {self.resource: {'floating_network_id': _network_id}}
- if parsed_args.port_id:
- body[self.resource].update({'port_id': parsed_args.port_id})
- if parsed_args.tenant_id:
- body[self.resource].update({'tenant_id': parsed_args.tenant_id})
- if parsed_args.fixed_ip_address:
- body[self.resource].update({'fixed_ip_address':
- parsed_args.fixed_ip_address})
- return body
+ body = {'floating_network_id': _network_id}
+ neutronV20.update_dict(parsed_args, body,
+ ['port_id', 'tenant_id',
+ 'fixed_ip_address', 'description',
+ 'floating_ip_address', 'subnet_id'])
+ dns.args2body_dns_create(parsed_args, body, 'domain')
+ dns.args2body_dns_create(parsed_args, body, 'name')
+ return {self.resource: body}
class DeleteFloatingIP(neutronV20.DeleteCommand):
- """Delete a given floating ip."""
+ """Delete a given floating IP."""
- log = logging.getLogger(__name__ + '.DeleteFloatingIP')
resource = 'floatingip'
allow_names = False
class AssociateFloatingIP(neutronV20.NeutronCommand):
- """Create a mapping between a floating ip and a fixed ip."""
+ """Create a mapping between a floating IP and a fixed IP."""
- api = 'network'
- log = logging.getLogger(__name__ + '.AssociateFloatingIP')
resource = 'floatingip'
def get_parser(self, prog_name):
parser = super(AssociateFloatingIP, self).get_parser(prog_name)
parser.add_argument(
'floatingip_id', metavar='FLOATINGIP_ID',
- help=_('ID of the floating IP to associate'))
+ help=_('ID of the floating IP to associate.'))
parser.add_argument(
'port_id', metavar='PORT',
help=_('ID or name of the port to be associated with the '
- 'floatingip'))
+ 'floating IP.'))
parser.add_argument(
'--fixed-ip-address',
- help=_('IP address on the port (only required if port has multiple'
- 'IPs)'))
+ help=_('IP address on the port (only required if port has '
+ 'multiple IPs).'))
parser.add_argument(
'--fixed_ip_address',
help=argparse.SUPPRESS)
return parser
- def run(self, parsed_args):
- self.log.debug('run(%s)' % parsed_args)
+ def take_action(self, parsed_args):
neutron_client = self.get_client()
- neutron_client.format = parsed_args.request_format
update_dict = {}
- if parsed_args.port_id:
- update_dict['port_id'] = parsed_args.port_id
- if parsed_args.fixed_ip_address:
- update_dict['fixed_ip_address'] = parsed_args.fixed_ip_address
+ neutronV20.update_dict(parsed_args, update_dict,
+ ['port_id', 'fixed_ip_address'])
neutron_client.update_floatingip(parsed_args.floatingip_id,
{'floatingip': update_dict})
- print(_('Associated floatingip %s') % parsed_args.floatingip_id,
+ print(_('Associated floating IP %s') % parsed_args.floatingip_id,
file=self.app.stdout)
class DisassociateFloatingIP(neutronV20.NeutronCommand):
- """Remove a mapping from a floating ip to a fixed ip.
- """
+ """Remove a mapping from a floating IP to a fixed IP."""
- api = 'network'
- log = logging.getLogger(__name__ + '.DisassociateFloatingIP')
resource = 'floatingip'
def get_parser(self, prog_name):
parser = super(DisassociateFloatingIP, self).get_parser(prog_name)
parser.add_argument(
'floatingip_id', metavar='FLOATINGIP_ID',
- help=_('ID of the floating IP to associate'))
+ help=_('ID of the floating IP to disassociate.'))
return parser
- def run(self, parsed_args):
- self.log.debug('run(%s)' % parsed_args)
+ def take_action(self, parsed_args):
neutron_client = self.get_client()
- neutron_client.format = parsed_args.request_format
neutron_client.update_floatingip(parsed_args.floatingip_id,
{'floatingip': {'port_id': None}})
- print(_('Disassociated floatingip %s') % parsed_args.floatingip_id,
+ print(_('Disassociated floating IP %s') % parsed_args.floatingip_id,
file=self.app.stdout)
diff --git a/neutronclient/neutron/v2_0/fw/firewall.py b/neutronclient/neutron/v2_0/fw/firewall.py
index 1d693b6f9..c04660a3b 100644
--- a/neutronclient/neutron/v2_0/fw/firewall.py
+++ b/neutronclient/neutron/v2_0/fw/firewall.py
@@ -13,21 +13,57 @@
# License for the specific language governing permissions and limitations
# under the License.
#
-# @author: KC Wang, Big Switch Networks
-#
+from neutronclient._i18n import _
+from neutronclient.common import utils
+from neutronclient.neutron import v2_0 as neutronv20
-import argparse
-import logging
-from neutronclient.neutron import v2_0 as neutronv20
-from neutronclient.openstack.common.gettextutils import _
+def add_common_args(parser):
+ parser.add_argument(
+ '--name',
+ help=_('Name for the firewall.'))
+ parser.add_argument(
+ '--description',
+ help=_('Description for the firewall.'))
+ router = parser.add_mutually_exclusive_group()
+ router.add_argument(
+ '--router',
+ dest='routers',
+ metavar='ROUTER',
+ action='append',
+ help=_('ID or name of the router associated with the firewall '
+ '(requires FWaaS router insertion extension to be enabled). '
+ 'This option can be repeated.'))
+ router.add_argument(
+ '--no-routers',
+ action='store_true',
+ help=_('Associate no routers with the firewall (requires FWaaS '
+ 'router insertion extension).'))
+
+
+def parse_common_args(client, parsed_args):
+ body = {}
+ if parsed_args.policy:
+ body['firewall_policy_id'] = neutronv20.find_resourceid_by_name_or_id(
+ client, 'firewall_policy',
+ parsed_args.policy)
+
+ if parsed_args.routers:
+ body['router_ids'] = [
+ neutronv20.find_resourceid_by_name_or_id(client, 'router', r)
+ for r in parsed_args.routers]
+ elif parsed_args.no_routers:
+ body['router_ids'] = []
+
+ neutronv20.update_dict(parsed_args, body,
+ ['name', 'description'])
+ return body
class ListFirewall(neutronv20.ListCommand):
"""List firewalls that belong to a given tenant."""
resource = 'firewall'
- log = logging.getLogger(__name__ + '.ListFirewall')
list_columns = ['id', 'name', 'firewall_policy_id']
_formatters = {}
pagination_support = True
@@ -38,59 +74,55 @@ class ShowFirewall(neutronv20.ShowCommand):
"""Show information of a given firewall."""
resource = 'firewall'
- log = logging.getLogger(__name__ + '.ShowFirewall')
class CreateFirewall(neutronv20.CreateCommand):
"""Create a firewall."""
resource = 'firewall'
- log = logging.getLogger(__name__ + '.CreateFirewall')
def add_known_arguments(self, parser):
+ add_common_args(parser)
parser.add_argument(
- 'firewall_policy_id', metavar='POLICY',
- help=_('Firewall policy id'))
- parser.add_argument(
- '--name',
- help=_('Name for the firewall'))
- parser.add_argument(
- '--description',
- help=_('Description for the firewall rule'))
- parser.add_argument(
- '--shared',
- action='store_true',
- help=_('Set shared to True (default False)'),
- default=argparse.SUPPRESS)
+ 'policy', metavar='POLICY',
+ help=_('ID or name of the firewall policy '
+ 'associated to this firewall.'))
parser.add_argument(
'--admin-state-down',
dest='admin_state',
action='store_false',
- help=_('Set admin state up to false'))
+ help=_('Set admin state up to false.'))
def args2body(self, parsed_args):
- _policy_id = neutronv20.find_resourceid_by_name_or_id(
- self.get_client(), 'firewall_policy',
- parsed_args.firewall_policy_id)
- body = {
- self.resource: {
- 'firewall_policy_id': _policy_id,
- 'admin_state_up': parsed_args.admin_state, }, }
- neutronv20.update_dict(parsed_args, body[self.resource],
- ['name', 'description', 'shared',
- 'tenant_id'])
- return body
+ body = parse_common_args(self.get_client(), parsed_args)
+ neutronv20.update_dict(parsed_args, body, ['tenant_id'])
+ body['admin_state_up'] = parsed_args.admin_state
+ return {self.resource: body}
class UpdateFirewall(neutronv20.UpdateCommand):
"""Update a given firewall."""
resource = 'firewall'
- log = logging.getLogger(__name__ + '.UpdateFirewall')
+
+ def add_known_arguments(self, parser):
+ add_common_args(parser)
+ parser.add_argument(
+ '--policy', metavar='POLICY',
+ help=_('ID or name of the firewall policy '
+ 'associated to this firewall.'))
+ utils.add_boolean_argument(
+ parser, '--admin-state-up', dest='admin_state_up',
+ help=_('Update the admin state for the firewall '
+ '(True means UP).'))
+
+ def args2body(self, parsed_args):
+ body = parse_common_args(self.get_client(), parsed_args)
+ neutronv20.update_dict(parsed_args, body, ['admin_state_up'])
+ return {self.resource: body}
class DeleteFirewall(neutronv20.DeleteCommand):
"""Delete a given firewall."""
resource = 'firewall'
- log = logging.getLogger(__name__ + '.DeleteFirewall')
diff --git a/neutronclient/neutron/v2_0/fw/firewallpolicy.py b/neutronclient/neutron/v2_0/fw/firewallpolicy.py
index 5e08e6955..3d9331cd6 100644
--- a/neutronclient/neutron/v2_0/fw/firewallpolicy.py
+++ b/neutronclient/neutron/v2_0/fw/firewallpolicy.py
@@ -13,17 +13,12 @@
# License for the specific language governing permissions and limitations
# under the License.
#
-# @author: KC Wang, Big Switch Networks
-#
-
-from __future__ import print_function
import argparse
-import logging
-import string
+from neutronclient._i18n import _
+from neutronclient.common import utils
from neutronclient.neutron import v2_0 as neutronv20
-from neutronclient.openstack.common.gettextutils import _
def _format_firewall_rules(firewall_policy):
@@ -31,15 +26,40 @@ def _format_firewall_rules(firewall_policy):
output = '[' + ',\n '.join([rule for rule in
firewall_policy['firewall_rules']]) + ']'
return output
- except Exception:
+ except (TypeError, KeyError):
return ''
+def add_common_args(parser):
+ parser.add_argument(
+ '--description',
+ help=_('Description for the firewall policy.'))
+ parser.add_argument(
+ '--firewall-rules', type=lambda x: x.split(),
+ help=_('Ordered list of whitespace-delimited firewall rule '
+ 'names or IDs; e.g., --firewall-rules \"rule1 rule2\"'))
+
+
+def parse_common_args(client, parsed_args):
+ if parsed_args.firewall_rules:
+ _firewall_rules = []
+ for f in parsed_args.firewall_rules:
+ _firewall_rules.append(
+ neutronv20.find_resourceid_by_name_or_id(
+ client, 'firewall_rule', f))
+ body = {'firewall_rules': _firewall_rules}
+ else:
+ body = {}
+ neutronv20.update_dict(parsed_args, body,
+ ['name', 'description', 'shared',
+ 'audited', 'tenant_id'])
+ return {'firewall_policy': body}
+
+
class ListFirewallPolicy(neutronv20.ListCommand):
"""List firewall policies that belong to a given tenant."""
resource = 'firewall_policy'
- log = logging.getLogger(__name__ + '.ListFirewallPolicy')
list_columns = ['id', 'name', 'firewall_rules']
_formatters = {'firewall_rules': _format_firewall_rules,
}
@@ -51,77 +71,67 @@ class ShowFirewallPolicy(neutronv20.ShowCommand):
"""Show information of a given firewall policy."""
resource = 'firewall_policy'
- log = logging.getLogger(__name__ + '.ShowFirewallPolicy')
class CreateFirewallPolicy(neutronv20.CreateCommand):
"""Create a firewall policy."""
resource = 'firewall_policy'
- log = logging.getLogger(__name__ + '.CreateFirewallPolicy')
def add_known_arguments(self, parser):
parser.add_argument(
'name',
metavar='NAME',
- help=_('Name for the firewall policy'))
- parser.add_argument(
- '--description',
- help=_('Description for the firewall policy'))
+ help=_('Name for the firewall policy.'))
parser.add_argument(
'--shared',
- dest='shared',
action='store_true',
- help=_('To create a shared policy'),
+ help=_('Create a shared policy.'),
default=argparse.SUPPRESS)
- parser.add_argument(
- '--firewall-rules', type=string.split,
- help=_('Ordered list of whitespace-delimited firewall rule '
- 'names or IDs; e.g., --firewall-rules \"rule1 rule2\"'))
parser.add_argument(
'--audited',
action='store_true',
- help=_('To set audited to True'),
+ help=_('Sets audited to True.'),
default=argparse.SUPPRESS)
+ add_common_args(parser)
def args2body(self, parsed_args):
- if parsed_args.firewall_rules:
- _firewall_rules = []
- for f in parsed_args.firewall_rules:
- _firewall_rules.append(
- neutronv20.find_resourceid_by_name_or_id(
- self.get_client(), 'firewall_rule', f))
- body = {self.resource: {
- 'firewall_rules': _firewall_rules,
- },
- }
- else:
- body = {self.resource: {}}
- neutronv20.update_dict(parsed_args, body[self.resource],
- ['name', 'description', 'shared',
- 'audited', 'tenant_id'])
- return body
+ return parse_common_args(self.get_client(), parsed_args)
class UpdateFirewallPolicy(neutronv20.UpdateCommand):
"""Update a given firewall policy."""
resource = 'firewall_policy'
- log = logging.getLogger(__name__ + '.UpdateFirewallPolicy')
+
+ def add_known_arguments(self, parser):
+ add_common_args(parser)
+ parser.add_argument(
+ '--name',
+ help=_('Name for the firewall policy.'))
+ utils.add_boolean_argument(
+ parser, '--shared',
+ help=_('Update the sharing status of the policy. '
+ '(True means shared).'))
+ utils.add_boolean_argument(
+ parser, '--audited',
+ help=_('Update the audit status of the policy. '
+ '(True means auditing is enabled).'))
+
+ def args2body(self, parsed_args):
+ return parse_common_args(self.get_client(), parsed_args)
class DeleteFirewallPolicy(neutronv20.DeleteCommand):
"""Delete a given firewall policy."""
resource = 'firewall_policy'
- log = logging.getLogger(__name__ + '.DeleteFirewallPolicy')
class FirewallPolicyInsertRule(neutronv20.UpdateCommand):
"""Insert a rule into a given firewall policy."""
resource = 'firewall_policy'
- log = logging.getLogger(__name__ + '.FirewallPolicyInsertRule')
def call_api(self, neutron_client, firewall_policy_id, body):
return neutron_client.firewall_policy_insert_rule(firewall_policy_id,
@@ -148,7 +158,6 @@ def args2body(self, parsed_args):
body = {'firewall_rule_id': _rule,
'insert_before': _insert_before,
'insert_after': _insert_after}
- neutronv20.update_dict(parsed_args, body, [])
return body
def get_parser(self, prog_name):
@@ -156,21 +165,20 @@ def get_parser(self, prog_name):
parser.add_argument(
'--insert-before',
metavar='FIREWALL_RULE',
- help=_('Insert before this rule'))
+ help=_('Insert before this rule.'))
parser.add_argument(
'--insert-after',
metavar='FIREWALL_RULE',
- help=_('Insert after this rule'))
+ help=_('Insert after this rule.'))
parser.add_argument(
'firewall_rule_id',
metavar='FIREWALL_RULE',
- help=_('New rule to insert'))
+ help=_('New rule to insert.'))
self.add_known_arguments(parser)
return parser
- def run(self, parsed_args):
+ def take_action(self, parsed_args):
neutron_client = self.get_client()
- neutron_client.format = parsed_args.request_format
body = self.args2body(parsed_args)
_id = neutronv20.find_resourceid_by_name_or_id(neutron_client,
self.resource,
@@ -184,7 +192,6 @@ class FirewallPolicyRemoveRule(neutronv20.UpdateCommand):
"""Remove a rule from a given firewall policy."""
resource = 'firewall_policy'
- log = logging.getLogger(__name__ + '.FirewallPolicyRemoveRule')
def call_api(self, neutron_client, firewall_policy_id, body):
return neutron_client.firewall_policy_remove_rule(firewall_policy_id,
@@ -197,7 +204,6 @@ def args2body(self, parsed_args):
self.get_client(), 'firewall_rule',
parsed_args.firewall_rule_id)
body = {'firewall_rule_id': _rule}
- neutronv20.update_dict(parsed_args, body, [])
return body
def get_parser(self, prog_name):
@@ -205,13 +211,13 @@ def get_parser(self, prog_name):
parser.add_argument(
'firewall_rule_id',
metavar='FIREWALL_RULE',
- help=_('Firewall rule to remove from policy'))
+ help=_('ID or name of the firewall rule to be removed '
+ 'from the policy.'))
self.add_known_arguments(parser)
return parser
- def run(self, parsed_args):
+ def take_action(self, parsed_args):
neutron_client = self.get_client()
- neutron_client.format = parsed_args.request_format
body = self.args2body(parsed_args)
_id = neutronv20.find_resourceid_by_name_or_id(neutron_client,
self.resource,
diff --git a/neutronclient/neutron/v2_0/fw/firewallrule.py b/neutronclient/neutron/v2_0/fw/firewallrule.py
index 072a074c0..df5db95d6 100644
--- a/neutronclient/neutron/v2_0/fw/firewallrule.py
+++ b/neutronclient/neutron/v2_0/fw/firewallrule.py
@@ -13,21 +13,74 @@
# License for the specific language governing permissions and limitations
# under the License.
#
-# @author: KC Wang, Big Switch Networks
-#
import argparse
-import logging
+from neutronclient._i18n import _
+from neutronclient.common import utils
from neutronclient.neutron import v2_0 as neutronv20
-from neutronclient.openstack.common.gettextutils import _
+
+
+def _add_common_args(parser, is_create=True):
+ """If is_create is True, protocol and action become mandatory arguments.
+
+ CreateCommand = is_create : True
+ UpdateCommand = is_create : False
+ """
+ parser.add_argument(
+ '--name',
+ help=_('Name for the firewall rule.'))
+ parser.add_argument(
+ '--description',
+ help=_('Description for the firewall rule.'))
+ parser.add_argument(
+ '--source-ip-address',
+ help=_('Source IP address or subnet.'))
+ parser.add_argument(
+ '--destination-ip-address',
+ help=_('Destination IP address or subnet.'))
+ parser.add_argument(
+ '--source-port',
+ help=_('Source port (integer in [1, 65535] or range in a:b).'))
+ parser.add_argument(
+ '--destination-port',
+ help=_('Destination port (integer in [1, 65535] or range in '
+ 'a:b).'))
+ utils.add_boolean_argument(
+ parser, '--enabled', dest='enabled',
+ help=_('Whether to enable or disable this rule.'))
+ parser.add_argument(
+ '--protocol', choices=['tcp', 'udp', 'icmp', 'any'],
+ required=is_create,
+ type=utils.convert_to_lowercase,
+ help=_('Protocol for the firewall rule.'))
+ parser.add_argument(
+ '--action',
+ required=is_create,
+ type=utils.convert_to_lowercase,
+ choices=['allow', 'deny', 'reject'],
+ help=_('Action for the firewall rule.'))
+
+
+def common_args2body(parsed_args):
+ body = {}
+ neutronv20.update_dict(parsed_args, body,
+ ['name', 'description', 'shared', 'tenant_id',
+ 'source_ip_address', 'destination_ip_address',
+ 'source_port', 'destination_port', 'action',
+ 'enabled', 'ip_version'])
+ protocol = parsed_args.protocol
+ if protocol:
+ if protocol == 'any':
+ protocol = None
+ body['protocol'] = protocol
+ return body
class ListFirewallRule(neutronv20.ListCommand):
"""List firewall rules that belong to a given tenant."""
resource = 'firewall_rule'
- log = logging.getLogger(__name__ + '.ListFirewallRule')
list_columns = ['id', 'name', 'firewall_policy_id', 'summary', 'enabled']
pagination_support = True
sorting_support = True
@@ -64,98 +117,52 @@ class ShowFirewallRule(neutronv20.ShowCommand):
"""Show information of a given firewall rule."""
resource = 'firewall_rule'
- log = logging.getLogger(__name__ + '.ShowFirewallRule')
class CreateFirewallRule(neutronv20.CreateCommand):
"""Create a firewall rule."""
resource = 'firewall_rule'
- log = logging.getLogger(__name__ + '.CreateFirewallRule')
def add_known_arguments(self, parser):
- parser.add_argument(
- '--name',
- help=_('Name for the firewall rule'))
- parser.add_argument(
- '--description',
- help=_('Description for the firewall rule'))
parser.add_argument(
'--shared',
- dest='shared',
action='store_true',
- help=_('Set shared to True (default False)'),
- default=argparse.SUPPRESS)
- parser.add_argument(
- '--source-ip-address',
- help=_('Source ip address or subnet'))
- parser.add_argument(
- '--destination-ip-address',
- help=_('Destination ip address or subnet'))
- parser.add_argument(
- '--source-port',
- help=_('Source port (integer in [1, 65535] or range in a:b)'))
- parser.add_argument(
- '--destination-port',
- help=_('Destination port (integer in [1, 65535] or range in a:b)'))
- parser.add_argument(
- '--disabled',
- dest='enabled',
- action='store_false',
- help=_('To disable this rule'),
+ help=_('Set shared flag for the firewall rule.'),
default=argparse.SUPPRESS)
+ _add_common_args(parser)
parser.add_argument(
- '--protocol', choices=['tcp', 'udp', 'icmp', 'any'],
- required=True,
- help=_('Protocol for the firewall rule'))
- parser.add_argument(
- '--action',
- required=True,
- choices=['allow', 'deny'],
- help=_('Action for the firewall rule'))
+ '--ip-version',
+ type=int, choices=[4, 6], default=4,
+ help=_('IP version for the firewall rule (default is 4).'))
def args2body(self, parsed_args):
- body = {
- self.resource: {},
- }
- neutronv20.update_dict(parsed_args, body[self.resource],
- ['name', 'description', 'shared', 'protocol',
- 'source_ip_address', 'destination_ip_address',
- 'source_port', 'destination_port',
- 'action', 'enabled', 'tenant_id'])
- protocol = parsed_args.protocol
- if protocol == 'any':
- protocol = None
- body[self.resource]['protocol'] = protocol
- return body
+ return {self.resource: common_args2body(parsed_args)}
class UpdateFirewallRule(neutronv20.UpdateCommand):
"""Update a given firewall rule."""
resource = 'firewall_rule'
- log = logging.getLogger(__name__ + '.UpdateFirewallRule')
def add_known_arguments(self, parser):
+ utils.add_boolean_argument(
+ parser,
+ '--shared',
+ dest='shared',
+ help=_('Update the shared flag for the firewall rule.'),
+ default=argparse.SUPPRESS)
parser.add_argument(
- '--protocol', choices=['tcp', 'udp', 'icmp', 'any'],
- required=False,
- help=_('Protocol for the firewall rule'))
+ '--ip-version',
+ type=int, choices=[4, 6],
+ help=_('Update IP version for the firewall rule.'))
+ _add_common_args(parser, is_create=False)
def args2body(self, parsed_args):
- body = {
- self.resource: {},
- }
- protocol = parsed_args.protocol
- if protocol:
- if protocol == 'any':
- protocol = None
- body[self.resource]['protocol'] = protocol
- return body
+ return {self.resource: common_args2body(parsed_args)}
class DeleteFirewallRule(neutronv20.DeleteCommand):
"""Delete a given firewall rule."""
resource = 'firewall_rule'
- log = logging.getLogger(__name__ + '.DeleteFirewallRule')
diff --git a/neutronclient/neutron/v2_0/lb/healthmonitor.py b/neutronclient/neutron/v2_0/lb/healthmonitor.py
index c3b9b1909..33dfbf0bc 100644
--- a/neutronclient/neutron/v2_0/lb/healthmonitor.py
+++ b/neutronclient/neutron/v2_0/lb/healthmonitor.py
@@ -13,45 +13,37 @@
# License for the specific language governing permissions and limitations
# under the License.
#
-# @author: Ilya Shakhat, Mirantis Inc.
-#
-
-from __future__ import print_function
-
-import logging
+from neutronclient._i18n import _
from neutronclient.neutron import v2_0 as neutronV20
-from neutronclient.openstack.common.gettextutils import _
class ListHealthMonitor(neutronV20.ListCommand):
- """List healthmonitors that belong to a given tenant."""
+ """List health monitors that belong to a given tenant."""
resource = 'health_monitor'
- log = logging.getLogger(__name__ + '.ListHealthMonitor')
list_columns = ['id', 'type', 'admin_state_up']
pagination_support = True
sorting_support = True
class ShowHealthMonitor(neutronV20.ShowCommand):
- """Show information of a given healthmonitor."""
+ """Show information of a given health monitor."""
resource = 'health_monitor'
- log = logging.getLogger(__name__ + '.ShowHealthMonitor')
+ allow_names = False
class CreateHealthMonitor(neutronV20.CreateCommand):
- """Create a healthmonitor."""
+ """Create a health monitor."""
resource = 'health_monitor'
- log = logging.getLogger(__name__ + '.CreateHealthMonitor')
def add_known_arguments(self, parser):
parser.add_argument(
'--admin-state-down',
dest='admin_state', action='store_false',
- help=_('Set admin state up to false'))
+ help=_('Set admin state up to false.'))
parser.add_argument(
'--expected-codes',
help=_('The list of HTTP status codes expected in '
@@ -59,7 +51,7 @@ def add_known_arguments(self, parser):
'attribute can contain one value, '
'or a list of values separated by comma, '
'or a range of values (e.g. "200-299"). If this attribute '
- 'is not specified, it defaults to "200". '))
+ 'is not specified, it defaults to "200".'))
parser.add_argument(
'--http-method',
help=_('The HTTP method used for requests by the monitor of type '
@@ -68,7 +60,7 @@ def add_known_arguments(self, parser):
'--url-path',
help=_('The HTTP path used in the HTTP request used by the monitor'
' to test a member health. This must be a string '
- 'beginning with a / (forward slash)'))
+ 'beginning with a / (forward slash).'))
parser.add_argument(
'--delay',
required=True,
@@ -87,58 +79,51 @@ def add_known_arguments(self, parser):
parser.add_argument(
'--type',
required=True, choices=['PING', 'TCP', 'HTTP', 'HTTPS'],
- help=_('One of predefined health monitor types'))
+ help=_('One of the predefined health monitor types.'))
def args2body(self, parsed_args):
- body = {
- self.resource: {
- 'admin_state_up': parsed_args.admin_state,
+ body = {'admin_state_up': parsed_args.admin_state,
'delay': parsed_args.delay,
'max_retries': parsed_args.max_retries,
'timeout': parsed_args.timeout,
- 'type': parsed_args.type,
- },
- }
- neutronV20.update_dict(parsed_args, body[self.resource],
+ 'type': parsed_args.type}
+ neutronV20.update_dict(parsed_args, body,
['expected_codes', 'http_method', 'url_path',
'tenant_id'])
- return body
+ return {self.resource: body}
class UpdateHealthMonitor(neutronV20.UpdateCommand):
- """Update a given healthmonitor."""
+ """Update a given health monitor."""
resource = 'health_monitor'
- log = logging.getLogger(__name__ + '.UpdateHealthMonitor')
allow_names = False
class DeleteHealthMonitor(neutronV20.DeleteCommand):
- """Delete a given healthmonitor."""
+ """Delete a given health monitor."""
resource = 'health_monitor'
- log = logging.getLogger(__name__ + '.DeleteHealthMonitor')
+ allow_names = False
class AssociateHealthMonitor(neutronV20.NeutronCommand):
"""Create a mapping between a health monitor and a pool."""
- log = logging.getLogger(__name__ + '.AssociateHealthMonitor')
resource = 'health_monitor'
def get_parser(self, prog_name):
parser = super(AssociateHealthMonitor, self).get_parser(prog_name)
parser.add_argument(
'health_monitor_id', metavar='HEALTH_MONITOR_ID',
- help=_('Health monitor to associate'))
+ help=_('Health monitor to associate.'))
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 associated with the health monitor.'))
return parser
- def run(self, parsed_args):
+ def take_action(self, parsed_args):
neutron_client = self.get_client()
- neutron_client.format = parsed_args.request_format
body = {'health_monitor': {'id': parsed_args.health_monitor_id}}
pool_id = neutronV20.find_resourceid_by_name_or_id(
neutron_client, 'pool', parsed_args.pool_id)
@@ -151,22 +136,21 @@ def run(self, parsed_args):
class DisassociateHealthMonitor(neutronV20.NeutronCommand):
"""Remove a mapping from a health monitor to a pool."""
- log = logging.getLogger(__name__ + '.DisassociateHealthMonitor')
resource = 'health_monitor'
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 run(self, parsed_args):
+ def take_action(self, parsed_args):
neutron_client = self.get_client()
- neutron_client.format = parsed_args.request_format
pool_id = neutronV20.find_resourceid_by_name_or_id(
neutron_client, 'pool', parsed_args.pool_id)
neutron_client.disassociate_health_monitor(pool_id,
diff --git a/neutronclient/neutron/v2_0/lb/member.py b/neutronclient/neutron/v2_0/lb/member.py
index d053c8a80..c90840cd1 100644
--- a/neutronclient/neutron/v2_0/lb/member.py
+++ b/neutronclient/neutron/v2_0/lb/member.py
@@ -13,20 +13,15 @@
# License for the specific language governing permissions and limitations
# under the License.
#
-# @author: Ilya Shakhat, Mirantis Inc.
-#
-
-import logging
+from neutronclient._i18n import _
from neutronclient.neutron import v2_0 as neutronV20
-from neutronclient.openstack.common.gettextutils import _
class ListMember(neutronV20.ListCommand):
"""List members that belong to a given tenant."""
resource = 'member'
- log = logging.getLogger(__name__ + '.ListMember')
list_columns = [
'id', 'address', 'protocol_port', 'weight', 'admin_state_up', 'status'
]
@@ -38,62 +33,55 @@ class ShowMember(neutronV20.ShowCommand):
"""Show information of a given member."""
resource = 'member'
- log = logging.getLogger(__name__ + '.ShowMember')
+ allow_names = False
class CreateMember(neutronV20.CreateCommand):
"""Create a member."""
resource = 'member'
- log = logging.getLogger(__name__ + '.CreateMember')
def add_known_arguments(self, parser):
- parser.add_argument(
- 'pool_id', metavar='POOL',
- help=_('Pool id or name this vip belongs to'))
parser.add_argument(
'--admin-state-down',
dest='admin_state', action='store_false',
- help=_('Set admin state up to false'))
+ help=_('Set admin state up to false.'))
parser.add_argument(
'--weight',
- help=_('Weight of pool member in the pool (default:1, [0..256])'))
+ help=_('Weight of pool member in the pool (default:1, [0..256]).'))
parser.add_argument(
'--address',
required=True,
- help=_('IP address of the pool member on the pool network. '))
+ help=_('IP address of the pool member on the pool network.'))
parser.add_argument(
'--protocol-port',
required=True,
help=_('Port on which the pool member listens for requests or '
- 'connections. '))
+ 'connections.'))
+ parser.add_argument(
+ 'pool_id', metavar='POOL',
+ help=_('ID or name of the pool this vip belongs to.'))
def args2body(self, parsed_args):
_pool_id = neutronV20.find_resourceid_by_name_or_id(
self.get_client(), 'pool', parsed_args.pool_id)
- body = {
- self.resource: {
- 'pool_id': _pool_id,
- 'admin_state_up': parsed_args.admin_state,
- },
- }
+ body = {'pool_id': _pool_id,
+ 'admin_state_up': parsed_args.admin_state}
neutronV20.update_dict(
parsed_args,
- body[self.resource],
+ body,
['address', 'protocol_port', 'weight', 'tenant_id']
)
- return body
+ return {self.resource: body}
class UpdateMember(neutronV20.UpdateCommand):
"""Update a given member."""
resource = 'member'
- log = logging.getLogger(__name__ + '.UpdateMember')
class DeleteMember(neutronV20.DeleteCommand):
"""Delete a given member."""
resource = 'member'
- log = logging.getLogger(__name__ + '.DeleteMember')
diff --git a/neutronclient/neutron/v2_0/lb/pool.py b/neutronclient/neutron/v2_0/lb/pool.py
index aa4d9f727..5684e8e6a 100644
--- a/neutronclient/neutron/v2_0/lb/pool.py
+++ b/neutronclient/neutron/v2_0/lb/pool.py
@@ -13,13 +13,9 @@
# License for the specific language governing permissions and limitations
# under the License.
#
-# @author: Ilya Shakhat, Mirantis Inc.
-#
-
-import logging
+from neutronclient._i18n import _
from neutronclient.neutron import v2_0 as neutronV20
-from neutronclient.openstack.common.gettextutils import _
def _format_provider(pool):
@@ -30,7 +26,6 @@ class ListPool(neutronV20.ListCommand):
"""List pools that belong to a given tenant."""
resource = 'pool'
- log = logging.getLogger(__name__ + '.ListPool')
list_columns = ['id', 'name', 'provider', 'lb_method', 'protocol',
'admin_state_up', 'status']
_formatters = {'provider': _format_provider}
@@ -42,86 +37,75 @@ class ShowPool(neutronV20.ShowCommand):
"""Show information of a given pool."""
resource = 'pool'
- log = logging.getLogger(__name__ + '.ShowPool')
class CreatePool(neutronV20.CreateCommand):
"""Create a pool."""
resource = 'pool'
- log = logging.getLogger(__name__ + '.CreatePool')
def add_known_arguments(self, parser):
parser.add_argument(
'--admin-state-down',
dest='admin_state', action='store_false',
- help=_('Set admin state up to false'))
+ help=_('Set admin state up to false.'))
parser.add_argument(
'--description',
- help=_('Description of the pool'))
+ help=_('Description of the pool.'))
parser.add_argument(
'--lb-method',
required=True,
choices=['ROUND_ROBIN', 'LEAST_CONNECTIONS', 'SOURCE_IP'],
help=_('The algorithm used to distribute load between the members '
- 'of the pool'))
+ 'of the pool.'))
parser.add_argument(
'--name',
required=True,
- help=_('The name of the pool'))
+ help=_('The name of the pool.'))
parser.add_argument(
'--protocol',
required=True,
choices=['HTTP', 'HTTPS', 'TCP'],
- help=_('Protocol for balancing'))
+ help=_('Protocol for balancing.'))
parser.add_argument(
'--subnet-id', metavar='SUBNET',
required=True,
help=_('The subnet on which the members of the pool will be '
- 'located'))
+ 'located.'))
parser.add_argument(
'--provider',
- help=_('Provider name of loadbalancer service'))
+ help=_('Provider name of the loadbalancer service.'))
def args2body(self, parsed_args):
_subnet_id = neutronV20.find_resourceid_by_name_or_id(
self.get_client(), 'subnet', parsed_args.subnet_id)
- body = {
- self.resource: {
- 'admin_state_up': parsed_args.admin_state,
- 'subnet_id': _subnet_id,
- },
- }
- neutronV20.update_dict(parsed_args, body[self.resource],
+ body = {'admin_state_up': parsed_args.admin_state,
+ 'subnet_id': _subnet_id}
+ neutronV20.update_dict(parsed_args, body,
['description', 'lb_method', 'name',
'protocol', 'tenant_id', 'provider'])
- return body
+ return {self.resource: body}
class UpdatePool(neutronV20.UpdateCommand):
"""Update a given pool."""
resource = 'pool'
- log = logging.getLogger(__name__ + '.UpdatePool')
class DeletePool(neutronV20.DeleteCommand):
"""Delete a given pool."""
resource = 'pool'
- log = logging.getLogger(__name__ + '.DeletePool')
class RetrievePoolStats(neutronV20.ShowCommand):
"""Retrieve stats for a given pool."""
resource = 'pool'
- log = logging.getLogger(__name__ + '.RetrievePoolStats')
- def get_data(self, parsed_args):
- self.log.debug('run(%s)' % parsed_args)
+ def take_action(self, parsed_args):
neutron_client = self.get_client()
- neutron_client.format = parsed_args.request_format
pool_id = neutronV20.find_resourceid_by_name_or_id(
self.get_client(), 'pool', parsed_args.id)
params = {}
@@ -132,6 +116,6 @@ def get_data(self, parsed_args):
self.format_output_data(data)
stats = data['stats']
if 'stats' in data:
- return zip(*sorted(stats.iteritems()))
+ return zip(*sorted(stats.items()))
else:
return None
diff --git a/neutronclient/tests/unit/fw/__init__.py b/neutronclient/neutron/v2_0/lb/v2/__init__.py
similarity index 100%
rename from neutronclient/tests/unit/fw/__init__.py
rename to neutronclient/neutron/v2_0/lb/v2/__init__.py
diff --git a/neutronclient/neutron/v2_0/lb/v2/healthmonitor.py b/neutronclient/neutron/v2_0/lb/v2/healthmonitor.py
new file mode 100644
index 000000000..34e72c842
--- /dev/null
+++ b/neutronclient/neutron/v2_0/lb/v2/healthmonitor.py
@@ -0,0 +1,143 @@
+# Copyright 2013 Mirantis Inc.
+# 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.
+#
+from neutronclient._i18n import _
+from neutronclient.common import utils
+from neutronclient.neutron import v2_0 as neutronV20
+
+
+def _add_common_args(parser, is_create=True):
+ parser.add_argument(
+ '--delay',
+ required=is_create,
+ help=_('The time in seconds between sending probes to members.'))
+ parser.add_argument(
+ '--name',
+ help=_('Name of the health monitor.'))
+ parser.add_argument(
+ '--timeout',
+ required=is_create,
+ 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(
+ '--http-method',
+ type=utils.convert_to_uppercase,
+ help=_('The HTTP method used for requests by the monitor of type '
+ 'HTTP.'))
+ parser.add_argument(
+ '--url-path',
+ help=_('The HTTP path used in the HTTP request used by the monitor '
+ 'to test a member health. This must be a string '
+ 'beginning with a / (forward slash).'))
+ parser.add_argument(
+ '--max-retries',
+ required=is_create,
+ help=_('Number of permissible connection failures before changing '
+ 'the member status to INACTIVE. [1..10].'))
+ parser.add_argument(
+ '--expected-codes',
+ help=_('The list of HTTP status codes expected in '
+ 'response from the member to declare it healthy. This '
+ 'attribute can contain one value, '
+ 'or a list of values separated by comma, '
+ 'or a range of values (e.g. "200-299"). If this attribute '
+ 'is not specified, it defaults to "200".'))
+
+
+def _parse_common_args(body, parsed_args):
+ neutronV20.update_dict(parsed_args, body,
+ ['expected_codes', 'http_method', 'url_path',
+ 'timeout', 'name', 'delay', 'max_retries'])
+
+
+class ListHealthMonitor(neutronV20.ListCommand):
+ """LBaaS v2 List healthmonitors that belong to a given tenant."""
+
+ resource = 'healthmonitor'
+ shadow_resource = 'lbaas_healthmonitor'
+ list_columns = ['id', 'name', 'type', 'admin_state_up']
+ pagination_support = True
+ sorting_support = True
+
+
+class ShowHealthMonitor(neutronV20.ShowCommand):
+ """LBaaS v2 Show information of a given healthmonitor."""
+
+ resource = 'healthmonitor'
+ shadow_resource = 'lbaas_healthmonitor'
+
+
+class CreateHealthMonitor(neutronV20.CreateCommand):
+ """LBaaS v2 Create a healthmonitor."""
+
+ resource = 'healthmonitor'
+ shadow_resource = 'lbaas_healthmonitor'
+
+ def add_known_arguments(self, parser):
+ _add_common_args(parser)
+ parser.add_argument(
+ '--admin-state-down',
+ dest='admin_state', action='store_false',
+ help=_('Set admin state up to false.'))
+ parser.add_argument(
+ '--type',
+ required=True, choices=['PING', 'TCP', 'HTTP', 'HTTPS'],
+ help=_('One of the predefined health monitor types.'))
+ parser.add_argument(
+ '--pool', required=True,
+ help=_('ID or name of the pool that this healthmonitor will '
+ 'monitor.'))
+
+ def args2body(self, parsed_args):
+ pool_id = neutronV20.find_resourceid_by_name_or_id(
+ self.get_client(), 'pool', parsed_args.pool,
+ cmd_resource='lbaas_pool')
+ body = {'admin_state_up': parsed_args.admin_state,
+ 'type': parsed_args.type,
+ 'pool_id': pool_id}
+ neutronV20.update_dict(parsed_args, body,
+ ['tenant_id'])
+ _parse_common_args(body, parsed_args)
+ return {self.resource: body}
+
+
+class UpdateHealthMonitor(neutronV20.UpdateCommand):
+ """LBaaS v2 Update a given healthmonitor."""
+
+ resource = 'healthmonitor'
+ shadow_resource = 'lbaas_healthmonitor'
+
+ def add_known_arguments(self, parser):
+ _add_common_args(parser, is_create=False)
+ utils.add_boolean_argument(
+ parser, '--admin-state-up',
+ help=_('Update the administrative state of '
+ 'the health monitor (True meaning "Up").'))
+
+ def args2body(self, parsed_args):
+ body = {}
+ _parse_common_args(body, parsed_args)
+ neutronV20.update_dict(parsed_args, body,
+ ['admin_state_up'])
+ return {self.resource: body}
+
+
+class DeleteHealthMonitor(neutronV20.DeleteCommand):
+ """LBaaS v2 Delete a given healthmonitor."""
+
+ resource = 'healthmonitor'
+ shadow_resource = 'lbaas_healthmonitor'
diff --git a/neutronclient/neutron/v2_0/lb/v2/l7policy.py b/neutronclient/neutron/v2_0/lb/v2/l7policy.py
new file mode 100644
index 000000000..26c331554
--- /dev/null
+++ b/neutronclient/neutron/v2_0/lb/v2/l7policy.py
@@ -0,0 +1,155 @@
+# 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.
+#
+
+from neutronclient._i18n import _
+from neutronclient.common import exceptions
+from neutronclient.common import utils
+from neutronclient.neutron import v2_0 as neutronV20
+
+
+def _get_listener_id(client, listener_id_or_name):
+ return neutronV20.find_resourceid_by_name_or_id(
+ client, 'listener', listener_id_or_name)
+
+
+def _get_pool_id(client, pool_id_or_name):
+ return neutronV20.find_resourceid_by_name_or_id(
+ client, 'pool', pool_id_or_name, cmd_resource='lbaas_pool')
+
+
+def _add_common_args(parser, is_create=True):
+ parser.add_argument(
+ '--name',
+ help=_('Name of the policy.'))
+ parser.add_argument(
+ '--description',
+ help=_('Description of the policy.'))
+ parser.add_argument(
+ '--action',
+ required=is_create,
+ metavar='ACTION',
+ type=utils.convert_to_uppercase,
+ choices=['REJECT', 'REDIRECT_TO_POOL', 'REDIRECT_TO_URL'],
+ help=_('Action type of the policy.'))
+ parser.add_argument(
+ '--redirect-pool',
+ help=_('ID or name of the pool for REDIRECT_TO_POOL action type.'))
+ parser.add_argument(
+ '--redirect-url',
+ help=_('URL for REDIRECT_TO_URL action type. '
+ 'This should be a valid URL string.'))
+ parser.add_argument(
+ '--position',
+ type=int,
+ help=_('L7 policy position in ordered policies list. '
+ 'This must be an integer starting from 1. '
+ 'Not specifying the position will place the policy '
+ 'at the tail of existing policies list.'))
+
+
+def _common_args2body(client, parsed_args, is_create=True):
+ if parsed_args.redirect_url:
+ if parsed_args.action != 'REDIRECT_TO_URL':
+ raise exceptions.CommandError(_('Action must be REDIRECT_TO_URL'))
+ if parsed_args.redirect_pool:
+ if parsed_args.action != 'REDIRECT_TO_POOL':
+ raise exceptions.CommandError(_('Action must be REDIRECT_TO_POOL'))
+ parsed_args.redirect_pool_id = _get_pool_id(
+ client, parsed_args.redirect_pool)
+ if (parsed_args.action == 'REDIRECT_TO_URL' and
+ not parsed_args.redirect_url):
+ raise exceptions.CommandError(_('Redirect URL must be specified'))
+ if (parsed_args.action == 'REDIRECT_TO_POOL' and
+ not parsed_args.redirect_pool):
+ raise exceptions.CommandError(_('Redirect pool must be specified'))
+
+ attributes = ['name', 'description',
+ 'action', 'redirect_pool_id', 'redirect_url',
+ 'position', 'admin_state_up']
+ if is_create:
+ parsed_args.listener_id = _get_listener_id(
+ client, parsed_args.listener)
+ attributes.extend(['listener_id', 'tenant_id'])
+ body = {}
+ neutronV20.update_dict(parsed_args, body, attributes)
+ return {'l7policy': body}
+
+
+class ListL7Policy(neutronV20.ListCommand):
+ """LBaaS v2 List L7 policies that belong to a given listener."""
+
+ resource = 'l7policy'
+ shadow_resource = 'lbaas_l7policy'
+ pagination_support = True
+ sorting_support = True
+ list_columns = [
+ 'id', 'name', 'action', 'redirect_pool_id', 'redirect_url',
+ 'position', 'admin_state_up', 'status'
+ ]
+
+
+class ShowL7Policy(neutronV20.ShowCommand):
+ """LBaaS v2 Show information of a given L7 policy."""
+
+ resource = 'l7policy'
+ shadow_resource = 'lbaas_l7policy'
+
+
+class CreateL7Policy(neutronV20.CreateCommand):
+ """LBaaS v2 Create L7 policy."""
+
+ resource = 'l7policy'
+ shadow_resource = 'lbaas_l7policy'
+
+ def add_known_arguments(self, parser):
+ _add_common_args(parser)
+ parser.add_argument(
+ '--admin-state-down',
+ dest='admin_state_up',
+ action='store_false',
+ help=_('Set admin state up to false.'))
+ parser.add_argument(
+ '--listener',
+ required=True,
+ metavar='LISTENER',
+ help=_('ID or name of the listener this policy belongs to.'))
+
+ def args2body(self, parsed_args):
+ return _common_args2body(self.get_client(), parsed_args)
+
+
+class UpdateL7Policy(neutronV20.UpdateCommand):
+ """LBaaS v2 Update a given L7 policy."""
+
+ resource = 'l7policy'
+ shadow_resource = 'lbaas_l7policy'
+
+ def add_known_arguments(self, parser):
+ _add_common_args(parser, is_create=False)
+ utils.add_boolean_argument(
+ parser, '--admin-state-up',
+ help=_('Specify the administrative state of the policy'
+ ' (True meaning "Up").'))
+
+ def args2body(self, parsed_args):
+ return _common_args2body(self.get_client(), parsed_args, False)
+
+
+class DeleteL7Policy(neutronV20.DeleteCommand):
+ """LBaaS v2 Delete a given L7 policy."""
+
+ resource = 'l7policy'
+ shadow_resource = 'lbaas_l7policy'
diff --git a/neutronclient/neutron/v2_0/lb/v2/l7rule.py b/neutronclient/neutron/v2_0/lb/v2/l7rule.py
new file mode 100644
index 000000000..3c468cabf
--- /dev/null
+++ b/neutronclient/neutron/v2_0/lb/v2/l7rule.py
@@ -0,0 +1,148 @@
+# 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.
+#
+
+
+from neutronclient._i18n import _
+from neutronclient.common import utils
+from neutronclient.neutron import v2_0 as neutronV20
+
+
+def _get_policy_id(client, policy_id_or_name):
+ return neutronV20.find_resourceid_by_name_or_id(
+ client, 'l7policy', policy_id_or_name,
+ cmd_resource='lbaas_l7policy')
+
+
+class LbaasL7RuleMixin(object):
+
+ def set_extra_attrs(self, parsed_args):
+ self.parent_id = _get_policy_id(self.get_client(),
+ parsed_args.l7policy)
+
+ def add_known_arguments(self, parser):
+ parser.add_argument(
+ 'l7policy', metavar='L7POLICY',
+ help=_('ID or name of L7 policy this rule belongs to.'))
+
+
+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.'))
+
+
+def _common_args2body(client, parsed_args, is_create=True):
+ attributes = ['type', 'compare_type',
+ 'invert', 'key', 'value', 'admin_state_up']
+ if is_create:
+ attributes.append('tenant_id')
+ body = {}
+ neutronV20.update_dict(parsed_args, body, attributes)
+ return {'rule': body}
+
+
+class ListL7Rule(LbaasL7RuleMixin, neutronV20.ListCommand):
+ """LBaaS v2 List L7 rules that belong to a given L7 policy."""
+
+ resource = 'rule'
+ shadow_resource = 'lbaas_l7rule'
+ pagination_support = True
+ sorting_support = True
+
+ list_columns = [
+ 'id', 'type', 'compare_type', 'invert', 'key', 'value',
+ 'admin_state_up', 'status'
+ ]
+
+ def take_action(self, parsed_args):
+ self.parent_id = _get_policy_id(self.get_client(),
+ parsed_args.l7policy)
+ self.values_specs.append('--l7policy_id=%s' % self.parent_id)
+ return super(ListL7Rule, self).take_action(parsed_args)
+
+
+class ShowL7Rule(LbaasL7RuleMixin, neutronV20.ShowCommand):
+ """LBaaS v2 Show information of a given rule."""
+
+ resource = 'rule'
+ shadow_resource = 'lbaas_l7rule'
+
+
+class CreateL7Rule(LbaasL7RuleMixin, neutronV20.CreateCommand):
+ """LBaaS v2 Create L7 rule."""
+
+ resource = 'rule'
+ shadow_resource = 'lbaas_l7rule'
+
+ def add_known_arguments(self, parser):
+ super(CreateL7Rule, self).add_known_arguments(parser)
+ _add_common_args(parser)
+ parser.add_argument(
+ '--admin-state-down',
+ dest='admin_state_up',
+ action='store_false',
+ help=_('Set admin state up to false'))
+
+ def args2body(self, parsed_args):
+ return _common_args2body(self.get_client(), parsed_args)
+
+
+class UpdateL7Rule(LbaasL7RuleMixin, neutronV20.UpdateCommand):
+ """LBaaS v2 Update a given L7 rule."""
+
+ resource = 'rule'
+ shadow_resource = 'lbaas_l7rule'
+
+ def add_known_arguments(self, parser):
+ super(UpdateL7Rule, self).add_known_arguments(parser)
+ _add_common_args(parser, False)
+ utils.add_boolean_argument(
+ parser, '--admin-state-up',
+ help=_('Specify the administrative state of the rule'
+ ' (True meaning "Up").'))
+
+ def args2body(self, parsed_args):
+ return _common_args2body(self.get_client(), parsed_args, False)
+
+
+class DeleteL7Rule(LbaasL7RuleMixin, neutronV20.DeleteCommand):
+ """LBaaS v2 Delete a given L7 rule."""
+
+ resource = 'rule'
+ shadow_resource = 'lbaas_l7rule'
diff --git a/neutronclient/neutron/v2_0/lb/v2/listener.py b/neutronclient/neutron/v2_0/lb/v2/listener.py
new file mode 100644
index 000000000..481943e6b
--- /dev/null
+++ b/neutronclient/neutron/v2_0/lb/v2/listener.py
@@ -0,0 +1,169 @@
+# Copyright 2014 Blue Box Group, Inc.
+# 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.
+#
+
+from neutronclient._i18n import _
+from neutronclient.common import exceptions
+from neutronclient.common import utils
+from neutronclient.neutron import v2_0 as neutronV20
+
+
+def _get_loadbalancer_id(client, lb_id_or_name):
+ return neutronV20.find_resourceid_by_name_or_id(
+ client, 'loadbalancer', lb_id_or_name,
+ cmd_resource='lbaas_loadbalancer')
+
+
+def _get_pool(client, pool_id_or_name):
+ return neutronV20.find_resource_by_name_or_id(
+ client, 'pool', pool_id_or_name, cmd_resource='lbaas_pool')
+
+
+def _get_pool_id(client, pool_id_or_name):
+ return neutronV20.find_resourceid_by_name_or_id(
+ client, 'pool', pool_id_or_name, cmd_resource='lbaas_pool')
+
+
+def _add_common_args(parser):
+ parser.add_argument(
+ '--description',
+ help=_('Description of the listener.'))
+ parser.add_argument(
+ '--connection-limit',
+ type=int,
+ help=_('The maximum number of connections per second allowed for '
+ 'the listener. Positive integer or -1 '
+ 'for unlimited (default).'))
+ parser.add_argument(
+ '--default-pool',
+ help=_('Default pool for the listener.'))
+
+
+def _parse_common_args(body, parsed_args, client):
+ neutronV20.update_dict(parsed_args, body,
+ ['name', 'description', 'connection_limit'])
+ if parsed_args.default_pool:
+ default_pool_id = _get_pool_id(
+ client, parsed_args.default_pool)
+ body['default_pool_id'] = default_pool_id
+
+
+class ListListener(neutronV20.ListCommand):
+ """LBaaS v2 List listeners that belong to a given tenant."""
+
+ resource = 'listener'
+ list_columns = ['id', 'default_pool_id', 'name', 'protocol',
+ 'protocol_port', 'admin_state_up', 'status']
+ pagination_support = True
+ sorting_support = True
+
+
+class ShowListener(neutronV20.ShowCommand):
+ """LBaaS v2 Show information of a given listener."""
+
+ resource = 'listener'
+
+
+class CreateListener(neutronV20.CreateCommand):
+ """LBaaS v2 Create a listener."""
+
+ resource = 'listener'
+
+ def add_known_arguments(self, parser):
+ _add_common_args(parser)
+ parser.add_argument(
+ '--admin-state-down',
+ dest='admin_state', action='store_false',
+ help=_('Set admin state up to false.'))
+ parser.add_argument(
+ '--name',
+ help=_('The name of the listener. At least one of --default-pool '
+ 'or --loadbalancer must be specified.'))
+ parser.add_argument(
+ '--default-tls-container-ref',
+ dest='default_tls_container_ref',
+ help=_('Default TLS container reference'
+ ' to retrieve TLS information.'))
+ parser.add_argument(
+ '--sni-container-refs',
+ dest='sni_container_refs',
+ nargs='+',
+ help=_('List of TLS container references for SNI.'))
+ parser.add_argument(
+ '--loadbalancer',
+ metavar='LOADBALANCER',
+ help=_('ID or name of the load balancer.'))
+ parser.add_argument(
+ '--protocol',
+ required=True,
+ choices=['TCP', 'HTTP', 'HTTPS', 'TERMINATED_HTTPS'],
+ type=utils.convert_to_uppercase,
+ help=_('Protocol for the listener.'))
+ parser.add_argument(
+ '--protocol-port',
+ dest='protocol_port', required=True,
+ metavar='PORT',
+ help=_('Protocol port for the listener.'))
+
+ def args2body(self, parsed_args):
+ if not parsed_args.loadbalancer and not parsed_args.default_pool:
+ message = _('Either --default-pool or --loadbalancer must be '
+ 'specified.')
+ raise exceptions.CommandError(message)
+ body = {
+ 'protocol': parsed_args.protocol,
+ 'protocol_port': parsed_args.protocol_port,
+ 'admin_state_up': parsed_args.admin_state
+ }
+ if parsed_args.loadbalancer:
+ loadbalancer_id = _get_loadbalancer_id(
+ self.get_client(), parsed_args.loadbalancer)
+ body['loadbalancer_id'] = loadbalancer_id
+
+ neutronV20.update_dict(parsed_args, body,
+ ['default_tls_container_ref',
+ 'sni_container_refs', 'tenant_id'])
+ _parse_common_args(body, parsed_args, self.get_client())
+ return {self.resource: body}
+
+
+class UpdateListener(neutronV20.UpdateCommand):
+ """LBaaS v2 Update a given listener."""
+
+ resource = 'listener'
+
+ def add_known_arguments(self, parser):
+ _add_common_args(parser)
+ parser.add_argument(
+ '--name',
+ help=_('Name of the listener.'))
+ utils.add_boolean_argument(
+ parser, '--admin-state-up', dest='admin_state_up',
+ help=_('Specify the administrative state of the listener. '
+ '(True meaning "Up")'))
+
+ def args2body(self, parsed_args):
+ body = {}
+ neutronV20.update_dict(parsed_args, body,
+ ['admin_state_up'])
+ _parse_common_args(body, parsed_args, self.get_client())
+ return {self.resource: body}
+
+
+class DeleteListener(neutronV20.DeleteCommand):
+ """LBaaS v2 Delete a given listener."""
+
+ resource = 'listener'
diff --git a/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py b/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py
new file mode 100644
index 000000000..c40809fad
--- /dev/null
+++ b/neutronclient/neutron/v2_0/lb/v2/loadbalancer.py
@@ -0,0 +1,178 @@
+# Copyright 2014 Blue Box Group, Inc.
+# 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.
+#
+from oslo_serialization import jsonutils
+
+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(
+ '--description',
+ help=_('Description of the load balancer.'))
+ parser.add_argument(
+ '--name', metavar='NAME',
+ help=_('Name of the load balancer.'))
+
+
+def _parse_common_args(body, parsed_args):
+ neutronV20.update_dict(parsed_args, body,
+ ['name', 'description'])
+
+
+class ListLoadBalancer(neutronV20.ListCommand):
+ """LBaaS v2 List loadbalancers that belong to a given tenant."""
+
+ resource = 'loadbalancer'
+ list_columns = ['id', 'name', 'vip_address',
+ 'provisioning_status', 'provider']
+ pagination_support = True
+ sorting_support = True
+
+
+class ShowLoadBalancer(neutronV20.ShowCommand):
+ """LBaaS v2 Show information of a given loadbalancer."""
+
+ resource = 'loadbalancer'
+
+
+class CreateLoadBalancer(neutronV20.CreateCommand):
+ """LBaaS v2 Create a loadbalancer."""
+
+ resource = 'loadbalancer'
+
+ def add_known_arguments(self, parser):
+ _add_common_args(parser)
+ parser.add_argument(
+ '--admin-state-down',
+ dest='admin_state', action='store_false',
+ help=_('Set admin state up to false.'))
+ parser.add_argument(
+ '--provider',
+ help=_('Provider name of the load balancer service.'))
+ parser.add_argument(
+ '--flavor',
+ help=_('ID or name of the flavor.'))
+ parser.add_argument(
+ '--vip-address',
+ help=_('VIP address for the load balancer.'))
+ parser.add_argument(
+ 'vip_subnet', metavar='VIP_SUBNET',
+ help=_('Load balancer VIP subnet.'))
+
+ def args2body(self, parsed_args):
+ _subnet_id = neutronV20.find_resourceid_by_name_or_id(
+ self.get_client(), 'subnet', parsed_args.vip_subnet)
+ body = {'vip_subnet_id': _subnet_id,
+ '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,
+ ['provider', 'vip_address', 'tenant_id'])
+ _parse_common_args(body, parsed_args)
+ return {self.resource: body}
+
+
+class UpdateLoadBalancer(neutronV20.UpdateCommand):
+ """LBaaS v2 Update a given loadbalancer."""
+
+ resource = 'loadbalancer'
+
+ def add_known_arguments(self, parser):
+ utils.add_boolean_argument(
+ parser, '--admin-state-up',
+ help=_('Update the administrative state of '
+ 'the load balancer (True meaning "Up").'))
+ _add_common_args(parser)
+
+ def args2body(self, parsed_args):
+ body = {}
+ _parse_common_args(body, parsed_args)
+ neutronV20.update_dict(parsed_args, body,
+ ['admin_state_up'])
+ return {self.resource: body}
+
+
+class DeleteLoadBalancer(neutronV20.DeleteCommand):
+ """LBaaS v2 Delete a given loadbalancer."""
+
+ resource = 'loadbalancer'
+
+
+class RetrieveLoadBalancerStats(neutronV20.ShowCommand):
+ """Retrieve stats for a given loadbalancer."""
+
+ resource = 'loadbalancer'
+
+ def take_action(self, parsed_args):
+ neutron_client = self.get_client()
+ loadbalancer_id = neutronV20.find_resourceid_by_name_or_id(
+ self.get_client(), 'loadbalancer', parsed_args.id)
+ params = {}
+ if parsed_args.fields:
+ params = {'fields': parsed_args.fields}
+
+ data = neutron_client.retrieve_loadbalancer_stats(loadbalancer_id,
+ **params)
+ self.format_output_data(data)
+ stats = data['stats']
+ if 'stats' in data:
+ # To render the output table like:
+ # +--------------------+-------+
+ # | Field | Value |
+ # +--------------------+-------+
+ # | field1 | value1|
+ # | field2 | value2|
+ # | field3 | value3|
+ # | ... | ... |
+ # +--------------------+-------+
+ # it has two columns and the Filed column is alphabetical,
+ # here convert the data dict to the 1-1 vector format below:
+ # [(field1, field2, field3, ...), (value1, value2, value3, ...)]
+ return list(zip(*sorted(stats.items())))
+
+
+class RetrieveLoadBalancerStatus(neutronV20.NeutronCommand):
+ """Retrieve status for a given loadbalancer.
+
+ The only output is a formatted JSON tree, and the table format
+ does not support this type of data.
+ """
+ resource = 'loadbalancer'
+
+ def get_parser(self, prog_name):
+ parser = super(RetrieveLoadBalancerStatus, self).get_parser(prog_name)
+ parser.add_argument(
+ self.resource, metavar=self.resource.upper(),
+ help=_('ID or name of %s to show.') % self.resource)
+
+ return parser
+
+ def take_action(self, parsed_args):
+ self.log.debug('run(%s)', parsed_args)
+ neutron_client = self.get_client()
+ lb_id = neutronV20.find_resourceid_by_name_or_id(
+ neutron_client, self.resource, parsed_args.loadbalancer)
+ params = {}
+ data = neutron_client.retrieve_loadbalancer_status(lb_id, **params)
+ res = data['statuses']
+ if 'statuses' in data:
+ print(jsonutils.dumps(res, indent=4))
diff --git a/neutronclient/neutron/v2_0/lb/v2/member.py b/neutronclient/neutron/v2_0/lb/v2/member.py
new file mode 100644
index 000000000..ee3c090a9
--- /dev/null
+++ b/neutronclient/neutron/v2_0/lb/v2/member.py
@@ -0,0 +1,150 @@
+# Copyright 2013 Mirantis Inc.
+# Copyright 2014 Blue Box Group, Inc.
+# 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.
+#
+from neutronclient._i18n import _
+from neutronclient.common import utils
+from neutronclient.neutron import v2_0 as neutronV20
+
+
+def _get_pool_id(client, pool_id_or_name):
+ return neutronV20.find_resourceid_by_name_or_id(client, 'pool',
+ pool_id_or_name,
+ cmd_resource='lbaas_pool')
+
+
+class LbaasMemberMixin(object):
+
+ def set_extra_attrs(self, parsed_args):
+ self.parent_id = _get_pool_id(self.get_client(), parsed_args.pool)
+
+ def add_known_arguments(self, parser):
+ parser.add_argument(
+ 'pool', metavar='POOL',
+ help=_('ID or name of the pool that this member belongs to.'))
+
+
+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]).'))
+
+
+def _parse_common_args(body, parsed_args):
+ neutronV20.update_dict(parsed_args, body, ['weight', 'name'])
+
+
+class ListMember(LbaasMemberMixin, neutronV20.ListCommand):
+ """LBaaS v2 List members that belong to a given pool."""
+
+ resource = 'member'
+ shadow_resource = 'lbaas_member'
+ list_columns = [
+ 'id', 'name', 'address', 'protocol_port', 'weight',
+ 'subnet_id', 'admin_state_up', 'status'
+ ]
+ pagination_support = True
+ sorting_support = True
+
+ def take_action(self, parsed_args):
+ self.parent_id = _get_pool_id(self.get_client(), parsed_args.pool)
+ self.values_specs.append('--pool_id=%s' % self.parent_id)
+ return super(ListMember, self).take_action(parsed_args)
+
+
+class ShowMember(LbaasMemberMixin, neutronV20.ShowCommand):
+ """LBaaS v2 Show information of a given member."""
+
+ resource = 'member'
+ shadow_resource = 'lbaas_member'
+
+
+class CreateMember(neutronV20.CreateCommand):
+ """LBaaS v2 Create a member."""
+
+ resource = 'member'
+ shadow_resource = 'lbaas_member'
+
+ def add_known_arguments(self, parser):
+ _add_common_args(parser)
+ parser.add_argument(
+ '--admin-state-down',
+ dest='admin_state', action='store_false',
+ help=_('Set admin state up to false.'))
+ parser.add_argument(
+ '--subnet',
+ required=True,
+ help=_('Subnet ID or name for the member.'))
+ parser.add_argument(
+ '--address',
+ required=True,
+ help=_('IP address of the pool member in the pool.'))
+ parser.add_argument(
+ '--protocol-port',
+ required=True,
+ help=_('Port on which the pool member listens for requests or '
+ 'connections.'))
+ parser.add_argument(
+ 'pool', metavar='POOL',
+ help=_('ID or name of the pool that this member belongs to.'))
+
+ def args2body(self, parsed_args):
+ self.parent_id = _get_pool_id(self.get_client(), parsed_args.pool)
+ _subnet_id = neutronV20.find_resourceid_by_name_or_id(
+ self.get_client(), 'subnet', parsed_args.subnet)
+ body = {'subnet_id': _subnet_id,
+ 'admin_state_up': parsed_args.admin_state,
+ 'protocol_port': parsed_args.protocol_port,
+ 'address': parsed_args.address}
+ neutronV20.update_dict(parsed_args, body,
+ ['subnet_id', 'tenant_id'])
+ _parse_common_args(body, parsed_args)
+ return {self.resource: body}
+
+
+class UpdateMember(neutronV20.UpdateCommand):
+ """LBaaS v2 Update a given member."""
+
+ resource = 'member'
+ shadow_resource = 'lbaas_member'
+
+ def add_known_arguments(self, parser):
+ 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',
+ help=_('Update the administrative state of '
+ 'the member (True meaning "Up").'))
+ _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_up"):
+ body['admin_state_up'] = parsed_args.admin_state_up
+ _parse_common_args(body, parsed_args)
+ return {self.resource: body}
+
+
+class DeleteMember(LbaasMemberMixin, neutronV20.DeleteCommand):
+ """LBaaS v2 Delete a given member."""
+
+ resource = 'member'
+ shadow_resource = 'lbaas_member'
diff --git a/neutronclient/neutron/v2_0/lb/v2/pool.py b/neutronclient/neutron/v2_0/lb/v2/pool.py
new file mode 100644
index 000000000..0af383970
--- /dev/null
+++ b/neutronclient/neutron/v2_0/lb/v2/pool.py
@@ -0,0 +1,189 @@
+# Copyright 2013 Mirantis Inc.
+# Copyright 2014 Blue Box Group, Inc.
+# Copyright 2015 Hewlett-Packard Development Company, L.P.
+# Copyright 2015 Blue Box, an IBM Company
+# 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 exceptions
+from neutronclient.common import utils
+from neutronclient.neutron import v2_0 as neutronV20
+
+
+def _get_loadbalancer_id(client, lb_id_or_name):
+ return neutronV20.find_resourceid_by_name_or_id(
+ client, 'loadbalancer', lb_id_or_name,
+ cmd_resource='lbaas_loadbalancer')
+
+
+def _get_listener(client, listener_id_or_name):
+ return neutronV20.find_resource_by_name_or_id(
+ client, 'listener', listener_id_or_name)
+
+
+def _get_listener_id(client, listener_id_or_name):
+ return neutronV20.find_resourceid_by_name_or_id(
+ client, 'listener', listener_id_or_name)
+
+
+def _add_common_args(parser, is_create=True):
+ parser.add_argument(
+ '--description',
+ help=_('Description of the pool.'))
+ parser.add_argument(
+ '--name', help=_('The name of the pool.'))
+ parser.add_argument(
+ '--lb-algorithm',
+ 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 '
+ 'of the pool.'))
+
+
+def _parse_common_args(parsed_args):
+ body = {}
+ neutronV20.update_dict(parsed_args,
+ body, ['description', 'lb_algorithm', 'name'])
+ return body
+
+
+class ListPool(neutronV20.ListCommand):
+ """LBaaS v2 List pools that belong to a given tenant."""
+
+ resource = 'pool'
+ shadow_resource = 'lbaas_pool'
+ list_columns = ['id', 'name', 'lb_algorithm', 'protocol',
+ 'admin_state_up']
+ pagination_support = True
+ sorting_support = True
+
+
+class ShowPool(neutronV20.ShowCommand):
+ """LBaaS v2 Show information of a given pool."""
+
+ resource = 'pool'
+ shadow_resource = 'lbaas_pool'
+
+ def cleanup_output_data(self, data):
+ if 'members' not in data['pool']:
+ return []
+ member_info = []
+ for member in data['pool']['members']:
+ member_info.append(member['id'])
+ data['pool']['members'] = member_info
+
+
+class CreatePool(neutronV20.CreateCommand):
+ """LBaaS v2 Create a pool."""
+
+ resource = 'pool'
+ shadow_resource = 'lbaas_pool'
+
+ def add_known_arguments(self, parser):
+ _add_common_args(parser)
+ parser.add_argument(
+ '--admin-state-down',
+ dest='admin_state', action='store_false',
+ help=_('Set admin state up to false.'))
+ parser.add_argument(
+ '--listener',
+ help=_('Listener whose default-pool should be set to this pool. '
+ 'At least one of --listener or --loadbalancer must be '
+ 'specified.'))
+ parser.add_argument(
+ '--loadbalancer',
+ help=_('Loadbalancer with which this pool should be associated. '
+ 'At least one of --listener or --loadbalancer must be '
+ 'specified.'))
+ parser.add_argument(
+ '--protocol',
+ type=utils.convert_to_uppercase,
+ required=True,
+ choices=['HTTP', 'HTTPS', 'TCP'],
+ help=_('Protocol for balancing.'))
+ parser.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.'))
+
+ def args2body(self, parsed_args):
+ if not parsed_args.listener and not parsed_args.loadbalancer:
+ message = _('At least one of --listener or --loadbalancer must be '
+ 'specified.')
+ raise exceptions.CommandError(message)
+ body = _parse_common_args(parsed_args)
+ if parsed_args.listener:
+ listener_id = _get_listener_id(
+ self.get_client(),
+ parsed_args.listener)
+ body['listener_id'] = listener_id
+ if parsed_args.loadbalancer:
+ loadbalancer_id = _get_loadbalancer_id(
+ self.get_client(),
+ parsed_args.loadbalancer)
+ body['loadbalancer_id'] = loadbalancer_id
+ body['admin_state_up'] = parsed_args.admin_state
+ neutronV20.update_dict(parsed_args, body,
+ ['tenant_id', 'protocol',
+ 'session_persistence'])
+ return {self.resource: body}
+
+
+class UpdatePool(neutronV20.UpdateCommand):
+ """LBaaS v2 Update a given pool."""
+
+ resource = 'pool'
+ shadow_resource = 'lbaas_pool'
+
+ def add_known_arguments(self, parser):
+ utils.add_boolean_argument(
+ parser, '--admin-state-up',
+ help=_('Update the administrative state of '
+ 'the pool (True meaning "Up").'))
+ 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.'))
+ 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}
+
+
+class DeletePool(neutronV20.DeleteCommand):
+ """LBaaS v2 Delete a given pool."""
+
+ resource = 'pool'
+ shadow_resource = 'lbaas_pool'
diff --git a/neutronclient/neutron/v2_0/lb/vip.py b/neutronclient/neutron/v2_0/lb/vip.py
index cf5a57c8c..e3ac8ee9d 100644
--- a/neutronclient/neutron/v2_0/lb/vip.py
+++ b/neutronclient/neutron/v2_0/lb/vip.py
@@ -13,20 +13,15 @@
# License for the specific language governing permissions and limitations
# under the License.
#
-# @author: Ilya Shakhat, Mirantis Inc.
-#
-
-import logging
+from neutronclient._i18n import _
from neutronclient.neutron import v2_0 as neutronV20
-from neutronclient.openstack.common.gettextutils import _
class ListVip(neutronV20.ListCommand):
"""List vips that belong to a given tenant."""
resource = 'vip'
- log = logging.getLogger(__name__ + '.ListVip')
list_columns = ['id', 'name', 'algorithm', 'address', 'protocol',
'admin_state_up', 'status']
pagination_support = True
@@ -37,50 +32,49 @@ class ShowVip(neutronV20.ShowCommand):
"""Show information of a given vip."""
resource = 'vip'
- log = logging.getLogger(__name__ + '.ShowVip')
class CreateVip(neutronV20.CreateCommand):
"""Create a vip."""
resource = 'vip'
- log = logging.getLogger(__name__ + '.CreateVip')
def add_known_arguments(self, parser):
parser.add_argument(
'pool_id', metavar='POOL',
- help=_('Pool id or name this vip belongs to'))
+ help=_('ID or name of the pool to which this vip belongs.'))
parser.add_argument(
'--address',
- help=_('IP address of the vip'))
+ help=_('IP address of the vip.'))
parser.add_argument(
'--admin-state-down',
dest='admin_state', action='store_false',
- help=_('Set admin state up to false'))
+ help=_('Set admin state up to false.'))
parser.add_argument(
'--connection-limit',
help=_('The maximum number of connections per second allowed for '
- 'the vip. Positive integer or -1 for unlimited (default)'))
+ 'the vip. Valid values: a positive integer or -1 '
+ 'for unlimited (default).'))
parser.add_argument(
'--description',
- help=_('Description of the vip'))
+ help=_('Description of the vip to be created.'))
parser.add_argument(
'--name',
required=True,
- help=_('Name of the vip'))
+ help=_('Name of the vip to be created.'))
parser.add_argument(
'--protocol-port',
required=True,
help=_('TCP port on which to listen for client traffic that is '
- 'associated with the vip address'))
+ 'associated with the vip address.'))
parser.add_argument(
'--protocol',
required=True, choices=['TCP', 'HTTP', 'HTTPS'],
- help=_('Protocol for balancing'))
+ help=_('Protocol for balancing.'))
parser.add_argument(
'--subnet-id', metavar='SUBNET',
required=True,
- help=_('The subnet on which to allocate the vip address'))
+ help=_('The subnet on which to allocate the vip address.'))
def args2body(self, parsed_args):
_pool_id = neutronV20.find_resourceid_by_name_or_id(
@@ -88,29 +82,23 @@ def args2body(self, parsed_args):
_subnet_id = neutronV20.find_resourceid_by_name_or_id(
self.get_client(), 'subnet', parsed_args.subnet_id)
- body = {
- self.resource: {
- 'pool_id': _pool_id,
+ body = {'pool_id': _pool_id,
'admin_state_up': parsed_args.admin_state,
- 'subnet_id': _subnet_id,
- },
- }
- neutronV20.update_dict(parsed_args, body[self.resource],
+ 'subnet_id': _subnet_id}
+ neutronV20.update_dict(parsed_args, body,
['address', 'connection_limit', 'description',
'name', 'protocol_port', 'protocol',
'tenant_id'])
- return body
+ return {self.resource: body}
class UpdateVip(neutronV20.UpdateCommand):
"""Update a given vip."""
resource = 'vip'
- log = logging.getLogger(__name__ + '.UpdateVip')
class DeleteVip(neutronV20.DeleteCommand):
"""Delete a given vip."""
resource = 'vip'
- log = logging.getLogger(__name__ + '.DeleteVip')
diff --git a/neutronclient/neutron/v2_0/metering.py b/neutronclient/neutron/v2_0/metering.py
index 66fa29f5e..8f6d2174e 100644
--- a/neutronclient/neutron/v2_0/metering.py
+++ b/neutronclient/neutron/v2_0/metering.py
@@ -1,7 +1,5 @@
# Copyright (C) 2013 eNovance SAS
#
-# Author: Sylvain Afchain
-#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
@@ -14,17 +12,15 @@
# License for the specific language governing permissions and limitations
# under the License.
-import logging
-
+from neutronclient._i18n import _
+from neutronclient.common import utils
from neutronclient.neutron import v2_0 as neutronv20
-from neutronclient.openstack.common.gettextutils import _
class ListMeteringLabel(neutronv20.ListCommand):
"""List metering labels that belong to a given tenant."""
resource = 'metering_label'
- log = logging.getLogger(__name__ + '.ListMeteringLabel')
list_columns = ['id', 'name', 'description', 'shared']
pagination_support = True
sorting_support = True
@@ -34,7 +30,6 @@ class ShowMeteringLabel(neutronv20.ShowCommand):
"""Show information of a given metering label."""
resource = 'metering_label'
- log = logging.getLogger(__name__ + '.ShowMeteringLabel')
allow_names = True
@@ -42,39 +37,29 @@ class CreateMeteringLabel(neutronv20.CreateCommand):
"""Create a metering label for a given tenant."""
resource = 'metering_label'
- log = logging.getLogger(__name__ + '.CreateMeteringLabel')
def add_known_arguments(self, parser):
parser.add_argument(
'name', metavar='NAME',
- help=_('Name of metering label to create'))
+ help=_('Name of the metering label to be created.'))
parser.add_argument(
'--description',
- help=_('Description of metering label to create'))
+ help=_('Description of the metering label to be created.'))
parser.add_argument(
'--shared',
action='store_true',
- help=_('Set the label as shared'))
+ help=_('Set the label as shared.'))
def args2body(self, parsed_args):
- body = {'metering_label': {
- 'name': parsed_args.name}, }
-
- if parsed_args.tenant_id:
- body['metering_label'].update({'tenant_id': parsed_args.tenant_id})
- if parsed_args.description:
- body['metering_label'].update(
- {'description': parsed_args.description})
- if parsed_args.shared:
- body['metering_label'].update(
- {'shared': True})
- return body
+ body = {'name': parsed_args.name}
+ neutronv20.update_dict(parsed_args, body,
+ ['tenant_id', 'description', 'shared'])
+ return {'metering_label': body}
class DeleteMeteringLabel(neutronv20.DeleteCommand):
"""Delete a given metering label."""
- log = logging.getLogger(__name__ + '.DeleteMeteringLabel')
resource = 'metering_label'
allow_names = True
@@ -83,7 +68,6 @@ class ListMeteringLabelRule(neutronv20.ListCommand):
"""List metering labels that belong to a given label."""
resource = 'metering_label_rule'
- log = logging.getLogger(__name__ + '.ListMeteringLabelRule')
list_columns = ['id', 'excluded', 'direction', 'remote_ip_prefix']
pagination_support = True
sorting_support = True
@@ -93,53 +77,45 @@ class ShowMeteringLabelRule(neutronv20.ShowCommand):
"""Show information of a given metering label rule."""
resource = 'metering_label_rule'
- log = logging.getLogger(__name__ + '.ShowMeteringLabelRule')
+ allow_names = False
class CreateMeteringLabelRule(neutronv20.CreateCommand):
"""Create a metering label rule for a given label."""
resource = 'metering_label_rule'
- log = logging.getLogger(__name__ + '.CreateMeteringLabelRule')
def add_known_arguments(self, parser):
parser.add_argument(
'label_id', metavar='LABEL',
- help=_('Id or Name of the label'))
+ help=_('ID or name of the label.'))
parser.add_argument(
'remote_ip_prefix', metavar='REMOTE_IP_PREFIX',
- help=_('CIDR to match on'))
+ help=_('CIDR to match on.'))
parser.add_argument(
'--direction',
default='ingress', choices=['ingress', 'egress'],
- help=_('Direction of traffic, default:ingress'))
+ type=utils.convert_to_lowercase,
+ help=_('Direction of traffic, default: ingress.'))
parser.add_argument(
'--excluded',
action='store_true',
- help=_('Exclude this cidr from the label, default:not excluded'))
+ help=_('Exclude this CIDR from the label, default: not excluded.'))
def args2body(self, parsed_args):
neutron_client = self.get_client()
- neutron_client.format = parsed_args.request_format
label_id = neutronv20.find_resourceid_by_name_or_id(
neutron_client, 'metering_label', parsed_args.label_id)
- body = {'metering_label_rule': {
- 'metering_label_id': label_id,
- 'remote_ip_prefix': parsed_args.remote_ip_prefix
- }}
-
- if parsed_args.direction:
- body['metering_label_rule'].update(
- {'direction': parsed_args.direction})
- if parsed_args.excluded:
- body['metering_label_rule'].update(
- {'excluded': True})
- return body
+ body = {'metering_label_id': label_id,
+ 'remote_ip_prefix': parsed_args.remote_ip_prefix}
+ neutronv20.update_dict(parsed_args, body,
+ ['direction', 'excluded'])
+ return {'metering_label_rule': body}
class DeleteMeteringLabelRule(neutronv20.DeleteCommand):
"""Delete a given metering label."""
- log = logging.getLogger(__name__ + '.DeleteMeteringLabelRule')
resource = 'metering_label_rule'
+ allow_names = False
diff --git a/neutronclient/neutron/v2_0/netpartition.py b/neutronclient/neutron/v2_0/netpartition.py
deleted file mode 100644
index b8310584b..000000000
--- a/neutronclient/neutron/v2_0/netpartition.py
+++ /dev/null
@@ -1,59 +0,0 @@
-# Copyright 2014 Alcatel-Lucent USA Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-#
-# @author: Ronak Shah, Nuage Networks, Alcatel-Lucent USA Inc.
-
-import logging
-
-from neutronclient.neutron.v2_0 import CreateCommand
-from neutronclient.neutron.v2_0 import DeleteCommand
-from neutronclient.neutron.v2_0 import ListCommand
-from neutronclient.neutron.v2_0 import ShowCommand
-
-
-class ListNetPartition(ListCommand):
- """List netpartitions that belong to a given tenant."""
- resource = 'net_partition'
- log = logging.getLogger(__name__ + '.ListNetPartition')
- list_columns = ['id', 'name']
-
-
-class ShowNetPartition(ShowCommand):
- """Show information of a given netpartition."""
-
- resource = 'net_partition'
- log = logging.getLogger(__name__ + '.ShowNetPartition')
-
-
-class CreateNetPartition(CreateCommand):
- """Create a netpartition for a given tenant."""
-
- resource = 'net_partition'
- log = logging.getLogger(__name__ + '.CreateNetPartition')
-
- def add_known_arguments(self, parser):
- parser.add_argument(
- 'name', metavar='name',
- help='Name of NetPartition to create')
-
- def args2body(self, parsed_args):
- body = {'net_partition': {'name': parsed_args.name}, }
- return body
-
-
-class DeleteNetPartition(DeleteCommand):
- """Delete a given netpartition."""
-
- resource = 'net_partition'
- log = logging.getLogger(__name__ + '.DeleteNetPartition')
diff --git a/neutronclient/neutron/v2_0/network.py b/neutronclient/neutron/v2_0/network.py
index c7c4c168d..6c68b623a 100644
--- a/neutronclient/neutron/v2_0/network.py
+++ b/neutronclient/neutron/v2_0/network.py
@@ -15,34 +15,82 @@
#
import argparse
-import logging
+from neutronclient._i18n import _
from neutronclient.common import exceptions
+from neutronclient.common import utils
from neutronclient.neutron import v2_0 as neutronV20
-from neutronclient.openstack.common.gettextutils import _
+from neutronclient.neutron.v2_0 import availability_zone
+from neutronclient.neutron.v2_0 import dns
+from neutronclient.neutron.v2_0.qos import policy as qos_policy
def _format_subnets(network):
try:
return '\n'.join([' '.join([s['id'], s.get('cidr', '')])
for s in network['subnets']])
- except Exception:
+ except (TypeError, KeyError):
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'
- log = logging.getLogger(__name__ + '.ListNetwork')
_formatters = {'subnets': _format_subnets, }
list_columns = ['id', 'name', 'subnets']
pagination_support = True
sorting_support = True
+ filter_attrs = [
+ 'tenant_id',
+ 'name',
+ 'admin_state_up',
+ {'name': 'status',
+ 'help': _("Filter %s according to their operation status."
+ "(For example: ACTIVE, ERROR etc)"),
+ 'boolean': False,
+ 'argparse_kwargs': {'type': utils.convert_to_uppercase}},
+ {'name': 'shared',
+ 'help': _('Filter and list the networks which are shared.'),
+ 'boolean': True},
+ {'name': 'router:external',
+ 'help': _('Filter and list the networks which are external.'),
+ 'boolean': True},
+ {'name': 'tags',
+ 'help': _("Filter and list %s which has all given tags. "
+ "Multiple tags can be set like --tags "),
+ 'boolean': False,
+ 'argparse_kwargs': {'metavar': 'TAG'}},
+ {'name': 'tags_any',
+ 'help': _("Filter and list %s which has any given tags. "
+ "Multiple tags can be set like --tags-any "),
+ 'boolean': False,
+ 'argparse_kwargs': {'metavar': 'TAG'}},
+ {'name': 'not_tags',
+ 'help': _("Filter and list %s which does not have all given tags. "
+ "Multiple tags can be set like --not-tags "),
+ 'boolean': False,
+ 'argparse_kwargs': {'metavar': 'TAG'}},
+ {'name': 'not_tags_any',
+ 'help': _("Filter and list %s which does not have any given tags. "
+ "Multiple tags can be set like --not-tags-any "
+ ""),
+ 'boolean': False,
+ 'argparse_kwargs': {'metavar': 'TAG'}},
+ ]
+
def extend_list(self, data, parsed_args):
"""Add subnet information to a network list."""
neutron_client = self.get_client()
@@ -70,7 +118,9 @@ def _get_subnet_list(sub_ids):
subnet_count = len(subnet_ids)
max_size = ((self.subnet_id_filter_len * subnet_count) -
uri_len_exc.excess)
- chunk_size = max_size / self.subnet_id_filter_len
+ 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):
subnets.extend(
@@ -86,7 +136,6 @@ def _get_subnet_list(sub_ids):
class ListExternalNetwork(ListNetwork):
"""List external networks that belong to a given tenant."""
- log = logging.getLogger(__name__ + '.ListExternalNetwork')
pagination_support = True
sorting_support = True
@@ -101,20 +150,18 @@ class ShowNetwork(neutronV20.ShowCommand):
"""Show information of a given network."""
resource = 'network'
- log = logging.getLogger(__name__ + '.ShowNetwork')
-class CreateNetwork(neutronV20.CreateCommand):
+class CreateNetwork(neutronV20.CreateCommand, qos_policy.CreateQosPolicyMixin):
"""Create a network for a given tenant."""
resource = 'network'
- log = logging.getLogger(__name__ + '.CreateNetwork')
def add_known_arguments(self, parser):
parser.add_argument(
'--admin-state-down',
dest='admin_state', action='store_false',
- help=_('Set Admin State Up to false'))
+ help=_('Set admin state up to false.'))
parser.add_argument(
'--admin_state_down',
dest='admin_state', action='store_false',
@@ -122,30 +169,79 @@ def add_known_arguments(self, parser):
parser.add_argument(
'--shared',
action='store_true',
- help=_('Set the network as shared'),
+ help=_('Set the network as shared.'),
default=argparse.SUPPRESS)
+ parser.add_argument(
+ '--provider:network_type',
+ metavar='',
+ help=_('The physical mechanism by which the virtual network'
+ ' is implemented.'))
+ parser.add_argument(
+ '--provider:physical_network',
+ metavar='',
+ help=_('Name of the physical network over which the virtual '
+ 'network is implemented.'))
+ parser.add_argument(
+ '--provider:segmentation_id',
+ metavar='',
+ help=_('VLAN ID for VLAN networks or tunnel-id for GRE/VXLAN '
+ 'networks.'))
+ utils.add_boolean_argument(
+ parser,
+ '--vlan-transparent',
+ default=argparse.SUPPRESS,
+ help=_('Create a VLAN transparent network.'))
parser.add_argument(
'name', metavar='NAME',
- help=_('Name of network to create'))
+ help=_('Name of the network to be created.'))
+ parser.add_argument(
+ '--description',
+ help=_('Description of network.'))
+
+ self.add_arguments_qos_policy(parser)
+ availability_zone.add_az_hint_argument(parser, self.resource)
+ dns.add_dns_argument_create(parser, self.resource, 'domain')
def args2body(self, parsed_args):
- body = {'network': {
- 'name': parsed_args.name,
- 'admin_state_up': parsed_args.admin_state}, }
- neutronV20.update_dict(parsed_args, body['network'],
- ['shared', 'tenant_id'])
- return body
+ body = {'admin_state_up': parsed_args.admin_state}
+ args2body_common(body, parsed_args)
+ neutronV20.update_dict(parsed_args, body,
+ ['shared', 'tenant_id',
+ 'vlan_transparent',
+ 'provider:network_type',
+ '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}
class DeleteNetwork(neutronV20.DeleteCommand):
"""Delete a given network."""
- log = logging.getLogger(__name__ + '.DeleteNetwork')
resource = 'network'
-class UpdateNetwork(neutronV20.UpdateCommand):
+class UpdateNetwork(neutronV20.UpdateCommand, qos_policy.UpdateQosPolicyMixin):
"""Update network's information."""
- log = logging.getLogger(__name__ + '.UpdateNetwork')
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
new file mode 100644
index 000000000..ba7af8f80
--- /dev/null
+++ b/neutronclient/neutron/v2_0/network_ip_availability.py
@@ -0,0 +1,72 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# 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 cliff import show
+
+from neutronclient._i18n import _
+from neutronclient.neutron import v2_0 as neutronV20
+
+
+class ListIpAvailability(neutronV20.ListCommand):
+ """List IP usage of networks"""
+
+ resource = 'network_ip_availability'
+ resource_plural = 'network_ip_availabilities'
+ list_columns = ['network_id', 'network_name', 'total_ips', 'used_ips']
+ paginations_support = True
+ sorting_support = True
+
+ filter_attrs = [
+ {'name': 'ip_version',
+ 'help': _('Returns IP availability for the network subnets '
+ 'with a given IP version. Default: 4'),
+ 'argparse_kwargs': {'type': int,
+ 'choices': [4, 6],
+ 'default': 4}
+ },
+ {'name': 'network_id',
+ 'help': _('Returns IP availability for the network '
+ 'matching a given network ID.')},
+ {'name': 'network_name',
+ 'help': _('Returns IP availability for the network '
+ 'matching a given name.')},
+ {'name': 'tenant_id',
+ 'help': _('Returns IP availability for the networks '
+ 'with a given tenant ID.')},
+ ]
+
+
+class ShowIpAvailability(neutronV20.NeutronCommand, show.ShowOne):
+ """Show IP usage of specific network"""
+
+ resource = 'network_ip_availability'
+
+ def get_parser(self, prog_name):
+ parser = super(ShowIpAvailability, self).get_parser(prog_name)
+ parser.add_argument(
+ 'network_id', metavar='NETWORK',
+ help=_('ID or name of network to look up.'))
+ return parser
+
+ def take_action(self, parsed_args):
+ self.log.debug('run(%s)', parsed_args)
+ neutron_client = self.get_client()
+ _id = neutronV20.find_resourceid_by_name_or_id(
+ neutron_client, 'network', parsed_args.network_id)
+ data = neutron_client.show_network_ip_availability(_id)
+ self.format_output_data(data)
+ resource = data[self.resource]
+ if self.resource in data:
+ return zip(*sorted(resource.items()))
+ else:
+ return None
diff --git a/neutronclient/neutron/v2_0/networkprofile.py b/neutronclient/neutron/v2_0/networkprofile.py
deleted file mode 100644
index 1fcf3be4a..000000000
--- a/neutronclient/neutron/v2_0/networkprofile.py
+++ /dev/null
@@ -1,136 +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.
-#
-#@author Abhishek Raut, Cisco Systems
-#@author Sergey Sudakovich, Cisco Systems
-#@author Rudrajit Tapadar, Cisco Systems
-
-from __future__ import print_function
-
-import logging
-
-from neutronclient.neutron import v2_0 as neutronV20
-from neutronclient.neutron.v2_0 import parse_args_to_dict
-from neutronclient.openstack.common.gettextutils import _
-
-RESOURCE = 'network_profile'
-SEGMENT_TYPE_CHOICES = ['vlan', 'overlay', 'multi-segment', 'trunk']
-
-
-class ListNetworkProfile(neutronV20.ListCommand):
- """List network profiles that belong to a given tenant."""
-
- resource = RESOURCE
- log = logging.getLogger(__name__ + '.ListNetworkProfile')
- _formatters = {}
- list_columns = ['id', 'name', 'segment_type', 'sub_type', 'segment_range',
- 'physical_network', 'multicast_ip_index',
- 'multicast_ip_range']
-
-
-class ShowNetworkProfile(neutronV20.ShowCommand):
- """Show information of a given network profile."""
-
- resource = RESOURCE
- log = logging.getLogger(__name__ + '.ShowNetworkProfile')
- allow_names = True
-
-
-class CreateNetworkProfile(neutronV20.CreateCommand):
- """Creates a network profile."""
-
- resource = RESOURCE
- log = logging.getLogger(__name__ + '.CreateNetworkProfile')
-
- def add_known_arguments(self, parser):
- parser.add_argument('name',
- help=_('Name for Network Profile'))
- parser.add_argument('segment_type',
- choices=SEGMENT_TYPE_CHOICES,
- help='Segment type')
- # TODO(Abhishek): Check on sub-type choices depending on segment_type
- parser.add_argument('--sub_type',
- help=_('Sub-type for the segment. Available sub-'
- 'types for overlay segments: native, enhanced; '
- 'For trunk segments: vlan, overlay.'))
- parser.add_argument('--segment_range',
- help=_('Range for the Segment'))
- parser.add_argument('--physical_network',
- help=_('Name for the Physical Network'))
- parser.add_argument('--multicast_ip_range',
- help=_('Multicast IPv4 Range'))
- parser.add_argument("--add-tenant",
- help=_("Add tenant to the network profile"))
-
- def args2body(self, parsed_args):
- body = {'network_profile': {'name': parsed_args.name}}
- if parsed_args.segment_type:
- body['network_profile'].update({'segment_type':
- parsed_args.segment_type})
- if parsed_args.sub_type:
- body['network_profile'].update({'sub_type':
- parsed_args.sub_type})
- if parsed_args.segment_range:
- body['network_profile'].update({'segment_range':
- parsed_args.segment_range})
- if parsed_args.physical_network:
- body['network_profile'].update({'physical_network':
- parsed_args.physical_network})
- if parsed_args.multicast_ip_range:
- body['network_profile'].update({'multicast_ip_range':
- parsed_args.multicast_ip_range})
- if parsed_args.add_tenant:
- body['network_profile'].update({'add_tenant':
- parsed_args.add_tenant})
- return body
-
-
-class DeleteNetworkProfile(neutronV20.DeleteCommand):
- """Delete a given network profile."""
-
- log = logging.getLogger(__name__ + '.DeleteNetworkProfile')
- resource = RESOURCE
- allow_names = True
-
-
-class UpdateNetworkProfile(neutronV20.UpdateCommand):
- """Update network profile's information."""
-
- resource = RESOURCE
- log = logging.getLogger(__name__ + '.UpdateNetworkProfile')
-
-
-class UpdateNetworkProfileV2(neutronV20.NeutronCommand):
-
- api = 'network'
- log = logging.getLogger(__name__ + '.UpdateNetworkProfileV2')
- resource = RESOURCE
-
- def get_parser(self, prog_name):
- parser = super(UpdateNetworkProfileV2, self).get_parser(prog_name)
- parser.add_argument("--remove-tenant",
- help="Remove tenant from the network profile")
- return parser
-
- def run(self, parsed_args):
- self.log.debug('run(%s)' % parsed_args)
- neutron_client = self.get_client()
- neutron_client.format = parsed_args.request_format
- data = {self.resource: parse_args_to_dict(parsed_args)}
- if parsed_args.remove_tenant:
- data[self.resource]['remove_tenant'] = parsed_args.remove_tenant
- neutron_client.update_network_profile(parsed_args.id,
- {self.resource: data})
- print((_('Updated %(resource)s: %(id)s') %
- {'id': parsed_args.id, 'resource': self.resource}),
- file=self.app.stdout)
- return
diff --git a/neutronclient/neutron/v2_0/nsx/networkgateway.py b/neutronclient/neutron/v2_0/nsx/networkgateway.py
deleted file mode 100644
index 6947d4cca..000000000
--- a/neutronclient/neutron/v2_0/nsx/networkgateway.py
+++ /dev/null
@@ -1,283 +0,0 @@
-# Copyright 2013 OpenStack Foundation.
-# All Rights Reserved
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-#
-
-from __future__ import print_function
-
-import logging
-
-from neutronclient.common import utils
-from neutronclient.neutron import v2_0 as neutronV20
-from neutronclient.openstack.common.gettextutils import _
-
-GW_RESOURCE = 'network_gateway'
-DEV_RESOURCE = 'gateway_device'
-CONNECTOR_TYPE_HELP = _("Type of the transport zone connector to use for this "
- "device. Valid values are gre, stt, ipsecgre, "
- "ipsecstt, and bridge. Defaults to stt")
-CONNECTOR_IP_HELP = _("IP address for this device's transport connector. "
- "It must correspond to the IP address of the interface "
- "used for tenant traffic on the NSX gateway node")
-CLIENT_CERT_HELP = _("PEM certificate used by the NSX gateway transport node "
- "to authenticate with the NSX controller")
-CLIENT_CERT_FILE_HELP = _("File containing the PEM certificate used by the "
- "NSX gateway transport node to authenticate with "
- "the NSX controller")
-
-
-class ListGatewayDevice(neutronV20.ListCommand):
- """List network gateway devices for a given tenant."""
-
- resource = DEV_RESOURCE
- log = logging.getLogger(__name__ + '.ListGatewayDevice')
- list_columns = ['id', 'name']
-
-
-class ShowGatewayDevice(neutronV20.ShowCommand):
- """Show information for a given network gateway device."""
-
- resource = DEV_RESOURCE
- log = logging.getLogger(__name__ + '.ShowGatewayDevice')
-
-
-def read_cert_file(cert_file):
- return open(cert_file, 'rb').read()
-
-
-def gateway_device_args2body(parsed_args):
- body = {}
- if parsed_args.name:
- body['name'] = parsed_args.name
- if parsed_args.connector_type:
- body['connector_type'] = parsed_args.connector_type
- if parsed_args.connector_ip:
- body['connector_ip'] = parsed_args.connector_ip
- cert_data = None
- if parsed_args.cert_file:
- cert_data = read_cert_file(parsed_args.cert_file)
- elif parsed_args.cert_data:
- cert_data = parsed_args.cert_data
- if cert_data:
- body['client_certificate'] = cert_data
- if getattr(parsed_args, 'tenant_id', None):
- body['tenant_id'] = parsed_args.tenant_id
- return {DEV_RESOURCE: body}
-
-
-class CreateGatewayDevice(neutronV20.CreateCommand):
- """Create a network gateway device."""
-
- resource = DEV_RESOURCE
- log = logging.getLogger(__name__ + '.CreateGatewayDevice')
-
- def add_known_arguments(self, parser):
- parser.add_argument(
- 'name', metavar='NAME',
- help='Name of network gateway device to create')
- parser.add_argument(
- '--connector-type',
- default='stt',
- choices=['stt', 'gre', 'ipsecgre', 'ipsecstt', 'bridge'],
- help=CONNECTOR_TYPE_HELP)
- parser.add_argument(
- '--connector-ip',
- required=True,
- help=CONNECTOR_IP_HELP)
- client_cert_group = parser.add_mutually_exclusive_group(
- required=True)
- client_cert_group.add_argument(
- '--client-certificate',
- dest='cert_data',
- help=CLIENT_CERT_HELP)
- client_cert_group.add_argument(
- '--client-certificate-file',
- dest='cert_file',
- help=CLIENT_CERT_FILE_HELP)
-
- def args2body(self, parsed_args):
- return gateway_device_args2body(parsed_args)
-
-
-class UpdateGatewayDevice(neutronV20.UpdateCommand):
- """Update a network gateway device."""
-
- resource = DEV_RESOURCE
- log = logging.getLogger(__name__ + '.UpdateGatewayDevice')
-
- def add_known_arguments(self, parser):
- parser.add_argument(
- '--name', metavar='NAME',
- help='New name for network gateway device')
- parser.add_argument(
- '--connector-type',
- required=False,
- choices=['stt', 'gre', 'ipsecgre', 'ipsecstt', 'bridge'],
- help=CONNECTOR_TYPE_HELP)
- parser.add_argument(
- '--connector-ip',
- required=False,
- help=CONNECTOR_IP_HELP)
- client_cert_group = parser.add_mutually_exclusive_group()
- client_cert_group.add_argument(
- '--client-certificate',
- dest='cert_data',
- help=CLIENT_CERT_HELP)
- client_cert_group.add_argument(
- '--client-certificate-file',
- dest='cert_file',
- help=CLIENT_CERT_FILE_HELP)
-
- def args2body(self, parsed_args):
- return gateway_device_args2body(parsed_args)
-
-
-class DeleteGatewayDevice(neutronV20.DeleteCommand):
- """Delete a given network gateway device."""
-
- resource = DEV_RESOURCE
- log = logging.getLogger(__name__ + '.DeleteGatewayDevice')
-
-
-class ListNetworkGateway(neutronV20.ListCommand):
- """List network gateways for a given tenant."""
-
- resource = GW_RESOURCE
- log = logging.getLogger(__name__ + '.ListNetworkGateway')
- list_columns = ['id', 'name']
-
-
-class ShowNetworkGateway(neutronV20.ShowCommand):
- """Show information of a given network gateway."""
-
- resource = GW_RESOURCE
- log = logging.getLogger(__name__ + '.ShowNetworkGateway')
-
-
-class CreateNetworkGateway(neutronV20.CreateCommand):
- """Create a network gateway."""
-
- resource = GW_RESOURCE
- log = logging.getLogger(__name__ + '.CreateNetworkGateway')
-
- def add_known_arguments(self, parser):
- parser.add_argument(
- 'name', metavar='NAME',
- help=_('Name of network gateway to create'))
- parser.add_argument(
- '--device', metavar='id=ID,interface_name=NAME_OR_ID',
- action='append',
- help=_('Device info for this gateway, '
- 'can be repeated for multiple devices for HA gateways'))
-
- def args2body(self, parsed_args):
- body = {self.resource: {
- 'name': parsed_args.name}}
- devices = []
- if parsed_args.device:
- for device in parsed_args.device:
- devices.append(utils.str2dict(device))
- if devices:
- body[self.resource].update({'devices': devices})
- if parsed_args.tenant_id:
- body[self.resource].update({'tenant_id': parsed_args.tenant_id})
- return body
-
-
-class DeleteNetworkGateway(neutronV20.DeleteCommand):
- """Delete a given network gateway."""
-
- resource = GW_RESOURCE
- log = logging.getLogger(__name__ + '.DeleteNetworkGateway')
-
-
-class UpdateNetworkGateway(neutronV20.UpdateCommand):
- """Update the name for a network gateway."""
-
- resource = GW_RESOURCE
- log = logging.getLogger(__name__ + '.UpdateNetworkGateway')
-
-
-class NetworkGatewayInterfaceCommand(neutronV20.NeutronCommand):
- """Base class for connecting/disconnecting networks to/from a gateway."""
-
- resource = GW_RESOURCE
-
- def get_parser(self, prog_name):
- parser = super(NetworkGatewayInterfaceCommand,
- self).get_parser(prog_name)
- parser.add_argument(
- 'net_gateway_id', metavar='NET-GATEWAY-ID',
- help=_('ID of the network gateway'))
- parser.add_argument(
- 'network_id', metavar='NETWORK-ID',
- help=_('ID of the internal network to connect on the gateway'))
- parser.add_argument(
- '--segmentation-type',
- help=_('L2 segmentation strategy on the external side of '
- 'the gateway (e.g.: VLAN, FLAT)'))
- parser.add_argument(
- '--segmentation-id',
- help=_('Identifier for the L2 segment on the external side '
- 'of the gateway'))
- return parser
-
- def retrieve_ids(self, client, args):
- gateway_id = neutronV20.find_resourceid_by_name_or_id(
- client, self.resource, args.net_gateway_id)
- network_id = neutronV20.find_resourceid_by_name_or_id(
- client, 'network', args.network_id)
- return (gateway_id, network_id)
-
-
-class ConnectNetworkGateway(NetworkGatewayInterfaceCommand):
- """Add an internal network interface to a router."""
-
- log = logging.getLogger(__name__ + '.ConnectNetworkGateway')
-
- def run(self, parsed_args):
- self.log.debug('run(%s)' % parsed_args)
- neutron_client = self.get_client()
- neutron_client.format = parsed_args.request_format
- (gateway_id, network_id) = self.retrieve_ids(neutron_client,
- parsed_args)
- neutron_client.connect_network_gateway(
- gateway_id, {'network_id': network_id,
- 'segmentation_type': parsed_args.segmentation_type,
- 'segmentation_id': parsed_args.segmentation_id})
- # TODO(Salvatore-Orlando): Do output formatting as
- # any other command
- print(_('Connected network to gateway %s') % gateway_id,
- file=self.app.stdout)
-
-
-class DisconnectNetworkGateway(NetworkGatewayInterfaceCommand):
- """Remove a network from a network gateway."""
-
- log = logging.getLogger(__name__ + '.DisconnectNetworkGateway')
-
- def run(self, parsed_args):
- self.log.debug('run(%s)' % parsed_args)
- neutron_client = self.get_client()
- neutron_client.format = parsed_args.request_format
- (gateway_id, network_id) = self.retrieve_ids(neutron_client,
- parsed_args)
- neutron_client.disconnect_network_gateway(
- gateway_id, {'network_id': network_id,
- 'segmentation_type': parsed_args.segmentation_type,
- 'segmentation_id': parsed_args.segmentation_id})
- # TODO(Salvatore-Orlando): Do output formatting as
- # any other command
- print(_('Disconnected network from gateway %s') % gateway_id,
- file=self.app.stdout)
diff --git a/neutronclient/neutron/v2_0/nsx/qos_queue.py b/neutronclient/neutron/v2_0/nsx/qos_queue.py
deleted file mode 100644
index e3eff3006..000000000
--- a/neutronclient/neutron/v2_0/nsx/qos_queue.py
+++ /dev/null
@@ -1,88 +0,0 @@
-# Copyright 2013 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 logging
-
-from neutronclient.neutron import v2_0 as neutronV20
-from neutronclient.openstack.common.gettextutils import _
-
-
-class ListQoSQueue(neutronV20.ListCommand):
- """List queues that belong to a given tenant."""
-
- resource = 'qos_queue'
- log = logging.getLogger(__name__ + '.ListQoSQueue')
- list_columns = ['id', 'name', 'min', 'max',
- 'qos_marking', 'dscp', 'default']
-
-
-class ShowQoSQueue(neutronV20.ShowCommand):
- """Show information of a given queue."""
-
- resource = 'qos_queue'
- log = logging.getLogger(__name__ + '.ShowQoSQueue')
- allow_names = True
-
-
-class CreateQoSQueue(neutronV20.CreateCommand):
- """Create a queue."""
-
- resource = 'qos_queue'
- log = logging.getLogger(__name__ + '.CreateQoSQueue')
-
- def add_known_arguments(self, parser):
- parser.add_argument(
- 'name', metavar='NAME',
- help=_('Name of queue'))
- parser.add_argument(
- '--min',
- help=_('min-rate')),
- parser.add_argument(
- '--max',
- help=_('max-rate')),
- parser.add_argument(
- '--qos-marking',
- help=_('QOS marking untrusted/trusted')),
- parser.add_argument(
- '--default',
- default=False,
- help=_('If true all ports created with be the size of this queue'
- ' if queue is not specified')),
- parser.add_argument(
- '--dscp',
- help=_('Differentiated Services Code Point')),
-
- def args2body(self, parsed_args):
- params = {'name': parsed_args.name,
- 'default': parsed_args.default}
- if parsed_args.min:
- params['min'] = parsed_args.min
- if parsed_args.max:
- params['max'] = parsed_args.max
- if parsed_args.qos_marking:
- params['qos_marking'] = parsed_args.qos_marking
- if parsed_args.dscp:
- params['dscp'] = parsed_args.dscp
- if parsed_args.tenant_id:
- params['tenant_id'] = parsed_args.tenant_id
- return {'qos_queue': params}
-
-
-class DeleteQoSQueue(neutronV20.DeleteCommand):
- """Delete a given queue."""
-
- log = logging.getLogger(__name__ + '.DeleteQoSQueue')
- resource = 'qos_queue'
- allow_names = True
diff --git a/neutronclient/neutron/v2_0/policyprofile.py b/neutronclient/neutron/v2_0/policyprofile.py
deleted file mode 100644
index af38c1f6e..000000000
--- a/neutronclient/neutron/v2_0/policyprofile.py
+++ /dev/null
@@ -1,80 +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.
-#
-#@author Abhishek Raut, Cisco Systems
-#@author Sergey Sudakovich, Cisco Systems
-
-from __future__ import print_function
-
-import logging
-
-from neutronclient.neutron import v2_0 as neutronV20
-from neutronclient.neutron.v2_0 import parse_args_to_dict
-from neutronclient.openstack.common.gettextutils import _
-
-RESOURCE = 'policy_profile'
-
-
-class ListPolicyProfile(neutronV20.ListCommand):
- """List policy profiles that belong to a given tenant."""
-
- resource = RESOURCE
- log = logging.getLogger(__name__ + '.ListProfile')
- _formatters = {}
- list_columns = ['id', 'name']
-
-
-class ShowPolicyProfile(neutronV20.ShowCommand):
- """Show information of a given policy profile."""
-
- resource = RESOURCE
- log = logging.getLogger(__name__ + '.ShowProfile')
- allow_names = True
-
-
-class UpdatePolicyProfile(neutronV20.UpdateCommand):
- """Update policy profile's information."""
-
- resource = RESOURCE
- log = logging.getLogger(__name__ + '.UpdatePolicyProfile')
-
-
-class UpdatePolicyProfileV2(neutronV20.UpdateCommand):
- """Update policy profile's information."""
-
- api = 'network'
- log = logging.getLogger(__name__ + '.UpdatePolicyProfileV2')
- resource = RESOURCE
-
- def get_parser(self, prog_name):
- parser = super(UpdatePolicyProfileV2, self).get_parser(prog_name)
- parser.add_argument("--add-tenant",
- help=_("Add tenant to the policy profile"))
- parser.add_argument("--remove-tenant",
- help=_("Remove tenant from the policy profile"))
- return parser
-
- def run(self, parsed_args):
- self.log.debug('run(%s)' % parsed_args)
- neutron_client = self.get_client()
- neutron_client.format = parsed_args.request_format
- data = {self.resource: parse_args_to_dict(parsed_args)}
- if parsed_args.add_tenant:
- data[self.resource]['add_tenant'] = parsed_args.add_tenant
- if parsed_args.remove_tenant:
- data[self.resource]['remove_tenant'] = parsed_args.remove_tenant
- neutron_client.update_policy_profile(parsed_args.id,
- {self.resource: data})
- print((_('Updated %(resource)s: %(id)s') %
- {'id': parsed_args.id, 'resource': self.resource}),
- file=self.app.stdout)
- return
diff --git a/neutronclient/neutron/v2_0/port.py b/neutronclient/neutron/v2_0/port.py
index c7036af00..3fe73544b 100644
--- a/neutronclient/neutron/v2_0/port.py
+++ b/neutronclient/neutron/v2_0/port.py
@@ -15,26 +15,77 @@
#
import argparse
-import logging
+from oslo_serialization import jsonutils
+
+from neutronclient._i18n import _
from neutronclient.common import exceptions
from neutronclient.common import utils
from neutronclient.neutron import v2_0 as neutronV20
-from neutronclient.openstack.common.gettextutils import _
+from neutronclient.neutron.v2_0 import dns
+from neutronclient.neutron.v2_0.qos import policy as qos_policy
def _format_fixed_ips(port):
try:
- return '\n'.join([utils.dumps(ip) for ip in port['fixed_ips']])
- except Exception:
+ return '\n'.join([jsonutils.dumps(ip) for ip in port['fixed_ips']])
+ except (TypeError, KeyError):
return ''
+def _add_updatable_args(parser):
+ parser.add_argument(
+ '--name',
+ help=_('Name of this port.'))
+ parser.add_argument(
+ '--description',
+ help=_('Description of this port.'))
+ parser.add_argument(
+ '--fixed-ip', metavar='subnet_id=SUBNET,ip_address=IP_ADDR',
+ action='append',
+ type=utils.str2dict_type(optional_keys=['subnet_id', 'ip_address']),
+ help=_('Desired IP and/or subnet for this port: '
+ 'subnet_id=,ip_address=. '
+ 'You can repeat this option.'))
+ parser.add_argument(
+ '--fixed_ip',
+ action='append',
+ help=argparse.SUPPRESS)
+ parser.add_argument(
+ '--device-id',
+ help=_('Device ID of this port.'))
+ parser.add_argument(
+ '--device_id',
+ help=argparse.SUPPRESS)
+ parser.add_argument(
+ '--device-owner',
+ help=_('Device owner of this port.'))
+ parser.add_argument(
+ '--device_owner',
+ help=argparse.SUPPRESS)
+
+
+def _updatable_args2body(parsed_args, body, client):
+ neutronV20.update_dict(parsed_args, body,
+ ['device_id', 'device_owner', 'name',
+ 'description'])
+ ips = []
+ if parsed_args.fixed_ip:
+ for ip_spec in parsed_args.fixed_ip:
+ if 'subnet_id' in ip_spec:
+ subnet_name_id = ip_spec['subnet_id']
+ _subnet_id = neutronV20.find_resourceid_by_name_or_id(
+ client, 'subnet', subnet_name_id)
+ ip_spec['subnet_id'] = _subnet_id
+ ips.append(ip_spec)
+ if ips:
+ body['fixed_ips'] = ips
+
+
class ListPort(neutronV20.ListCommand):
"""List ports that belong to a given tenant."""
resource = 'port'
- log = logging.getLogger(__name__ + '.ListPort')
_formatters = {'fixed_ips': _format_fixed_ips, }
list_columns = ['id', 'name', 'mac_address', 'fixed_ips']
pagination_support = True
@@ -45,7 +96,6 @@ class ListRouterPort(neutronV20.ListCommand):
"""List ports that belong to a given tenant, with specified router."""
resource = 'port'
- log = logging.getLogger(__name__ + '.ListRouterPort')
_formatters = {'fixed_ips': _format_fixed_ips, }
list_columns = ['id', 'name', 'mac_address', 'fixed_ips']
pagination_support = True
@@ -54,24 +104,22 @@ class ListRouterPort(neutronV20.ListCommand):
def get_parser(self, prog_name):
parser = super(ListRouterPort, self).get_parser(prog_name)
parser.add_argument(
- 'id', metavar='router',
- help=_('ID or name of router to look up'))
+ 'id', metavar='ROUTER',
+ help=_('ID or name of the router to look up.'))
return parser
- def get_data(self, parsed_args):
+ def take_action(self, parsed_args):
neutron_client = self.get_client()
- neutron_client.format = parsed_args.request_format
_id = neutronV20.find_resourceid_by_name_or_id(
neutron_client, 'router', parsed_args.id)
self.values_specs.append('--device_id=%s' % _id)
- return super(ListRouterPort, self).get_data(parsed_args)
+ return super(ListRouterPort, self).take_action(parsed_args)
class ShowPort(neutronV20.ShowCommand):
"""Show information of a given port."""
resource = 'port'
- log = logging.getLogger(__name__ + '.ShowPort')
class UpdatePortSecGroupMixin(object):
@@ -80,12 +128,12 @@ def add_arguments_secgroup(self, parser):
group_sg.add_argument(
'--security-group', metavar='SECURITY_GROUP',
default=[], action='append', dest='security_groups',
- help=_('Security group associated with the port '
- '(This option can be repeated)'))
+ help=_('Security group associated with the port. You can '
+ 'repeat this option.'))
group_sg.add_argument(
'--no-security-groups',
action='store_true',
- help=_('Associate no security groups with the port'))
+ help=_('Associate no security groups with the port.'))
def _resolv_sgid(self, secgroup):
return neutronV20.find_resourceid_by_name_or_id(
@@ -107,9 +155,12 @@ def add_arguments_extradhcpopt(self, parser):
default=[],
action='append',
dest='extra_dhcp_opts',
+ type=utils.str2dict_type(
+ required_keys=['opt_name'],
+ optional_keys=['opt_value', 'ip_version']),
help=_('Extra dhcp options to be assigned to this port: '
- 'opt_name=,opt_value=, '
- '(This option can be repeated.)'))
+ 'opt_name=,opt_value=,'
+ 'ip_version={4,6}. You can repeat this option.'))
def args2body_extradhcpopt(self, parsed_args, port):
ops = []
@@ -119,127 +170,172 @@ def args2body_extradhcpopt(self, parsed_args, port):
# both must be thrown out.
opt_ele = {}
edo_err_msg = _("Invalid --extra-dhcp-opt option, can only be: "
- "opt_name=,opt_value=, "
- "(This option can be repeated.")
+ "opt_name=,opt_value=,"
+ "ip_version={4,6}. "
+ "You can repeat this option.")
for opt in parsed_args.extra_dhcp_opts:
- if opt.split('=')[0] in ['opt_value', 'opt_name']:
- opt_ele.update(utils.str2dict(opt))
- if (('opt_name' in opt_ele) and
- ('opt_value' in opt_ele)):
- if opt_ele['opt_value'] == 'null':
- opt_ele['opt_value'] = None
- ops.append(opt_ele)
- opt_ele = {}
- else:
- raise exceptions.CommandError(edo_err_msg)
+ opt_ele.update(opt)
+ if ('opt_name' in opt_ele and
+ ('opt_value' in opt_ele or 'ip_version' in opt_ele)):
+ if opt_ele.get('opt_value') == 'null':
+ opt_ele['opt_value'] = None
+ ops.append(opt_ele)
+ opt_ele = {}
else:
raise exceptions.CommandError(edo_err_msg)
if ops:
- port.update({'extra_dhcp_opts': ops})
+ port['extra_dhcp_opts'] = ops
+
+
+class UpdatePortAllowedAddressPair(object):
+ """Update Port for allowed_address_pairs"""
+
+ 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|CIDR[,mac_address=MAC_ADDR]',
+ default=[],
+ action='append',
+ dest='allowed_address_pairs',
+ type=utils.str2dict_type(
+ 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',
+ action='store_true',
+ help=_('Associate no allowed address pairs with the port.'))
+
+ def args2body_allowedaddresspairs(self, parsed_args, port):
+ if parsed_args.allowed_address_pairs:
+ port['allowed_address_pairs'] = parsed_args.allowed_address_pairs
+ elif parsed_args.no_allowed_address_pairs:
+ port['allowed_address_pairs'] = []
class CreatePort(neutronV20.CreateCommand, UpdatePortSecGroupMixin,
- UpdateExtraDhcpOptMixin):
+ UpdateExtraDhcpOptMixin, qos_policy.CreateQosPolicyMixin,
+ UpdatePortAllowedAddressPair):
"""Create a port for a given tenant."""
resource = 'port'
- log = logging.getLogger(__name__ + '.CreatePort')
def add_known_arguments(self, parser):
- parser.add_argument(
- '--name',
- help=_('Name of this port'))
+ _add_updatable_args(parser)
parser.add_argument(
'--admin-state-down',
dest='admin_state', action='store_false',
- help=_('Set admin state up to false'))
+ help=_('Set admin state up to false.'))
parser.add_argument(
'--admin_state_down',
dest='admin_state', action='store_false',
help=argparse.SUPPRESS)
parser.add_argument(
'--mac-address',
- help=_('MAC address of this port'))
+ help=_('MAC address of this port.'))
parser.add_argument(
'--mac_address',
help=argparse.SUPPRESS)
parser.add_argument(
- '--device-id',
- help=_('Device id of this port'))
+ '--vnic-type',
+ metavar='',
+ choices=['direct', 'direct-physical', 'macvtap',
+ 'normal', 'baremetal', 'smart-nic'],
+ type=utils.convert_to_lowercase,
+ help=_('VNIC type for this port.'))
parser.add_argument(
- '--device_id',
+ '--vnic_type',
+ choices=['direct', 'direct-physical', 'macvtap',
+ 'normal', 'baremetal', 'smart-nic'],
+ type=utils.convert_to_lowercase,
help=argparse.SUPPRESS)
parser.add_argument(
- '--fixed-ip', metavar='subnet_id=SUBNET,ip_address=IP_ADDR',
- action='append',
- help=_('Desired IP and/or subnet for this port: '
- 'subnet_id=,ip_address=, '
- '(This option can be repeated.)'))
+ '--binding-profile',
+ help=_('Custom data to be passed as binding:profile.'))
parser.add_argument(
- '--fixed_ip',
- action='append',
+ '--binding_profile',
help=argparse.SUPPRESS)
-
self.add_arguments_secgroup(parser)
self.add_arguments_extradhcpopt(parser)
+ self.add_arguments_qos_policy(parser)
+ self.add_arguments_allowedaddresspairs(parser)
parser.add_argument(
'network_id', metavar='NETWORK',
- help=_('Network id or name this port belongs to'))
+ help=_('ID or name of the network this port belongs to.'))
+ dns.add_dns_argument_create(parser, self.resource, 'name')
def args2body(self, parsed_args):
+ client = self.get_client()
_network_id = neutronV20.find_resourceid_by_name_or_id(
- self.get_client(), 'network', parsed_args.network_id)
- body = {'port': {'admin_state_up': parsed_args.admin_state,
- 'network_id': _network_id, }, }
- if parsed_args.mac_address:
- body['port'].update({'mac_address': parsed_args.mac_address})
- if parsed_args.device_id:
- body['port'].update({'device_id': parsed_args.device_id})
- if parsed_args.tenant_id:
- body['port'].update({'tenant_id': parsed_args.tenant_id})
- if parsed_args.name:
- body['port'].update({'name': parsed_args.name})
- ips = []
- if parsed_args.fixed_ip:
- for ip_spec in parsed_args.fixed_ip:
- ip_dict = utils.str2dict(ip_spec)
- if 'subnet_id' in ip_dict:
- subnet_name_id = ip_dict['subnet_id']
- _subnet_id = neutronV20.find_resourceid_by_name_or_id(
- self.get_client(), 'subnet', subnet_name_id)
- ip_dict['subnet_id'] = _subnet_id
- ips.append(ip_dict)
- if ips:
- body['port'].update({'fixed_ips': ips})
-
- self.args2body_secgroup(parsed_args, body['port'])
- self.args2body_extradhcpopt(parsed_args, body['port'])
-
- return body
+ client, 'network', parsed_args.network_id)
+ body = {'admin_state_up': parsed_args.admin_state,
+ 'network_id': _network_id, }
+ _updatable_args2body(parsed_args, body, client)
+ neutronV20.update_dict(parsed_args, body,
+ ['mac_address', 'tenant_id'])
+ if parsed_args.vnic_type:
+ body['binding:vnic_type'] = parsed_args.vnic_type
+ if parsed_args.binding_profile:
+ body['binding:profile'] = jsonutils.loads(
+ parsed_args.binding_profile)
+
+ self.args2body_secgroup(parsed_args, body)
+ self.args2body_extradhcpopt(parsed_args, body)
+ self.args2body_qos_policy(parsed_args, body)
+ self.args2body_allowedaddresspairs(parsed_args, body)
+ dns.args2body_dns_create(parsed_args, body, 'name')
+
+ return {'port': body}
class DeletePort(neutronV20.DeleteCommand):
"""Delete a given port."""
resource = 'port'
- log = logging.getLogger(__name__ + '.DeletePort')
class UpdatePort(neutronV20.UpdateCommand, UpdatePortSecGroupMixin,
- UpdateExtraDhcpOptMixin):
+ UpdateExtraDhcpOptMixin, qos_policy.UpdateQosPolicyMixin,
+ UpdatePortAllowedAddressPair):
"""Update port's information."""
resource = 'port'
- log = logging.getLogger(__name__ + '.UpdatePort')
def add_known_arguments(self, parser):
+ _add_updatable_args(parser)
+ parser.add_argument(
+ '--admin-state-up',
+ choices=['True', 'False'],
+ help=_('Set admin state up for the port.'))
+ parser.add_argument(
+ '--admin_state_up',
+ choices=['True', 'False'],
+ help=argparse.SUPPRESS)
self.add_arguments_secgroup(parser)
self.add_arguments_extradhcpopt(parser)
+ self.add_arguments_qos_policy(parser)
+ self.add_arguments_allowedaddresspairs(parser)
+ dns.add_dns_argument_update(parser, self.resource, 'name')
def args2body(self, parsed_args):
- body = {'port': {}}
- self.args2body_secgroup(parsed_args, body['port'])
- self.args2body_extradhcpopt(parsed_args, body['port'])
- return body
+ body = {}
+ client = self.get_client()
+ _updatable_args2body(parsed_args, body, client)
+ if parsed_args.admin_state_up:
+ body['admin_state_up'] = parsed_args.admin_state_up
+
+ self.args2body_secgroup(parsed_args, body)
+ self.args2body_extradhcpopt(parsed_args, body)
+ self.args2body_qos_policy(parsed_args, body)
+ self.args2body_allowedaddresspairs(parsed_args, body)
+ dns.args2body_dns_update(parsed_args, body, 'name')
+
+ return {'port': body}
diff --git a/neutronclient/neutron/v2_0/purge.py b/neutronclient/neutron/v2_0/purge.py
new file mode 100644
index 000000000..332a20b32
--- /dev/null
+++ b/neutronclient/neutron/v2_0/purge.py
@@ -0,0 +1,150 @@
+# 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._i18n import _
+from neutronclient.neutron import v2_0 as neutronV20
+
+
+class Purge(neutronV20.NeutronCommand):
+ """Delete all resources that belong to a given tenant."""
+
+ def _pluralize(self, string):
+ return string + 's'
+
+ def _get_resources(self, neutron_client, resource_types, tenant_id):
+ resources = []
+ for resource_type in resource_types:
+ resources.append([])
+ resource_type_plural = self._pluralize(resource_type)
+ opts = {'fields': ['id', 'tenant_id']}
+ if resource_type_plural == 'ports':
+ opts['fields'].append('device_id')
+ opts['fields'].append('device_owner')
+ function = getattr(neutron_client, 'list_%s' %
+ resource_type_plural)
+ if callable(function):
+ returned_resources = function(**opts).get(resource_type_plural,
+ [])
+ for resource in returned_resources:
+ if resource['tenant_id'] == tenant_id:
+ index = resource_types.index(resource_type)
+ resources[index].append(resource)
+ self.total_resources += 1
+ return resources
+
+ def _delete_resource(self, neutron_client, resource_type, resource):
+ resource_id = resource['id']
+ if resource_type == 'port':
+ router_interface_owners = ['network:router_interface',
+ 'network:router_interface_distributed']
+ if resource.get('device_owner', '') in router_interface_owners:
+ body = {'port_id': resource_id}
+ neutron_client.remove_interface_router(resource['device_id'],
+ body)
+ return
+ function = getattr(neutron_client, 'delete_%s' % resource_type)
+ if callable(function):
+ function(resource_id)
+
+ def _purge_resources(self, neutron_client, resource_types,
+ tenant_resources):
+ deleted = {}
+ failed = {}
+ failures = False
+ for resources in tenant_resources:
+ index = tenant_resources.index(resources)
+ resource_type = resource_types[index]
+ failed[resource_type] = 0
+ deleted[resource_type] = 0
+ for resource in resources:
+ try:
+ self._delete_resource(neutron_client, resource_type,
+ resource)
+ deleted[resource_type] += 1
+ self.deleted_resources += 1
+ except Exception:
+ failures = True
+ failed[resource_type] += 1
+ self.total_resources -= 1
+ percent_complete = 100
+ if self.total_resources > 0:
+ percent_complete = (self.deleted_resources /
+ float(self.total_resources)) * 100
+ sys.stdout.write("\rPurging resources: %d%% complete." %
+ percent_complete)
+ sys.stdout.flush()
+ return (deleted, failed, failures)
+
+ def _build_message(self, deleted, failed, failures):
+ msg = ''
+ deleted_msg = []
+ for resource, value in deleted.items():
+ if value:
+ if not msg:
+ msg = 'Deleted'
+ if not value == 1:
+ resource = self._pluralize(resource)
+ deleted_msg.append(" %d %s" % (value, resource))
+ if deleted_msg:
+ msg += ','.join(deleted_msg)
+
+ failed_msg = []
+ if failures:
+ if msg:
+ msg += '. '
+ msg += 'The following resources could not be deleted:'
+ for resource, value in failed.items():
+ if value:
+ if not value == 1:
+ resource = self._pluralize(resource)
+ failed_msg.append(" %d %s" % (value, resource))
+ msg += ','.join(failed_msg)
+
+ if msg:
+ msg += '.'
+ else:
+ msg = _('Tenant has no supported resources.')
+
+ return msg
+
+ def get_parser(self, prog_name):
+ parser = super(Purge, self).get_parser(prog_name)
+ parser.add_argument(
+ 'tenant', metavar='TENANT',
+ help=_('ID of Tenant owning the resources to be deleted.'))
+ return parser
+
+ def take_action(self, parsed_args):
+ neutron_client = self.get_client()
+
+ self.any_failures = False
+
+ # A list of the types of resources supported in the order in which
+ # they should be deleted.
+ resource_types = ['floatingip', 'port', 'router',
+ 'network', 'security_group']
+
+ deleted = {}
+ failed = {}
+ self.total_resources = 0
+ self.deleted_resources = 0
+ resources = self._get_resources(neutron_client, resource_types,
+ parsed_args.tenant)
+ deleted, failed, failures = self._purge_resources(neutron_client,
+ resource_types,
+ resources)
+ print('\n%s' % self._build_message(deleted, failed, failures))
diff --git a/neutronclient/tests/unit/lb/__init__.py b/neutronclient/neutron/v2_0/qos/__init__.py
similarity index 100%
rename from neutronclient/tests/unit/lb/__init__.py
rename to neutronclient/neutron/v2_0/qos/__init__.py
diff --git a/neutronclient/neutron/v2_0/qos/bandwidth_limit_rule.py b/neutronclient/neutron/v2_0/qos/bandwidth_limit_rule.py
new file mode 100644
index 000000000..27e7b626a
--- /dev/null
+++ b/neutronclient/neutron/v2_0/qos/bandwidth_limit_rule.py
@@ -0,0 +1,101 @@
+# Copyright 2015 Huawei Technologies India Pvt Ltd, Inc.
+# All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+
+
+from neutronclient._i18n import _
+from neutronclient.common import exceptions
+from neutronclient.neutron import v2_0 as neutronv20
+from neutronclient.neutron.v2_0.qos import rule as qos_rule
+
+
+BANDWIDTH_LIMIT_RULE_RESOURCE = 'bandwidth_limit_rule'
+
+
+def add_bandwidth_limit_arguments(parser):
+ parser.add_argument(
+ '--max-kbps',
+ help=_('Maximum bandwidth in kbps.'))
+ parser.add_argument(
+ '--max-burst-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."))
+ neutronv20.update_dict(parsed_args, body,
+ ['max_kbps', 'max_burst_kbps', 'tenant_id'])
+
+
+class CreateQoSBandwidthLimitRule(qos_rule.QosRuleMixin,
+ neutronv20.CreateCommand):
+ """Create a qos bandwidth limit rule."""
+
+ resource = BANDWIDTH_LIMIT_RULE_RESOURCE
+
+ def add_known_arguments(self, parser):
+ super(CreateQoSBandwidthLimitRule, self).add_known_arguments(parser)
+ add_bandwidth_limit_arguments(parser)
+
+ def args2body(self, parsed_args):
+ body = {}
+ update_bandwidth_limit_args2body(parsed_args, body)
+ return {self.resource: body}
+
+
+class ListQoSBandwidthLimitRules(qos_rule.QosRuleMixin,
+ neutronv20.ListCommand):
+ """List all qos bandwidth limit rules belonging to the specified policy."""
+
+ resource = BANDWIDTH_LIMIT_RULE_RESOURCE
+ _formatters = {}
+ pagination_support = True
+ sorting_support = True
+
+
+class ShowQoSBandwidthLimitRule(qos_rule.QosRuleMixin, neutronv20.ShowCommand):
+ """Show information about the given qos bandwidth limit rule."""
+
+ resource = BANDWIDTH_LIMIT_RULE_RESOURCE
+ allow_names = False
+
+
+class UpdateQoSBandwidthLimitRule(qos_rule.QosRuleMixin,
+ neutronv20.UpdateCommand):
+ """Update the given qos bandwidth limit rule."""
+
+ resource = BANDWIDTH_LIMIT_RULE_RESOURCE
+ allow_names = False
+
+ def add_known_arguments(self, parser):
+ super(UpdateQoSBandwidthLimitRule, self).add_known_arguments(parser)
+ add_bandwidth_limit_arguments(parser)
+
+ def args2body(self, parsed_args):
+ body = {}
+ update_bandwidth_limit_args2body(parsed_args, body)
+ return {self.resource: body}
+
+
+class DeleteQoSBandwidthLimitRule(qos_rule.QosRuleMixin,
+ neutronv20.DeleteCommand):
+ """Delete a given qos bandwidth limit rule."""
+
+ resource = BANDWIDTH_LIMIT_RULE_RESOURCE
+ allow_names = False
diff --git a/neutronclient/neutron/v2_0/qos/dscp_marking_rule.py b/neutronclient/neutron/v2_0/qos/dscp_marking_rule.py
new file mode 100644
index 000000000..963c481a7
--- /dev/null
+++ b/neutronclient/neutron/v2_0/qos/dscp_marking_rule.py
@@ -0,0 +1,112 @@
+# 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.
+#
+
+from neutronclient._i18n import _
+from neutronclient.common import exceptions
+from neutronclient.neutron import v2_0 as neutronv20
+from neutronclient.neutron.v2_0.qos import rule as qos_rule
+
+
+DSCP_MARKING_RESOURCE = 'dscp_marking_rule'
+# DSCP DETAILS
+# 0 - none | 8 - cs1 | 10 - af11 | 12 - af12 | 14 - af13 |
+# 16 - cs2 | 18 - af21 | 20 - af22 | 22 - af23 | 24 - cs3 |
+# 26 - af31 | 28 - af32 | 30 - af33 | 32 - cs4 | 34 - af41 |
+# 36 - af42 | 38 - af43 | 40 - cs5 | 46 - ef | 48 - cs6 |
+# 56 - cs7
+
+DSCP_VALID_MARKS = [0, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32,
+ 34, 36, 38, 40, 46, 48, 56]
+
+
+def add_dscp_marking_arguments(parser):
+ parser.add_argument(
+ '--dscp-mark',
+ required=True,
+ type=str,
+ help=_('DSCP mark: value can be 0, even numbers from 8-56, \
+ excluding 42, 44, 50, 52, and 54.'))
+
+
+def update_dscp_args2body(parsed_args, body):
+ dscp_mark = parsed_args.dscp_mark
+ if int(dscp_mark) not in DSCP_VALID_MARKS:
+ raise exceptions.CommandError(_("DSCP mark: %s not supported. "
+ "Please note value can either be 0 "
+ "or any even number from 8-56 "
+ "excluding 42, 44, 50, 52 and "
+ "54.") % dscp_mark)
+ neutronv20.update_dict(parsed_args, body,
+ ['dscp_mark'])
+
+
+class CreateQoSDscpMarkingRule(qos_rule.QosRuleMixin,
+ neutronv20.CreateCommand):
+ """Create a QoS DSCP marking rule."""
+
+ resource = DSCP_MARKING_RESOURCE
+
+ def add_known_arguments(self, parser):
+ super(CreateQoSDscpMarkingRule, self).add_known_arguments(parser)
+ add_dscp_marking_arguments(parser)
+
+ def args2body(self, parsed_args):
+ body = {}
+ update_dscp_args2body(parsed_args, body)
+ return {self.resource: body}
+
+
+class ListQoSDscpMarkingRules(qos_rule.QosRuleMixin,
+ neutronv20.ListCommand):
+ """List all QoS DSCP marking rules belonging to the specified policy."""
+
+ _formatters = {}
+ pagination_support = True
+ sorting_support = True
+ resource = DSCP_MARKING_RESOURCE
+
+
+class ShowQoSDscpMarkingRule(qos_rule.QosRuleMixin,
+ neutronv20.ShowCommand):
+ """Show information about the given qos dscp marking rule."""
+
+ resource = DSCP_MARKING_RESOURCE
+ allow_names = False
+
+
+class UpdateQoSDscpMarkingRule(qos_rule.QosRuleMixin,
+ neutronv20.UpdateCommand):
+ """Update the given QoS DSCP marking rule."""
+
+ allow_names = False
+ resource = DSCP_MARKING_RESOURCE
+
+ def add_known_arguments(self, parser):
+ super(UpdateQoSDscpMarkingRule, self).add_known_arguments(parser)
+ add_dscp_marking_arguments(parser)
+
+ def args2body(self, parsed_args):
+ body = {}
+ update_dscp_args2body(parsed_args, body)
+ return {self.resource: body}
+
+
+class DeleteQoSDscpMarkingRule(qos_rule.QosRuleMixin,
+ neutronv20.DeleteCommand):
+ """Delete a given qos dscp marking rule."""
+
+ allow_names = False
+ resource = DSCP_MARKING_RESOURCE
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/qos/policy.py b/neutronclient/neutron/v2_0/qos/policy.py
new file mode 100644
index 000000000..3dc982f31
--- /dev/null
+++ b/neutronclient/neutron/v2_0/qos/policy.py
@@ -0,0 +1,160 @@
+# Copyright 2015 Huawei Technologies India Pvt Ltd, 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 os
+
+from neutronclient._i18n import _
+from neutronclient.neutron import v2_0 as neutronv20
+
+
+def get_qos_policy_id(client, policy_id_or_name):
+ _policy_id = neutronv20.find_resourceid_by_name_or_id(
+ client, 'policy', policy_id_or_name, cmd_resource='qos_policy')
+ return _policy_id
+
+
+class CreateQosPolicyMixin(object):
+ def add_arguments_qos_policy(self, parser):
+ qos_policy_args = parser.add_mutually_exclusive_group()
+ qos_policy_args.add_argument(
+ '--qos-policy',
+ help=_('ID or name of the QoS policy that should'
+ 'be attached to the resource.'))
+ return qos_policy_args
+
+ def args2body_qos_policy(self, parsed_args, resource):
+ if parsed_args.qos_policy:
+ _policy_id = get_qos_policy_id(self.get_client(),
+ parsed_args.qos_policy)
+ resource['qos_policy_id'] = _policy_id
+
+
+class UpdateQosPolicyMixin(CreateQosPolicyMixin):
+ def add_arguments_qos_policy(self, parser):
+ qos_policy_args = (super(UpdateQosPolicyMixin, self).
+ add_arguments_qos_policy(parser))
+ qos_policy_args.add_argument(
+ '--no-qos-policy',
+ action='store_true',
+ help=_('Detach QoS policy from the resource.'))
+ return qos_policy_args
+
+ def args2body_qos_policy(self, parsed_args, resource):
+ super(UpdateQosPolicyMixin, self).args2body_qos_policy(parsed_args,
+ resource)
+ if parsed_args.no_qos_policy:
+ resource['qos_policy_id'] = None
+
+
+class ListQoSPolicy(neutronv20.ListCommand):
+ """List QoS policies that belong to a given tenant connection."""
+
+ resource = 'policy'
+ shadow_resource = 'qos_policy'
+ list_columns = ['id', 'name']
+ pagination_support = True
+ sorting_support = True
+
+
+class ShowQoSPolicy(neutronv20.ShowCommand):
+ """Show information of a given qos policy."""
+
+ resource = 'policy'
+ shadow_resource = 'qos_policy'
+
+ def format_output_data(self, data):
+ rules = []
+ for rule in data['policy'].get('rules', []):
+ rules.append("%s (type: %s)" % (rule['id'], rule['type']))
+ data['policy']['rules'] = os.linesep.join(rules)
+
+ super(ShowQoSPolicy, self).format_output_data(data)
+
+
+class CreateQoSPolicy(neutronv20.CreateCommand):
+ """Create a qos policy."""
+
+ resource = 'policy'
+ shadow_resource = 'qos_policy'
+
+ def add_known_arguments(self, parser):
+ parser.add_argument(
+ 'name', metavar='NAME',
+ help=_('Name of the QoS policy to be created.'))
+ parser.add_argument(
+ '--description',
+ help=_('Description of the QoS policy to be created.'))
+ parser.add_argument(
+ '--shared',
+ action='store_true',
+ help=_('Accessible by other tenants. '
+ 'Set shared to True (default is False).'))
+
+ def args2body(self, parsed_args):
+ body = {'name': parsed_args.name}
+ if parsed_args.description:
+ body['description'] = parsed_args.description
+ if parsed_args.shared:
+ body['shared'] = parsed_args.shared
+ if parsed_args.tenant_id:
+ body['tenant_id'] = parsed_args.tenant_id
+ return {self.resource: body}
+
+
+class UpdateQoSPolicy(neutronv20.UpdateCommand):
+ """Update a given qos policy."""
+
+ resource = 'policy'
+ shadow_resource = 'qos_policy'
+
+ def add_known_arguments(self, parser):
+ parser.add_argument(
+ '--name',
+ help=_('Name of the QoS policy.'))
+ parser.add_argument(
+ '--description',
+ help=_('Description of the QoS policy.'))
+ shared_group = parser.add_mutually_exclusive_group()
+ shared_group.add_argument(
+ '--shared',
+ action='store_true',
+ help=_('Accessible by other tenants. '
+ 'Set shared to True (default is False).'))
+ shared_group.add_argument(
+ '--no-shared',
+ action='store_true',
+ help=_('Not accessible by other tenants. '
+ 'Set shared to False.'))
+
+ def args2body(self, parsed_args):
+ body = {}
+ if parsed_args.name:
+ body['name'] = parsed_args.name
+ if parsed_args.description:
+ body['description'] = parsed_args.description
+ if parsed_args.shared:
+ body['shared'] = True
+ if parsed_args.no_shared:
+ body['shared'] = False
+
+ return {self.resource: body}
+
+
+class DeleteQoSPolicy(neutronv20.DeleteCommand):
+ """Delete a given qos policy."""
+
+ resource = 'policy'
+ shadow_resource = 'qos_policy'
diff --git a/neutronclient/neutron/v2_0/qos/rule.py b/neutronclient/neutron/v2_0/qos/rule.py
new file mode 100644
index 000000000..a2fc9a472
--- /dev/null
+++ b/neutronclient/neutron/v2_0/qos/rule.py
@@ -0,0 +1,63 @@
+# Copyright 2015 Huawei Technologies India Pvt Ltd, Inc.
+# All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+
+
+from neutronclient._i18n import _
+from neutronclient.neutron import v2_0 as neutronv20
+from neutronclient.neutron.v2_0.qos import policy as qos_policy
+
+
+def add_policy_argument(parser):
+ parser.add_argument(
+ 'policy', metavar='QOS_POLICY',
+ help=_('ID or name of the QoS policy.'))
+
+
+def add_rule_argument(parser):
+ parser.add_argument(
+ 'rule', metavar='QOS_RULE',
+ help=_('ID of the QoS rule.'))
+
+
+def update_policy_args2body(parsed_args, body):
+ neutronv20.update_dict(parsed_args, body, ['policy'])
+
+
+def update_rule_args2body(parsed_args, body):
+ neutronv20.update_dict(parsed_args, body, ['rule'])
+
+
+class QosRuleMixin(object):
+ def add_known_arguments(self, parser):
+ add_policy_argument(parser)
+
+ def set_extra_attrs(self, parsed_args):
+ self.parent_id = qos_policy.get_qos_policy_id(self.get_client(),
+ parsed_args.policy)
+
+ def args2body(self, parsed_args):
+ body = {}
+ update_policy_args2body(parsed_args, body)
+ return {'qos_rule': body}
+
+
+class ListQoSRuleTypes(neutronv20.ListCommand):
+ """List available qos rule types."""
+
+ resource = 'rule_type'
+ shadow_resource = 'qos_rule_type'
+ pagination_support = True
+ sorting_support = True
diff --git a/neutronclient/neutron/v2_0/quota.py b/neutronclient/neutron/v2_0/quota.py
index 1dd07f86a..a55e29f6d 100644
--- a/neutronclient/neutron/v2_0/quota.py
+++ b/neutronclient/neutron/v2_0/quota.py
@@ -14,48 +14,45 @@
# under the License.
#
-from __future__ import print_function
-
+import abc
import argparse
-import logging
from cliff import lister
from cliff import show
+from oslo_serialization import jsonutils
+from neutronclient._i18n import _
from neutronclient.common import exceptions
from neutronclient.common import utils
from neutronclient.neutron import v2_0 as neutronV20
-from neutronclient.openstack.common.gettextutils import _
-def get_tenant_id(tenant_id, client):
- return (tenant_id if tenant_id else
+def get_tenant_id(args, client):
+ return (args.pos_tenant_id or args.tenant_id or
client.get_quotas_tenant()['tenant']['tenant_id'])
class DeleteQuota(neutronV20.NeutronCommand):
"""Delete defined quotas of a given tenant."""
- api = 'network'
resource = 'quota'
- log = logging.getLogger(__name__ + '.DeleteQuota')
def get_parser(self, prog_name):
parser = super(DeleteQuota, self).get_parser(prog_name)
parser.add_argument(
'--tenant-id', metavar='tenant-id',
- help=_('The owner tenant ID'))
+ help=_('The owner tenant ID.'))
parser.add_argument(
'--tenant_id',
help=argparse.SUPPRESS)
+ parser.add_argument(
+ 'pos_tenant_id',
+ help=argparse.SUPPRESS, nargs='?')
return parser
- def run(self, parsed_args):
- self.log.debug('run(%s)' % parsed_args)
+ def take_action(self, parsed_args):
neutron_client = self.get_client()
- neutron_client.format = parsed_args.request_format
- tenant_id = get_tenant_id(parsed_args.tenant_id,
- neutron_client)
+ tenant_id = get_tenant_id(parsed_args, neutron_client)
obj_deleter = getattr(neutron_client,
"delete_%s" % self.resource)
obj_deleter(tenant_id)
@@ -69,20 +66,16 @@ def run(self, parsed_args):
class ListQuota(neutronV20.NeutronCommand, lister.Lister):
"""List quotas of all tenants who have non-default quota values."""
- api = 'network'
resource = 'quota'
- log = logging.getLogger(__name__ + '.ListQuota')
def get_parser(self, prog_name):
parser = super(ListQuota, self).get_parser(prog_name)
return parser
- def get_data(self, parsed_args):
- self.log.debug('get_data(%s)', parsed_args)
+ def take_action(self, parsed_args):
neutron_client = self.get_client()
search_opts = {}
self.log.debug('search options: %s', search_opts)
- neutron_client.format = parsed_args.request_format
obj_lister = getattr(neutron_client,
"list_%ss" % self.resource)
data = obj_lister(**search_opts)
@@ -92,91 +85,117 @@ def get_data(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 ShowQuota(neutronV20.NeutronCommand, show.ShowOne):
- """Show quotas of a given tenant
+class ShowQuotaBase(neutronV20.NeutronCommand, show.ShowOne):
+ """Base class to show quotas of a given tenant."""
- """
- api = 'network'
resource = "quota"
- log = logging.getLogger(__name__ + '.ShowQuota')
+
+ @abc.abstractmethod
+ def retrieve_data(self, tenant_id, neutron_client):
+ """Retrieve data using neutron client for the given tenant."""
def get_parser(self, prog_name):
- parser = super(ShowQuota, self).get_parser(prog_name)
+ parser = super(ShowQuotaBase, self).get_parser(prog_name)
parser.add_argument(
'--tenant-id', metavar='tenant-id',
- help=_('The owner tenant ID'))
+ help=_('The owner tenant ID.'))
parser.add_argument(
'--tenant_id',
help=argparse.SUPPRESS)
+ # allow people to do neutron quota-show .
+ # we use a different name for this 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 get_data(self, parsed_args):
- self.log.debug('get_data(%s)', parsed_args)
+ def take_action(self, parsed_args):
neutron_client = self.get_client()
- neutron_client.format = parsed_args.request_format
- tenant_id = get_tenant_id(parsed_args.tenant_id,
- neutron_client)
- params = {}
- obj_shower = getattr(neutron_client,
- "show_%s" % self.resource)
- data = obj_shower(tenant_id, **params)
+ tenant_id = get_tenant_id(parsed_args, neutron_client)
+ data = self.retrieve_data(tenant_id, neutron_client)
if self.resource in data:
- for k, v in data[self.resource].iteritems():
- if isinstance(v, list):
- value = ""
- for _item in v:
- if value:
- value += "\n"
- if isinstance(_item, dict):
- value += utils.dumps(_item)
- else:
- value += str(_item)
- data[self.resource][k] = value
- elif v is None:
- data[self.resource][k] = ''
- return zip(*sorted(data[self.resource].iteritems()))
- else:
- return None
+ return zip(*sorted(data[self.resource].items()))
+ return
+
+
+class ShowQuota(ShowQuotaBase):
+ """Show quotas for a given tenant."""
+
+ def retrieve_data(self, tenant_id, neutron_client):
+ return neutron_client.show_quota(tenant_id)
+
+
+class ShowQuotaDefault(ShowQuotaBase):
+ """Show default quotas for a given tenant."""
+
+ def retrieve_data(self, tenant_id, neutron_client):
+ return neutron_client.show_quota_default(tenant_id)
class UpdateQuota(neutronV20.NeutronCommand, show.ShowOne):
- """Define tenant's quotas not to use defaults."""
+ """Update a given tenant's quotas."""
resource = 'quota'
- log = logging.getLogger(__name__ + '.UpdateQuota')
def get_parser(self, prog_name):
parser = super(UpdateQuota, self).get_parser(prog_name)
parser.add_argument(
'--tenant-id', metavar='tenant-id',
- help=_('The owner tenant ID'))
+ help=_('The owner tenant ID.'))
parser.add_argument(
'--tenant_id',
help=argparse.SUPPRESS)
parser.add_argument(
'--network', metavar='networks',
- help=_('The limit of networks'))
+ help=_('The limit of networks.'))
parser.add_argument(
'--subnet', metavar='subnets',
- help=_('The limit of subnets'))
+ help=_('The limit of subnets.'))
parser.add_argument(
'--port', metavar='ports',
- help=_('The limit of ports'))
+ help=_('The limit of ports.'))
parser.add_argument(
'--router', metavar='routers',
- help=_('The limit of routers'))
+ help=_('The limit of routers.'))
parser.add_argument(
'--floatingip', metavar='floatingips',
- help=_('The limit of floating IPs'))
+ help=_('The limit of floating IPs.'))
parser.add_argument(
'--security-group', metavar='security_groups',
- help=_('The limit of security groups'))
+ help=_('The limit of security groups.'))
parser.add_argument(
'--security-group-rule', metavar='security_group_rules',
- help=_('The limit of security groups rules'))
+ help=_('The limit of security groups rules.'))
+ parser.add_argument(
+ '--vip', metavar='vips',
+ help=_('The limit of vips.'))
+ parser.add_argument(
+ '--pool', metavar='pools',
+ help=_('The limit of pools.'))
+ parser.add_argument(
+ '--member', metavar='members',
+ 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='?')
+
return parser
def _validate_int(self, name, value):
@@ -185,23 +204,27 @@ 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'):
+ 'security_group', 'security_group_rule',
+ '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 get_data(self, parsed_args):
- self.log.debug('run(%s)', parsed_args)
+ def take_action(self, parsed_args):
neutron_client = self.get_client()
- neutron_client.format = parsed_args.request_format
_extra_values = neutronV20.parse_args_to_dict(self.values_specs)
neutronV20._merge_args(self, parsed_args, _extra_values,
self.values_specs)
@@ -212,23 +235,22 @@ def get_data(self, parsed_args):
body[self.resource] = _extra_values
obj_updator = getattr(neutron_client,
"update_%s" % self.resource)
- tenant_id = get_tenant_id(parsed_args.tenant_id,
- neutron_client)
+ tenant_id = get_tenant_id(parsed_args, neutron_client)
data = obj_updator(tenant_id, body)
if self.resource in data:
- for k, v in data[self.resource].iteritems():
+ for k, v in data[self.resource].items():
if isinstance(v, list):
value = ""
for _item in v:
if value:
value += "\n"
if isinstance(_item, dict):
- value += utils.dumps(_item)
+ value += jsonutils.dumps(_item)
else:
value += str(_item)
data[self.resource][k] = value
elif v is None:
data[self.resource][k] = ''
- return zip(*sorted(data[self.resource].iteritems()))
+ return zip(*sorted(data[self.resource].items()))
else:
- return None
+ return
diff --git a/neutronclient/neutron/v2_0/rbac.py b/neutronclient/neutron/v2_0/rbac.py
new file mode 100644
index 000000000..8b356d3ec
--- /dev/null
+++ b/neutronclient/neutron/v2_0/rbac.py
@@ -0,0 +1,118 @@
+# 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.
+
+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}
+RBAC_OBJECTS = {'network': {'network': 'network'},
+ 'qos-policy': {'policy': 'qos_policy'}}
+
+
+def _get_cmd_resource(obj_type):
+ resource = list(RBAC_OBJECTS[obj_type])[0]
+ cmd_resource = RBAC_OBJECTS[obj_type][resource]
+ return resource, cmd_resource
+
+
+def get_rbac_obj_params(client, obj_type, obj_id_or_name):
+ resource, cmd_resource = _get_cmd_resource(obj_type)
+ obj_id = neutronV20.find_resourceid_by_name_or_id(
+ client=client, resource=resource, name_or_id=obj_id_or_name,
+ cmd_resource=cmd_resource)
+
+ return obj_id, cmd_resource
+
+
+class ListRBACPolicy(neutronV20.ListCommand):
+ """List RBAC policies that belong to a given tenant."""
+
+ resource = 'rbac_policy'
+ list_columns = ['id', 'object_type', 'object_id']
+ pagination_support = True
+ sorting_support = True
+ allow_names = False
+
+
+class ShowRBACPolicy(neutronV20.ShowCommand):
+ """Show information of a given RBAC policy."""
+
+ resource = 'rbac_policy'
+ allow_names = False
+
+
+class CreateRBACPolicy(neutronV20.CreateCommand):
+ """Create a RBAC policy for a given tenant."""
+
+ resource = 'rbac_policy'
+
+ def add_known_arguments(self, parser):
+ parser.add_argument(
+ 'name',
+ metavar='RBAC_OBJECT',
+ help=_('ID or name of the RBAC object.'))
+ 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()
+ _object_id, _object_type = get_rbac_obj_params(neutron_client,
+ parsed_args.type,
+ parsed_args.name)
+ body = {
+ 'object_id': _object_id,
+ 'object_type': _object_type,
+ 'target_tenant': parsed_args.target_tenant,
+ 'action': parsed_args.action,
+ }
+ return {self.resource: body}
+
+
+class UpdateRBACPolicy(neutronV20.UpdateCommand):
+ """Update RBAC policy for given tenant."""
+
+ resource = 'rbac_policy'
+ allow_names = False
+
+ def add_known_arguments(self, parser):
+ parser.add_argument(
+ '--target-tenant',
+ help=_('ID of the tenant to which the RBAC '
+ 'policy will be enforced.'))
+
+ def args2body(self, parsed_args):
+ body = {'target_tenant': parsed_args.target_tenant}
+ return {self.resource: body}
+
+
+class DeleteRBACPolicy(neutronV20.DeleteCommand):
+ """Delete a RBAC policy."""
+
+ resource = 'rbac_policy'
+ allow_names = False
diff --git a/neutronclient/neutron/v2_0/router.py b/neutronclient/neutron/v2_0/router.py
index 83f872988..1f78cb85c 100644
--- a/neutronclient/neutron/v2_0/router.py
+++ b/neutronclient/neutron/v2_0/router.py
@@ -14,21 +14,21 @@
# under the License.
#
-from __future__ import print_function
-
import argparse
-import logging
+from oslo_serialization import jsonutils
+
+from neutronclient._i18n import _
from neutronclient.common import exceptions
from neutronclient.common import utils
from neutronclient.neutron import v2_0 as neutronV20
-from neutronclient.openstack.common.gettextutils import _
+from neutronclient.neutron.v2_0 import availability_zone
def _format_external_gateway_info(router):
try:
- return utils.dumps(router['external_gateway_info'])
- except Exception:
+ return jsonutils.dumps(router['external_gateway_info'])
+ except (TypeError, KeyError):
return ''
@@ -36,9 +36,8 @@ class ListRouter(neutronV20.ListCommand):
"""List routers that belong to a given tenant."""
resource = 'router'
- log = logging.getLogger(__name__ + '.ListRouter')
_formatters = {'external_gateway_info': _format_external_gateway_info, }
- list_columns = ['id', 'name', 'external_gateway_info']
+ list_columns = ['id', 'name', 'external_gateway_info', 'distributed', 'ha']
pagination_support = True
sorting_support = True
@@ -47,59 +46,111 @@ class ShowRouter(neutronV20.ShowCommand):
"""Show information of a given router."""
resource = 'router'
- log = logging.getLogger(__name__ + '.ShowRouter')
class CreateRouter(neutronV20.CreateCommand):
"""Create a router for a given tenant."""
resource = 'router'
- log = logging.getLogger(__name__ + '.CreateRouter')
_formatters = {'external_gateway_info': _format_external_gateway_info, }
def add_known_arguments(self, parser):
parser.add_argument(
'--admin-state-down',
dest='admin_state', action='store_false',
- help=_('Set Admin State Up to false'))
+ help=_('Set admin state up to false.'))
parser.add_argument(
'--admin_state_down',
dest='admin_state', action='store_false',
help=argparse.SUPPRESS)
parser.add_argument(
'name', metavar='NAME',
- help=_('Name of router to create'))
+ help=_('Name of the router to be created.'))
parser.add_argument(
- 'distributed', action='store_true',
- help=_('Create a distributed router (VMware NSX plugin only)'))
+ '--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.'))
+ utils.add_boolean_argument(
+ parser, '--ha', dest='ha',
+ help=_('Create a highly available router.'))
+
+ availability_zone.add_az_hint_argument(parser, self.resource)
def args2body(self, parsed_args):
- body = {'router': {
- 'name': parsed_args.name,
- 'admin_state_up': parsed_args.admin_state, }, }
- if parsed_args.tenant_id:
- body['router'].update({'tenant_id': parsed_args.tenant_id})
- return body
+ 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'])
+ availability_zone.args2body_az_hint(parsed_args, body)
+ return {self.resource: body}
class DeleteRouter(neutronV20.DeleteCommand):
"""Delete a given router."""
- log = logging.getLogger(__name__ + '.DeleteRouter')
resource = 'router'
class UpdateRouter(neutronV20.UpdateCommand):
"""Update router's information."""
- log = logging.getLogger(__name__ + '.UpdateRouter')
resource = 'router'
+ def add_known_arguments(self, parser):
+ parser.add_argument(
+ '--name',
+ help=_('Updated name of the router.'))
+ parser.add_argument(
+ '--description',
+ help=_('Description of router.'))
+ utils.add_boolean_argument(
+ parser, '--admin-state-up', dest='admin_state',
+ help=_('Specify the administrative state of the router '
+ '(True means "Up").'))
+ utils.add_boolean_argument(
+ parser, '--admin_state_up', dest='admin_state',
+ help=argparse.SUPPRESS)
+ utils.add_boolean_argument(
+ parser, '--distributed', dest='distributed',
+ help=_('True means this router should operate in '
+ 'distributed mode.'))
+ routes_group = parser.add_mutually_exclusive_group()
+ routes_group.add_argument(
+ '--route', metavar='destination=CIDR,nexthop=IP_ADDR',
+ action='append', dest='routes',
+ type=utils.str2dict_type(required_keys=['destination', 'nexthop']),
+ help=_('Route to associate with the router.'
+ ' You can repeat this option.'))
+ routes_group.add_argument(
+ '--no-routes',
+ action='store_true',
+ help=_('Remove routes associated with the router.'))
+
+ def args2body(self, parsed_args):
+ body = {}
+ if hasattr(parsed_args, 'admin_state'):
+ body['admin_state_up'] = parsed_args.admin_state
+ neutronV20.update_dict(parsed_args, body,
+ ['name', 'distributed', 'description'])
+ if parsed_args.no_routes:
+ body['routes'] = None
+ elif parsed_args.routes:
+ body['routes'] = parsed_args.routes
+ return {self.resource: body}
+
class RouterInterfaceCommand(neutronV20.NeutronCommand):
"""Based class to Add/Remove router interface."""
- api = 'network'
resource = 'router'
def call_api(self, neutron_client, router_id, body):
@@ -111,47 +162,44 @@ def success_message(self, router_id, portinfo):
def get_parser(self, prog_name):
parser = super(RouterInterfaceCommand, self).get_parser(prog_name)
parser.add_argument(
- 'router_id', metavar='router-id',
- help=_('ID of the router'))
+ 'router', metavar='ROUTER',
+ help=_('ID or name of the router.'))
parser.add_argument(
'interface', metavar='INTERFACE',
help=_('The format is "SUBNET|subnet=SUBNET|port=PORT". '
- 'Either a subnet or port must be specified. '
- 'Both ID and name are accepted as SUBNET or PORT. '
- 'Note that "subnet=" can be omitted when specifying subnet.'))
+ 'Either a subnet or port must be specified. '
+ 'Both ID and name are accepted as SUBNET or PORT. '
+ 'Note that "subnet=" can be omitted when specifying a '
+ 'subnet.'))
return parser
- def run(self, parsed_args):
- self.log.debug('run(%s)' % parsed_args)
+ def take_action(self, parsed_args):
neutron_client = self.get_client()
- neutron_client.format = parsed_args.request_format
if '=' in parsed_args.interface:
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
_router_id = neutronV20.find_resourceid_by_name_or_id(
- neutron_client, self.resource, parsed_args.router_id)
+ neutron_client, self.resource, parsed_args.router)
_interface_id = neutronV20.find_resourceid_by_name_or_id(
neutron_client, resource, value)
body = {'%s_id' % resource: _interface_id}
portinfo = self.call_api(neutron_client, _router_id, body)
- print(self.success_message(parsed_args.router_id, portinfo),
+ print(self.success_message(parsed_args.router, portinfo),
file=self.app.stdout)
class AddInterfaceRouter(RouterInterfaceCommand):
"""Add an internal network interface to a router."""
- log = logging.getLogger(__name__ + '.AddInterfaceRouter')
-
def call_api(self, neutron_client, router_id, body):
return neutron_client.add_interface_router(router_id, body)
@@ -163,8 +211,6 @@ def success_message(self, router_id, portinfo):
class RemoveInterfaceRouter(RouterInterfaceCommand):
"""Remove an internal network interface from a router."""
- log = logging.getLogger(__name__ + '.RemoveInterfaceRouter')
-
def call_api(self, neutron_client, router_id, body):
return neutron_client.remove_interface_router(router_id, body)
@@ -176,59 +222,76 @@ def success_message(self, router_id, portinfo):
class SetGatewayRouter(neutronV20.NeutronCommand):
"""Set the external network gateway for a router."""
- log = logging.getLogger(__name__ + '.SetGatewayRouter')
- api = 'network'
resource = 'router'
def get_parser(self, prog_name):
parser = super(SetGatewayRouter, self).get_parser(prog_name)
parser.add_argument(
- 'router_id', metavar='router-id',
- help=_('ID of the router'))
+ 'router', metavar='ROUTER',
+ help=_('ID or name of the router.'))
+ parser.add_argument(
+ 'external_network', metavar='EXTERNAL-NETWORK',
+ help=_('ID or name of the external network for the gateway.'))
parser.add_argument(
- 'external_network_id', metavar='external-network-id',
- help=_('ID of the external network for the gateway'))
+ '--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'))
+ help=_('Disable source NAT on the router gateway.'))
+ parser.add_argument(
+ '--fixed-ip', metavar='subnet_id=SUBNET,ip_address=IP_ADDR',
+ action='append',
+ type=utils.str2dict_type(optional_keys=['subnet_id',
+ 'ip_address']),
+ help=_('Desired IP and/or subnet on external network: '
+ 'subnet_id=,ip_address=. '
+ 'You can specify both of subnet_id and ip_address or '
+ 'specify one of them as well. '
+ 'You can repeat this option.'))
return parser
- def run(self, parsed_args):
- self.log.debug('run(%s)' % parsed_args)
+ def take_action(self, parsed_args):
neutron_client = self.get_client()
- neutron_client.format = parsed_args.request_format
_router_id = neutronV20.find_resourceid_by_name_or_id(
- neutron_client, self.resource, parsed_args.router_id)
+ neutron_client, self.resource, parsed_args.router)
_ext_net_id = neutronV20.find_resourceid_by_name_or_id(
- neutron_client, 'network', parsed_args.external_network_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:
+ ips = []
+ for ip_spec in parsed_args.fixed_ip:
+ subnet_name_id = ip_spec.get('subnet_id')
+ if subnet_name_id:
+ subnet_id = neutronV20.find_resourceid_by_name_or_id(
+ neutron_client, 'subnet', subnet_name_id)
+ ip_spec['subnet_id'] = subnet_id
+ ips.append(ip_spec)
+ router_dict['external_fixed_ips'] = ips
neutron_client.add_gateway_router(_router_id, router_dict)
- print(_('Set gateway for router %s') % parsed_args.router_id,
+ print(_('Set gateway for router %s') % parsed_args.router,
file=self.app.stdout)
class RemoveGatewayRouter(neutronV20.NeutronCommand):
"""Remove an external network gateway from a router."""
- log = logging.getLogger(__name__ + '.RemoveGatewayRouter')
- api = 'network'
resource = 'router'
def get_parser(self, prog_name):
parser = super(RemoveGatewayRouter, self).get_parser(prog_name)
parser.add_argument(
- 'router_id', metavar='router-id',
- help=_('ID of the router'))
+ 'router', metavar='ROUTER',
+ help=_('ID or name of the router.'))
return parser
- def run(self, parsed_args):
- self.log.debug('run(%s)' % parsed_args)
+ def take_action(self, parsed_args):
neutron_client = self.get_client()
- neutron_client.format = parsed_args.request_format
_router_id = neutronV20.find_resourceid_by_name_or_id(
- neutron_client, self.resource, parsed_args.router_id)
+ neutron_client, self.resource, parsed_args.router)
neutron_client.remove_gateway_router(_router_id)
- print(_('Removed gateway from router %s') % parsed_args.router_id,
+ print(_('Removed gateway from router %s') % parsed_args.router,
file=self.app.stdout)
diff --git a/neutronclient/neutron/v2_0/securitygroup.py b/neutronclient/neutron/v2_0/securitygroup.py
index b59eba5cc..3c0bafa0e 100644
--- a/neutronclient/neutron/v2_0/securitygroup.py
+++ b/neutronclient/neutron/v2_0/securitygroup.py
@@ -15,18 +15,95 @@
#
import argparse
-import logging
+from neutronclient._i18n import _
+from neutronclient.common import exceptions
+from neutronclient.common import utils
from neutronclient.neutron import v2_0 as neutronV20
-from neutronclient.openstack.common.gettextutils import _
+
+
+def _get_remote(rule):
+ if rule['remote_ip_prefix']:
+ remote = '%s (CIDR)' % rule['remote_ip_prefix']
+ elif rule['remote_group_id']:
+ remote = '%s (group)' % rule['remote_group_id']
+ else:
+ remote = None
+ return remote
+
+
+def _get_protocol_port(rule):
+ proto = rule['protocol']
+ port_min = rule['port_range_min']
+ port_max = rule['port_range_max']
+ if proto in ('tcp', 'udp'):
+ if (port_min and port_min == port_max):
+ protocol_port = '%s/%s' % (port_min, proto)
+ elif port_min:
+ protocol_port = '%s-%s/%s' % (port_min, port_max, proto)
+ else:
+ protocol_port = proto
+ elif proto == 'icmp':
+ icmp_opts = []
+ if port_min is not None:
+ icmp_opts.append('type:%s' % port_min)
+ if port_max is not None:
+ icmp_opts.append('code:%s' % port_max)
+
+ if icmp_opts:
+ protocol_port = 'icmp (%s)' % ', '.join(icmp_opts)
+ else:
+ protocol_port = 'icmp'
+ elif proto is not None:
+ # port_range_min/max are not recognized for protocol
+ # other than TCP, UDP and ICMP.
+ protocol_port = proto
+ else:
+ protocol_port = None
+
+ return protocol_port
+
+
+def _format_sg_rule(rule):
+ formatted = []
+ for field in ['direction',
+ 'ethertype',
+ ('protocol_port', _get_protocol_port),
+ 'remote_ip_prefix',
+ 'remote_group_id']:
+ if isinstance(field, tuple):
+ field, get_method = field
+ data = get_method(rule)
+ else:
+ data = rule[field]
+ if not data:
+ continue
+ if field in ('remote_ip_prefix', 'remote_group_id'):
+ data = '%s: %s' % (field, data)
+ formatted.append(data)
+ return ', '.join(formatted)
+
+
+def _format_sg_rules(secgroup):
+ try:
+ return '\n'.join(sorted([_format_sg_rule(rule) for rule
+ in secgroup['security_group_rules']]))
+ except Exception:
+ return ''
+
+
+def generate_default_ethertype(protocol):
+ if protocol == 'icmpv6':
+ return 'IPv6'
+ return 'IPv4'
class ListSecurityGroup(neutronV20.ListCommand):
"""List security groups that belong to a given tenant."""
resource = 'security_group'
- log = logging.getLogger(__name__ + '.ListSecurityGroup')
- list_columns = ['id', 'name', 'description']
+ list_columns = ['id', 'name', 'security_group_rules']
+ _formatters = {'security_group_rules': _format_sg_rules}
pagination_support = True
sorting_support = True
@@ -35,39 +112,33 @@ class ShowSecurityGroup(neutronV20.ShowCommand):
"""Show information of a given security group."""
resource = 'security_group'
- log = logging.getLogger(__name__ + '.ShowSecurityGroup')
allow_names = True
+ json_indent = 5
class CreateSecurityGroup(neutronV20.CreateCommand):
"""Create a security group."""
resource = 'security_group'
- log = logging.getLogger(__name__ + '.CreateSecurityGroup')
def add_known_arguments(self, parser):
parser.add_argument(
'name', metavar='NAME',
- help=_('Name of security group'))
+ help=_('Name of the security group to be created.'))
parser.add_argument(
'--description',
- help=_('Description of security group'))
+ help=_('Description of the security group to be created.'))
def args2body(self, parsed_args):
- body = {'security_group': {
- 'name': parsed_args.name}}
- if parsed_args.description:
- body['security_group'].update(
- {'description': parsed_args.description})
- if parsed_args.tenant_id:
- body['security_group'].update({'tenant_id': parsed_args.tenant_id})
- return body
+ body = {'name': parsed_args.name}
+ neutronV20.update_dict(parsed_args, body,
+ ['description', 'tenant_id'])
+ return {'security_group': body}
class DeleteSecurityGroup(neutronV20.DeleteCommand):
"""Delete a given security group."""
- log = logging.getLogger(__name__ + '.DeleteSecurityGroup')
resource = 'security_group'
allow_names = True
@@ -75,37 +146,45 @@ class DeleteSecurityGroup(neutronV20.DeleteCommand):
class UpdateSecurityGroup(neutronV20.UpdateCommand):
"""Update a given security group."""
- log = logging.getLogger(__name__ + '.UpdateSecurityGroup')
resource = 'security_group'
def add_known_arguments(self, parser):
parser.add_argument(
'--name',
- help=_('Name of security group'))
+ help=_('Updated name of the security group.'))
parser.add_argument(
'--description',
- help=_('Description of security group'))
+ help=_('Updated description of the security group.'))
def args2body(self, parsed_args):
- body = {'security_group': {}}
- if parsed_args.name:
- body['security_group'].update(
- {'name': parsed_args.name})
- if parsed_args.description:
- body['security_group'].update(
- {'description': parsed_args.description})
- return body
+ body = {}
+ neutronV20.update_dict(parsed_args, body,
+ ['name', 'description'])
+ return {'security_group': body}
class ListSecurityGroupRule(neutronV20.ListCommand):
"""List security group rules that belong to a given tenant."""
resource = 'security_group_rule'
- log = logging.getLogger(__name__ + '.ListSecurityGroupRule')
- list_columns = ['id', 'security_group_id', 'direction', 'protocol',
- 'remote_ip_prefix', 'remote_group_id']
+ list_columns = ['id', 'security_group_id', 'direction',
+ 'ethertype', 'port/protocol', 'remote']
+ # replace_rules: key is an attribute name in Neutron API and
+ # corresponding value is a display name shown by CLI.
replace_rules = {'security_group_id': 'security_group',
'remote_group_id': 'remote_group'}
+ digest_fields = {
+ # The entry 'protocol/port' is left deliberately for backwards
+ # compatibility.
+ 'remote': {
+ 'method': _get_remote,
+ 'depends_on': ['remote_ip_prefix', 'remote_group_id']},
+ 'port/protocol': {
+ 'method': _get_protocol_port,
+ 'depends_on': ['protocol', 'port_range_min', 'port_range_max']},
+ 'protocol/port': {
+ 'method': _get_protocol_port,
+ 'depends_on': ['protocol', 'port_range_min', 'port_range_max']}}
pagination_support = True
sorting_support = True
@@ -113,7 +192,7 @@ def get_parser(self, prog_name):
parser = super(ListSecurityGroupRule, self).get_parser(prog_name)
parser.add_argument(
'--no-nameconv', action='store_true',
- help=_('Do not convert security group ID to its name'))
+ help=_('Do not convert security group ID to its name.'))
return parser
@staticmethod
@@ -122,35 +201,82 @@ def replace_columns(cols, rules, reverse=False):
rules = dict((rules[k], k) for k in rules.keys())
return [rules.get(col, col) for col in cols]
+ def get_required_fields(self, fields):
+ fields = self.replace_columns(fields, self.replace_rules, reverse=True)
+ for field, digest_fields in self.digest_fields.items():
+ if field in fields:
+ fields += digest_fields['depends_on']
+ fields.remove(field)
+ return fields
+
def retrieve_list(self, parsed_args):
- parsed_args.fields = self.replace_columns(parsed_args.fields,
- self.replace_rules,
- reverse=True)
+ parsed_args.fields = self.get_required_fields(parsed_args.fields)
return super(ListSecurityGroupRule, self).retrieve_list(parsed_args)
- def extend_list(self, data, parsed_args):
- if parsed_args.no_nameconv:
- return
+ def _get_sg_name_dict(self, data, page_size, no_nameconv):
+ """Get names of security groups referred in the retrieved rules.
+
+ :return: a dict from secgroup ID to secgroup name
+ """
+ if no_nameconv:
+ return {}
neutron_client = self.get_client()
search_opts = {'fields': ['id', 'name']}
if self.pagination_support:
- page_size = parsed_args.page_size
if page_size:
search_opts.update({'limit': page_size})
sec_group_ids = set()
for rule in data:
for key in self.replace_rules:
- sec_group_ids.add(rule[key])
- search_opts.update({"id": sec_group_ids})
- secgroups = neutron_client.list_security_groups(**search_opts)
- secgroups = secgroups.get('security_groups', [])
- sg_dict = dict([(sg['id'], sg['name'])
- for sg in secgroups if sg['name']])
+ if rule.get(key):
+ sec_group_ids.add(rule[key])
+ sec_group_ids = list(sec_group_ids)
+
+ def _get_sec_group_list(sec_group_ids):
+ search_opts['id'] = sec_group_ids
+ return neutron_client.list_security_groups(
+ **search_opts).get('security_groups', [])
+
+ try:
+ secgroups = _get_sec_group_list(sec_group_ids)
+ except exceptions.RequestURITooLong as uri_len_exc:
+ # Length of a query filter on security group rule id
+ # id=& (with len(uuid)=36)
+ sec_group_id_filter_len = 40
+ # The URI is too long because of too many sec_group_id filters
+ # Use the excess attribute of the exception to know how many
+ # sec_group_id filters can be inserted into a single request
+ sec_group_count = len(sec_group_ids)
+ max_size = ((sec_group_id_filter_len * sec_group_count) -
+ uri_len_exc.excess)
+ chunk_size = max_size // sec_group_id_filter_len
+ secgroups = []
+ for i in range(0, sec_group_count, chunk_size):
+ secgroups.extend(
+ _get_sec_group_list(sec_group_ids[i: i + chunk_size]))
+
+ return dict([(sg['id'], sg['name'])
+ for sg in secgroups if sg['name']])
+
+ @staticmethod
+ def _has_fields(rule, required_fields):
+ return all([key in rule for key in required_fields])
+
+ def extend_list(self, data, parsed_args):
+ sg_dict = self._get_sg_name_dict(data, parsed_args.page_size,
+ parsed_args.no_nameconv)
for rule in data:
+ # Replace security group UUID with its name.
for key in self.replace_rules:
- rule[key] = sg_dict.get(rule[key], rule[key])
+ if key in rule:
+ rule[key] = sg_dict.get(rule[key], rule[key])
+ for field, digest_rule in self.digest_fields.items():
+ if self._has_fields(rule, digest_rule['depends_on']):
+ rule[field] = digest_rule['method'](rule) or 'any'
def setup_columns(self, info, parsed_args):
+ # Translate the specified columns from the command line
+ # into field names used in "info".
parsed_args.columns = self.replace_columns(parsed_args.columns,
self.replace_rules,
reverse=True)
@@ -161,6 +287,7 @@ def setup_columns(self, info, parsed_args):
parsed_args)
cols = info[0]
if not parsed_args.no_nameconv:
+ # Replace column names in the header line (in info[0])
cols = self.replace_columns(info[0], self.replace_rules)
parsed_args.columns = cols
return (cols, info[1])
@@ -170,7 +297,6 @@ class ShowSecurityGroupRule(neutronV20.ShowCommand):
"""Show information of a given security group rule."""
resource = 'security_group_rule'
- log = logging.getLogger(__name__ + '.ShowSecurityGroupRule')
allow_names = False
@@ -178,44 +304,51 @@ class CreateSecurityGroupRule(neutronV20.CreateCommand):
"""Create a security group rule."""
resource = 'security_group_rule'
- log = logging.getLogger(__name__ + '.CreateSecurityGroupRule')
def add_known_arguments(self, parser):
+ parser.add_argument(
+ '--description',
+ help=_('Description of security group rule.'))
parser.add_argument(
'security_group_id', metavar='SECURITY_GROUP',
- help=_('Security group name or id to add rule.'))
+ help=_('ID or name of the security group to '
+ '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'))
+ help=_('Direction of traffic: ingress/egress.'))
parser.add_argument(
'--ethertype',
- default='IPv4',
help=_('IPv4/IPv6'))
parser.add_argument(
'--protocol',
- help=_('Protocol of packet'))
+ type=utils.convert_to_lowercase,
+ help=_('Protocol of packet. Allowed values are '
+ '[icmp, icmpv6, tcp, udp] and '
+ 'integer representations [0-255].'))
parser.add_argument(
'--port-range-min',
- help=_('Starting port range'))
+ help=_('Starting port range. For ICMP it is type.'))
parser.add_argument(
'--port_range_min',
help=argparse.SUPPRESS)
parser.add_argument(
'--port-range-max',
- help=_('Ending port range'))
+ help=_('Ending port range. For ICMP it is code.'))
parser.add_argument(
'--port_range_max',
help=argparse.SUPPRESS)
parser.add_argument(
'--remote-ip-prefix',
- help=_('CIDR to match on'))
+ help=_('CIDR to match on.'))
parser.add_argument(
'--remote_ip_prefix',
help=argparse.SUPPRESS)
parser.add_argument(
'--remote-group-id', metavar='REMOTE_GROUP',
- help=_('Remote security group name or id to apply rule'))
+ help=_('ID or name of the remote security group '
+ 'to which the rule is applied.'))
parser.add_argument(
'--remote_group_id',
help=argparse.SUPPRESS)
@@ -223,37 +356,24 @@ def add_known_arguments(self, parser):
def args2body(self, parsed_args):
_security_group_id = neutronV20.find_resourceid_by_name_or_id(
self.get_client(), 'security_group', parsed_args.security_group_id)
- body = {'security_group_rule': {
- 'security_group_id': _security_group_id,
- 'direction': parsed_args.direction,
- 'ethertype': parsed_args.ethertype}}
- if parsed_args.protocol:
- body['security_group_rule'].update(
- {'protocol': parsed_args.protocol})
- if parsed_args.port_range_min:
- body['security_group_rule'].update(
- {'port_range_min': parsed_args.port_range_min})
- if parsed_args.port_range_max:
- body['security_group_rule'].update(
- {'port_range_max': parsed_args.port_range_max})
- if parsed_args.remote_ip_prefix:
- body['security_group_rule'].update(
- {'remote_ip_prefix': parsed_args.remote_ip_prefix})
+ body = {'security_group_id': _security_group_id,
+ 'direction': parsed_args.direction,
+ 'ethertype': parsed_args.ethertype or
+ generate_default_ethertype(parsed_args.protocol)}
+ neutronV20.update_dict(parsed_args, body,
+ ['protocol', 'port_range_min', 'port_range_max',
+ 'remote_ip_prefix', 'tenant_id',
+ 'description'])
if parsed_args.remote_group_id:
_remote_group_id = neutronV20.find_resourceid_by_name_or_id(
self.get_client(), 'security_group',
parsed_args.remote_group_id)
- body['security_group_rule'].update(
- {'remote_group_id': _remote_group_id})
- if parsed_args.tenant_id:
- body['security_group_rule'].update(
- {'tenant_id': parsed_args.tenant_id})
- return body
+ body['remote_group_id'] = _remote_group_id
+ return {'security_group_rule': body}
class DeleteSecurityGroupRule(neutronV20.DeleteCommand):
"""Delete a given security group rule."""
- log = logging.getLogger(__name__ + '.DeleteSecurityGroupRule')
resource = 'security_group_rule'
allow_names = False
diff --git a/neutronclient/neutron/v2_0/servicetype.py b/neutronclient/neutron/v2_0/servicetype.py
index 4666a9706..ec8454f94 100644
--- a/neutronclient/neutron/v2_0/servicetype.py
+++ b/neutronclient/neutron/v2_0/servicetype.py
@@ -14,8 +14,6 @@
# under the License.
#
-import logging
-
from neutronclient.neutron import v2_0 as neutronV20
@@ -23,7 +21,6 @@ class ListServiceProvider(neutronV20.ListCommand):
"""List service providers."""
resource = 'service_provider'
- log = logging.getLogger(__name__ + '.ListServiceProviders')
list_columns = ['service_type', 'name', 'default']
_formatters = {}
pagination_support = True
diff --git a/neutronclient/neutron/v2_0/subnet.py b/neutronclient/neutron/v2_0/subnet.py
index de0569f30..633826422 100644
--- a/neutronclient/neutron/v2_0/subnet.py
+++ b/neutronclient/neutron/v2_0/subnet.py
@@ -15,43 +15,133 @@
#
import argparse
-import logging
+from oslo_serialization import jsonutils
+
+from neutronclient._i18n import _
from neutronclient.common import exceptions
from neutronclient.common import utils
from neutronclient.neutron import v2_0 as neutronV20
-from neutronclient.openstack.common.gettextutils import _
def _format_allocation_pools(subnet):
try:
- return '\n'.join([utils.dumps(pool) for pool in
+ return '\n'.join([jsonutils.dumps(pool) for pool in
subnet['allocation_pools']])
- except Exception:
+ except (TypeError, KeyError):
return ''
def _format_dns_nameservers(subnet):
try:
- return '\n'.join([utils.dumps(server) for server in
+ return '\n'.join([jsonutils.dumps(server) for server in
subnet['dns_nameservers']])
- except Exception:
+ except (TypeError, KeyError):
return ''
def _format_host_routes(subnet):
try:
- return '\n'.join([utils.dumps(route) for route in
+ return '\n'.join([jsonutils.dumps(route) for route in
subnet['host_routes']])
- except Exception:
+ except (TypeError, KeyError):
return ''
+def add_updatable_arguments(parser):
+ parser.add_argument(
+ '--name',
+ help=_('Name of this subnet.'))
+ parser.add_argument(
+ '--description',
+ help=_('Description of this subnet.'))
+ gateway_sg = parser.add_mutually_exclusive_group()
+ gateway_sg.add_argument(
+ '--gateway', metavar='GATEWAY_IP',
+ help=_('Gateway IP of this subnet.'))
+ gateway_sg.add_argument(
+ '--no-gateway',
+ action='store_true',
+ help=_('Do not configure a gateway for this subnet.'))
+ parser.add_argument(
+ '--allocation-pool', metavar='start=IP_ADDR,end=IP_ADDR',
+ action='append', dest='allocation_pools',
+ type=utils.str2dict_type(required_keys=['start', 'end']),
+ help=_('Allocation pool IP addresses for this subnet '
+ '(This option can be repeated).'))
+ parser.add_argument(
+ '--allocation_pool',
+ action='append', dest='allocation_pools',
+ type=utils.str2dict_type(required_keys=['start', 'end']),
+ help=argparse.SUPPRESS)
+ parser.add_argument(
+ '--host-route', metavar='destination=CIDR,nexthop=IP_ADDR',
+ action='append', dest='host_routes',
+ type=utils.str2dict_type(required_keys=['destination', 'nexthop']),
+ help=_('Additional route (This option can be repeated).'))
+ parser.add_argument(
+ '--dns-nameserver', metavar='DNS_NAMESERVER',
+ action='append', dest='dns_nameservers',
+ help=_('DNS name server for this subnet '
+ '(This option can be repeated).'))
+ parser.add_argument(
+ '--disable-dhcp',
+ action='store_true',
+ help=_('Disable DHCP for this subnet.'))
+ parser.add_argument(
+ '--enable-dhcp',
+ action='store_true',
+ help=_('Enable DHCP for this subnet.'))
+ # NOTE(ihrachys): yes, that's awful, but should be left as-is for
+ # backwards compatibility for versions <=2.3.4 that passed the
+ # boolean values through to the server without any argument
+ # validation.
+ parser.add_argument(
+ '--enable-dhcp=True',
+ action='store_true',
+ dest='enable_dhcp',
+ help=argparse.SUPPRESS)
+ parser.add_argument(
+ '--enable-dhcp=False',
+ action='store_true',
+ dest='disable_dhcp',
+ help=argparse.SUPPRESS)
+
+
+def updatable_args2body(parsed_args, body, for_create=True, ip_version=None):
+ if parsed_args.disable_dhcp and parsed_args.enable_dhcp:
+ raise exceptions.CommandError(_(
+ "You cannot enable and disable DHCP at the same time."))
+
+ neutronV20.update_dict(parsed_args, body,
+ ['name', 'allocation_pools',
+ 'host_routes', 'dns_nameservers',
+ 'description'])
+ if parsed_args.no_gateway:
+ body['gateway_ip'] = None
+ elif parsed_args.gateway:
+ body['gateway_ip'] = parsed_args.gateway
+ if parsed_args.disable_dhcp:
+ body['enable_dhcp'] = False
+ if parsed_args.enable_dhcp:
+ body['enable_dhcp'] = True
+ if for_create and parsed_args.ipv6_ra_mode:
+ if ip_version == 4:
+ raise exceptions.CommandError(_("--ipv6-ra-mode is invalid "
+ "when --ip-version is 4"))
+ body['ipv6_ra_mode'] = parsed_args.ipv6_ra_mode
+ if for_create and parsed_args.ipv6_address_mode:
+ if ip_version == 4:
+ raise exceptions.CommandError(_("--ipv6-address-mode is "
+ "invalid when --ip-version "
+ "is 4"))
+ body['ipv6_address_mode'] = parsed_args.ipv6_address_mode
+
+
class ListSubnet(neutronV20.ListCommand):
"""List subnets that belong to a given tenant."""
resource = 'subnet'
- log = logging.getLogger(__name__ + '.ListSubnet')
_formatters = {'allocation_pools': _format_allocation_pools,
'dns_nameservers': _format_dns_nameservers,
'host_routes': _format_host_routes, }
@@ -64,105 +154,123 @@ class ShowSubnet(neutronV20.ShowCommand):
"""Show information of a given subnet."""
resource = 'subnet'
- log = logging.getLogger(__name__ + '.ShowSubnet')
class CreateSubnet(neutronV20.CreateCommand):
"""Create a subnet for a given tenant."""
resource = 'subnet'
- log = logging.getLogger(__name__ + '.CreateSubnet')
def add_known_arguments(self, parser):
- parser.add_argument(
- '--name',
- help=_('Name of this subnet'))
+ add_updatable_arguments(parser)
parser.add_argument(
'--ip-version',
type=int,
default=4, choices=[4, 6],
- help=_('IP version with default 4'))
+ help=_('IP version to use, default is 4. '
+ 'Note that when subnetpool is specified, '
+ 'IP version is determined from the subnetpool '
+ 'and this option is ignored.'))
parser.add_argument(
'--ip_version',
type=int,
choices=[4, 6],
help=argparse.SUPPRESS)
parser.add_argument(
- '--gateway', metavar='GATEWAY_IP',
- help=_('Gateway ip of this subnet'))
- parser.add_argument(
- '--no-gateway',
- action='store_true',
- help=_('No distribution of gateway'))
+ 'network_id', metavar='NETWORK',
+ help=_('Network ID or name this subnet belongs to.'))
parser.add_argument(
- '--allocation-pool', metavar='start=IP_ADDR,end=IP_ADDR',
- action='append', dest='allocation_pools', type=utils.str2dict,
- help=_('Allocation pool IP addresses for this subnet '
- '(This option can be repeated)'))
+ 'cidr', nargs='?', metavar='CIDR',
+ help=_('CIDR of subnet to create.'))
parser.add_argument(
- '--allocation_pool',
- action='append', dest='allocation_pools', type=utils.str2dict,
- help=argparse.SUPPRESS)
+ '--ipv6-ra-mode',
+ type=utils.convert_to_lowercase,
+ choices=['dhcpv6-stateful', 'dhcpv6-stateless', 'slaac'],
+ help=_('IPv6 RA (Router Advertisement) mode.'))
parser.add_argument(
- '--host-route', metavar='destination=CIDR,nexthop=IP_ADDR',
- action='append', dest='host_routes', type=utils.str2dict,
- help=_('Additional route (This option can be repeated)'))
+ '--ipv6-address-mode',
+ type=utils.convert_to_lowercase,
+ choices=['dhcpv6-stateful', 'dhcpv6-stateless', 'slaac'],
+ help=_('IPv6 address mode.'))
parser.add_argument(
- '--dns-nameserver', metavar='DNS_NAMESERVER',
- action='append', dest='dns_nameservers',
- help=_('DNS name server for this subnet '
- '(This option can be repeated)'))
+ '--subnetpool', metavar='SUBNETPOOL',
+ help=_('ID or name of subnetpool from which this subnet '
+ 'will obtain a CIDR.'))
parser.add_argument(
- '--disable-dhcp',
+ '--use-default-subnetpool',
action='store_true',
- help=_('Disable DHCP for this subnet'))
+ help=_('Use default subnetpool for ip_version, if it exists.'))
parser.add_argument(
- 'network_id', metavar='NETWORK',
- help=_('Network id or name this subnet belongs to'))
+ '--prefixlen', metavar='PREFIX_LENGTH',
+ help=_('Prefix length for subnet allocation from subnetpool.'))
parser.add_argument(
- 'cidr', metavar='CIDR',
- help=_('CIDR of subnet to create'))
+ '--segment', metavar='SEGMENT',
+ help=_('ID of segment with which this subnet will be associated.'))
def args2body(self, parsed_args):
_network_id = neutronV20.find_resourceid_by_name_or_id(
self.get_client(), 'network', parsed_args.network_id)
- body = {'subnet': {'cidr': parsed_args.cidr,
- 'network_id': _network_id,
- 'ip_version': parsed_args.ip_version, }, }
-
- if parsed_args.gateway and parsed_args.no_gateway:
- raise exceptions.CommandError(_("--gateway option and "
- "--no-gateway option can "
- "not be used same time"))
- if parsed_args.no_gateway:
- body['subnet'].update({'gateway_ip': None})
- if parsed_args.gateway:
- body['subnet'].update({'gateway_ip': parsed_args.gateway})
+ body = {'network_id': _network_id}
+
+ if parsed_args.prefixlen:
+ body['prefixlen'] = parsed_args.prefixlen
+ ip_version = parsed_args.ip_version
+ if parsed_args.use_default_subnetpool:
+ body['use_default_subnetpool'] = True
+ if parsed_args.segment:
+ body['segment_id'] = neutronV20.find_resourceid_by_name_or_id(
+ self.get_client(), 'segment', parsed_args.segment)
+ if parsed_args.subnetpool:
+ if parsed_args.subnetpool == 'None':
+ _subnetpool_id = None
+ else:
+ _subnetpool = neutronV20.find_resource_by_name_or_id(
+ self.get_client(), 'subnetpool', parsed_args.subnetpool)
+ _subnetpool_id = _subnetpool['id']
+ # Now that we have the pool_id - let's just have a check on the
+ # ip version used in the pool
+ ip_version = _subnetpool['ip_version']
+ body['subnetpool_id'] = _subnetpool_id
+
+ # IP version needs to be set as IP version can be
+ # determined by subnetpool.
+ body['ip_version'] = ip_version
+
+ if parsed_args.cidr:
+ # With subnetpool, cidr is now optional for creating subnet.
+ cidr = parsed_args.cidr
+ body['cidr'] = cidr
+ unusable_cidr = '/32' if ip_version == 4 else '/128'
+ if cidr.endswith(unusable_cidr):
+ self.log.warning(_("An IPv%(ip)d subnet with a %(cidr)s CIDR "
+ "will have only one usable IP address so "
+ "the device attached to it will not have "
+ "any IP connectivity."),
+ {"ip": ip_version,
+ "cidr": unusable_cidr})
+
+ updatable_args2body(parsed_args, body, ip_version=ip_version)
if parsed_args.tenant_id:
- body['subnet'].update({'tenant_id': parsed_args.tenant_id})
- if parsed_args.name:
- body['subnet'].update({'name': parsed_args.name})
- if parsed_args.disable_dhcp:
- body['subnet'].update({'enable_dhcp': False})
- if parsed_args.allocation_pools:
- body['subnet']['allocation_pools'] = parsed_args.allocation_pools
- if parsed_args.host_routes:
- body['subnet']['host_routes'] = parsed_args.host_routes
- if parsed_args.dns_nameservers:
- body['subnet']['dns_nameservers'] = parsed_args.dns_nameservers
+ body['tenant_id'] = parsed_args.tenant_id
- return body
+ return {'subnet': body}
class DeleteSubnet(neutronV20.DeleteCommand):
"""Delete a given subnet."""
resource = 'subnet'
- log = logging.getLogger(__name__ + '.DeleteSubnet')
class UpdateSubnet(neutronV20.UpdateCommand):
"""Update subnet's information."""
resource = 'subnet'
- log = logging.getLogger(__name__ + '.UpdateSubnet')
+
+ def add_known_arguments(self, parser):
+ add_updatable_arguments(parser)
+
+ def args2body(self, parsed_args):
+ body = {}
+ updatable_args2body(parsed_args, body, for_create=False)
+ return {'subnet': body}
diff --git a/neutronclient/neutron/v2_0/subnetpool.py b/neutronclient/neutron/v2_0/subnetpool.py
new file mode 100644
index 000000000..fdefc8a47
--- /dev/null
+++ b/neutronclient/neutron/v2_0/subnetpool.py
@@ -0,0 +1,154 @@
+# 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.
+#
+
+from neutronclient._i18n import _
+from neutronclient.common import utils
+from neutronclient.neutron import v2_0 as neutronV20
+
+
+def _format_prefixes(subnetpool):
+ try:
+ return '\n'.join(pool for pool in subnetpool['prefixes'])
+ except (TypeError, KeyError):
+ return subnetpool['prefixes']
+
+
+def add_updatable_arguments(parser, for_create=False):
+ parser.add_argument(
+ '--description',
+ help=_('Description of subnetpool.'))
+ parser.add_argument(
+ '--min-prefixlen', type=int,
+ help=_('Subnetpool minimum prefix length.'))
+ parser.add_argument(
+ '--max-prefixlen', type=int,
+ help=_('Subnetpool maximum prefix length.'))
+ parser.add_argument(
+ '--default-prefixlen', type=int,
+ help=_('Subnetpool default prefix length.'))
+ parser.add_argument(
+ '--pool-prefix',
+ action='append', dest='prefixes',
+ required=for_create,
+ help=_('Subnetpool prefixes (This option can be repeated).'))
+ utils.add_boolean_argument(
+ parser, '--is-default',
+ help=_('Specify whether this should be the default subnetpool '
+ '(True meaning default).'))
+
+
+def updatable_args2body(parsed_args, body):
+ neutronV20.update_dict(parsed_args, body,
+ ['name', 'prefixes', 'default_prefixlen',
+ 'min_prefixlen', 'max_prefixlen', 'is_default',
+ 'description'])
+
+
+class ListSubnetPool(neutronV20.ListCommand):
+ """List subnetpools that belong to a given tenant."""
+
+ _formatters = {'prefixes': _format_prefixes, }
+ resource = 'subnetpool'
+ list_columns = ['id', 'name', 'prefixes',
+ 'default_prefixlen', 'address_scope_id', 'is_default']
+ pagination_support = True
+ sorting_support = True
+
+
+class ShowSubnetPool(neutronV20.ShowCommand):
+ """Show information of a given subnetpool."""
+
+ resource = 'subnetpool'
+
+
+class CreateSubnetPool(neutronV20.CreateCommand):
+ """Create a subnetpool for a given tenant."""
+
+ resource = 'subnetpool'
+
+ def add_known_arguments(self, parser):
+ add_updatable_arguments(parser, for_create=True)
+ parser.add_argument(
+ '--shared',
+ action='store_true',
+ help=_('Set the subnetpool as shared.'))
+ parser.add_argument(
+ 'name',
+ metavar='NAME',
+ help=_('Name of the subnetpool to be created.'))
+ parser.add_argument(
+ '--address-scope',
+ metavar='ADDRSCOPE',
+ help=_('ID or name of the address scope with which the subnetpool '
+ 'is associated. Prefixes must be unique across address '
+ 'scopes.'))
+
+ 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
+
+ # Parse and update for "address-scope" option
+ if parsed_args.address_scope:
+ _addrscope_id = neutronV20.find_resourceid_by_name_or_id(
+ self.get_client(), 'address_scope',
+ parsed_args.address_scope)
+ body['address_scope_id'] = _addrscope_id
+ return {'subnetpool': body}
+
+
+class DeleteSubnetPool(neutronV20.DeleteCommand):
+ """Delete a given subnetpool."""
+
+ resource = 'subnetpool'
+
+
+class UpdateSubnetPool(neutronV20.UpdateCommand):
+ """Update subnetpool's information."""
+
+ resource = 'subnetpool'
+
+ def add_known_arguments(self, parser):
+ add_updatable_arguments(parser)
+ parser.add_argument('--name',
+ help=_('Updated name of the subnetpool.'))
+ addrscope_args = parser.add_mutually_exclusive_group()
+ addrscope_args.add_argument('--address-scope',
+ metavar='ADDRSCOPE',
+ help=_('ID or name of the address scope '
+ 'with which the subnetpool is '
+ 'associated. Prefixes must be '
+ 'unique across address scopes.'))
+ addrscope_args.add_argument('--no-address-scope',
+ action='store_true',
+ help=_('Detach subnetpool from the '
+ 'address scope.'))
+
+ def args2body(self, parsed_args):
+ body = {}
+ updatable_args2body(parsed_args, body)
+
+ # Parse and update for "address-scope" option/s
+ if parsed_args.no_address_scope:
+ body['address_scope_id'] = None
+ elif parsed_args.address_scope:
+ _addrscope_id = neutronV20.find_resourceid_by_name_or_id(
+ self.get_client(), 'address_scope',
+ parsed_args.address_scope)
+ body['address_scope_id'] = _addrscope_id
+ return {'subnetpool': body}
diff --git a/neutronclient/neutron/v2_0/tag.py b/neutronclient/neutron/v2_0/tag.py
new file mode 100644
index 000000000..ff59b74ec
--- /dev/null
+++ b/neutronclient/neutron/v2_0/tag.py
@@ -0,0 +1,107 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# 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 exceptions
+from neutronclient.neutron import v2_0 as neutronv20
+
+
+# List of resources can be set tag
+TAG_RESOURCES = ['network', 'subnet', 'port', 'router', 'subnetpool']
+
+
+def _convert_resource_args(client, parsed_args):
+ resource_type = client.get_resource_plural(parsed_args.resource_type)
+ resource_id = neutronv20.find_resourceid_by_name_or_id(
+ client, parsed_args.resource_type, parsed_args.resource)
+ return resource_type, resource_id
+
+
+def _add_common_arguments(parser):
+ parser.add_argument('--resource-type',
+ choices=TAG_RESOURCES,
+ dest='resource_type',
+ required=True,
+ help=_('Resource Type.'))
+ parser.add_argument('--resource',
+ required=True,
+ help=_('Resource name or ID.'))
+
+
+class AddTag(neutronv20.NeutronCommand):
+ """Add a tag into the resource."""
+
+ def get_parser(self, prog_name):
+ parser = super(AddTag, self).get_parser(prog_name)
+ _add_common_arguments(parser)
+ parser.add_argument('--tag',
+ required=True,
+ help=_('Tag to be added.'))
+ return parser
+
+ 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)
+
+
+class ReplaceTag(neutronv20.NeutronCommand):
+ """Replace all tags on the resource."""
+
+ def get_parser(self, prog_name):
+ parser = super(ReplaceTag, self).get_parser(prog_name)
+ _add_common_arguments(parser)
+ parser.add_argument('--tag',
+ metavar='TAG',
+ action='append',
+ dest='tags',
+ required=True,
+ help=_('Tag (This option can be repeated).'))
+ return parser
+
+ def take_action(self, parsed_args):
+ client = self.get_client()
+ resource_type, resource_id = _convert_resource_args(client,
+ parsed_args)
+ body = {'tags': parsed_args.tags}
+ client.replace_tag(resource_type, resource_id, body)
+
+
+class RemoveTag(neutronv20.NeutronCommand):
+ """Remove a tag on the resource."""
+
+ def get_parser(self, prog_name):
+ parser = super(RemoveTag, self).get_parser(prog_name)
+ _add_common_arguments(parser)
+ tag_opt = parser.add_mutually_exclusive_group()
+ tag_opt.add_argument('--all',
+ action='store_true',
+ help=_('Remove all tags on the resource.'))
+ tag_opt.add_argument('--tag',
+ help=_('Tag to be removed.'))
+ return parser
+
+ def take_action(self, parsed_args):
+ if not parsed_args.all and not parsed_args.tag:
+ raise exceptions.CommandError(
+ _("--all or --tag must be specified"))
+ client = self.get_client()
+ resource_type, resource_id = _convert_resource_args(client,
+ parsed_args)
+ if parsed_args.all:
+ client.remove_tag_all(resource_type, resource_id)
+ else:
+ client.remove_tag(resource_type, resource_id, parsed_args.tag)
diff --git a/neutronclient/neutron/v2_0/vpn/endpoint_group.py b/neutronclient/neutron/v2_0/vpn/endpoint_group.py
new file mode 100644
index 000000000..a112b315e
--- /dev/null
+++ b/neutronclient/neutron/v2_0/vpn/endpoint_group.py
@@ -0,0 +1,100 @@
+# (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.
+#
+
+from neutronclient._i18n import _
+from neutronclient.neutron import v2_0 as neutronv20
+
+
+def add_known_endpoint_group_arguments(parser, is_create=True):
+ parser.add_argument(
+ '--name',
+ help=_('Set a name for the endpoint group.'))
+ parser.add_argument(
+ '--description',
+ help=_('Set a description for the endpoint group.'))
+ if is_create:
+ parser.add_argument(
+ '--type',
+ required=is_create,
+ help=_('Type of endpoints in group (e.g. subnet, cidr, vlan).'))
+ parser.add_argument(
+ '--value',
+ action='append', dest='endpoints',
+ required=is_create,
+ help=_('Endpoint(s) for the group. Must all be of the same type.'))
+
+
+class ListEndpointGroup(neutronv20.ListCommand):
+ """List VPN endpoint groups that belong to a given tenant."""
+
+ resource = 'endpoint_group'
+ list_columns = ['id', 'name', 'type', 'endpoints']
+ _formatters = {}
+ pagination_support = True
+ sorting_support = True
+
+
+class ShowEndpointGroup(neutronv20.ShowCommand):
+ """Show a specific VPN endpoint group."""
+
+ resource = 'endpoint_group'
+
+
+class CreateEndpointGroup(neutronv20.CreateCommand):
+ """Create a VPN endpoint group."""
+ resource = 'endpoint_group'
+
+ def add_known_arguments(self, parser):
+ add_known_endpoint_group_arguments(parser)
+
+ def subnet_name2id(self, endpoint):
+ return neutronv20.find_resourceid_by_name_or_id(self.get_client(),
+ 'subnet', endpoint)
+
+ def args2body(self, parsed_args):
+ if parsed_args.type == 'subnet':
+ endpoints = [self.subnet_name2id(ep)
+ for ep in parsed_args.endpoints]
+ else:
+ endpoints = parsed_args.endpoints
+
+ body = {'endpoints': endpoints}
+
+ neutronv20.update_dict(parsed_args, body,
+ ['name', 'description',
+ 'tenant_id', 'type'])
+ return {self.resource: body}
+
+
+class UpdateEndpointGroup(neutronv20.UpdateCommand):
+ """Update a given VPN endpoint group."""
+
+ resource = 'endpoint_group'
+
+ def add_known_arguments(self, parser):
+ add_known_endpoint_group_arguments(parser, is_create=False)
+
+ def args2body(self, parsed_args):
+ body = {}
+ neutronv20.update_dict(parsed_args, body,
+ ['name', 'description'])
+ return {self.resource: body}
+
+
+class DeleteEndpointGroup(neutronv20.DeleteCommand):
+ """Delete a given VPN endpoint group."""
+
+ resource = 'endpoint_group'
diff --git a/neutronclient/neutron/v2_0/vpn/ikepolicy.py b/neutronclient/neutron/v2_0/vpn/ikepolicy.py
index 2f15e5245..20a96aaea 100644
--- a/neutronclient/neutron/v2_0/vpn/ikepolicy.py
+++ b/neutronclient/neutron/v2_0/vpn/ikepolicy.py
@@ -13,22 +13,69 @@
# License for the specific language governing permissions and limitations
# under the License.
#
-# @author: Swaminathan Vasudevan, Hewlett-Packard.
-#
-import logging
+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
-from neutronclient.openstack.common.gettextutils import _
+
+
+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 IKEPolicies that belong to a tenant."""
+ """List IKE policies that belong to a tenant."""
resource = 'ikepolicy'
- log = logging.getLogger(__name__ + '.ListIKEPolicy')
list_columns = ['id', 'name', 'auth_algorithm',
'encryption_algorithm', 'ike_version', 'pfs']
_formatters = {}
@@ -37,101 +84,45 @@ class ListIKEPolicy(neutronv20.ListCommand):
class ShowIKEPolicy(neutronv20.ShowCommand):
- """Show information of a given IKEPolicy."""
+ """Show information of a given IKE policy."""
resource = 'ikepolicy'
- log = logging.getLogger(__name__ + '.ShowIKEPolicy')
+ help_resource = 'IKE policy'
class CreateIKEPolicy(neutronv20.CreateCommand):
- """Create an IKEPolicy."""
+ """Create an IKE policy."""
resource = 'ikepolicy'
- log = logging.getLogger(__name__ + '.CreateIKEPolicy')
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'],
- help=_('Authentication algorithm in lowercase. '
- 'default:sha1'))
- parser.add_argument(
- '--encryption-algorithm',
- default='aes-128', choices=['3des',
- 'aes-128',
- 'aes-192',
- 'aes-256'],
- 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,
- help=vpn_utils.lifetime_help("IKE"))
parser.add_argument(
'name', metavar='NAME',
- help=_('Name of the IKE Policy'))
+ help=_('Name of the IKE policy.'))
+ add_common_args(parser)
def args2body(self, parsed_args):
-
- body = {'ikepolicy': {
- 'auth_algorithm': parsed_args.auth_algorithm,
- 'encryption_algorithm': parsed_args.encryption_algorithm,
- 'phase1_negotiation_mode': parsed_args.phase1_negotiation_mode,
- 'ike_version': parsed_args.ike_version,
- 'pfs': parsed_args.pfs,
- }, }
- if parsed_args.name:
- body['ikepolicy'].update({'name': parsed_args.name})
- if parsed_args.description:
- body['ikepolicy'].update({'description': parsed_args.description})
- if parsed_args.tenant_id:
- body['ikepolicy'].update({'tenant_id': parsed_args.tenant_id})
- if parsed_args.lifetime:
- vpn_utils.validate_lifetime_dict(parsed_args.lifetime)
- body['ikepolicy'].update({'lifetime': parsed_args.lifetime})
- return body
+ return {'ikepolicy': parse_common_args2body(parsed_args, body={})}
class UpdateIKEPolicy(neutronv20.UpdateCommand):
- """Update a given IKE Policy."""
+ """Update a given IKE policy."""
resource = 'ikepolicy'
- log = logging.getLogger(__name__ + '.UpdateIKEPolicy')
+ help_resource = 'IKE policy'
def add_known_arguments(self, parser):
parser.add_argument(
- '--lifetime',
- metavar="units=UNITS,value=VALUE",
- type=utils.str2dict,
- 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 = {'ikepolicy': {
- }, }
- if parsed_args.lifetime:
- vpn_utils.validate_lifetime_dict(parsed_args.lifetime)
- body['ikepolicy'].update({'lifetime': parsed_args.lifetime})
- return body
+ return {'ikepolicy': parse_common_args2body(parsed_args, body={})}
class DeleteIKEPolicy(neutronv20.DeleteCommand):
- """Delete a given IKE Policy."""
+ """Delete a given IKE policy."""
resource = 'ikepolicy'
- log = logging.getLogger(__name__ + '.DeleteIKEPolicy')
+ help_resource = 'IKE policy'
diff --git a/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py b/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py
index 35ca19e65..03a635f6e 100644
--- a/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py
+++ b/neutronclient/neutron/v2_0/vpn/ipsec_site_connection.py
@@ -13,106 +13,153 @@
# License for the specific language governing permissions and limitations
# under the License.
#
-# @author: Swaminathan Vasudevan, Hewlett-Packard.
-#
-import logging
+import argparse
+
+from oslo_serialization import jsonutils
+from neutronclient._i18n import _
from neutronclient.common import exceptions
from neutronclient.common import utils
from neutronclient.neutron import v2_0 as neutronv20
from neutronclient.neutron.v2_0.vpn import utils as vpn_utils
-from neutronclient.openstack.common.gettextutils import _
def _format_peer_cidrs(ipsec_site_connection):
try:
- return '\n'.join([utils.dumps(cidrs) for cidrs in
+ return '\n'.join([jsonutils.dumps(cidrs) for cidrs in
ipsec_site_connection['peer_cidrs']])
- except Exception:
+ except (TypeError, KeyError):
return ''
class ListIPsecSiteConnection(neutronv20.ListCommand):
- """List IPsecSiteConnections that belong to a given tenant."""
+ """List IPsec site connections that belong to a given tenant."""
resource = 'ipsec_site_connection'
- log = logging.getLogger(__name__ + '.ListIPsecSiteConnection')
_formatters = {'peer_cidrs': _format_peer_cidrs}
list_columns = [
- 'id', 'name', 'peer_address', 'peer_cidrs', 'route_mode',
- 'auth_mode', 'status']
+ 'id', 'name', 'peer_address', 'auth_mode', 'status']
pagination_support = True
sorting_support = True
class ShowIPsecSiteConnection(neutronv20.ShowCommand):
- """Show information of a given IPsecSiteConnection."""
+ """Show information of a given IPsec site connection."""
resource = 'ipsec_site_connection'
- log = logging.getLogger(__name__ + '.ShowIPsecSiteConnection')
+ help_resource = 'IPsec site connection'
-class CreateIPsecSiteConnection(neutronv20.CreateCommand):
- """Create an IPsecSiteConnection."""
- resource = 'ipsec_site_connection'
- log = logging.getLogger(__name__ + '.CreateIPsecSiteConnection')
+class IPsecSiteConnectionMixin(object):
- def add_known_arguments(self, parser):
- parser.add_argument(
- '--admin-state-down',
- default=True, action='store_false',
- help=_('Set admin state up to false'))
+ def add_known_arguments(self, parser, is_create=True):
parser.add_argument(
'--name',
- help=_('Set friendly name for the connection'))
+ help=_('Set friendly 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'))
+ help=_('Set a description for the connection.'))
parser.add_argument(
'--dpd',
metavar="action=ACTION,interval=INTERVAL,timeout=TIMEOUT",
- type=utils.str2dict,
- help=vpn_utils.dpd_help("IPsec Connection"))
+ type=utils.str2dict_type(
+ optional_keys=['action', 'interval', 'timeout']),
+ help=vpn_utils.dpd_help("IPsec connection."))
parser.add_argument(
- '--vpnservice-id', metavar='VPNSERVICE',
- required=True,
- help=_('VPNService instance id associated with this connection'))
+ '--local-ep-group',
+ help=_('Local endpoint group ID/name with subnet(s) for '
+ 'IPSec connection.'))
parser.add_argument(
- '--ikepolicy-id', metavar='IKEPOLICY',
- required=True,
- help=_('IKEPolicy id associated with this connection'))
+ '--peer-ep-group',
+ help=_('Peer endpoint group ID/name with CIDR(s) for '
+ 'IPSec connection.'))
parser.add_argument(
- '--ipsecpolicy-id', metavar='IPSECPOLICY',
- required=True,
- help=_('IPsecPolicy id associated with this connection'))
+ '--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=True,
+ required=is_create,
help=_('Peer gateway public IPv4/IPv6 address or FQDN.'))
parser.add_argument(
- '--peer-id',
+ '--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."""
+
+ if body is None:
+ body = {}
+ if parsed_args.dpd:
+ vpn_utils.validate_dpd_dict(parsed_args.dpd)
+ body['dpd'] = parsed_args.dpd
+ if parsed_args.local_ep_group:
+ _local_epg = neutronv20.find_resourceid_by_name_or_id(
+ self.get_client(), 'endpoint_group',
+ parsed_args.local_ep_group)
+ body['local_ep_group_id'] = _local_epg
+ if parsed_args.peer_ep_group:
+ _peer_epg = neutronv20.find_resourceid_by_name_or_id(
+ 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}
+
+
+class CreateIPsecSiteConnection(IPsecSiteConnectionMixin,
+ neutronv20.CreateCommand):
+ """Create an IPsec site connection."""
+ resource = 'ipsec_site_connection'
+
+ def add_known_arguments(self, parser):
+ parser.add_argument(
+ '--admin-state-down',
+ default=True, action='store_false',
+ help=_('Set admin state up to false.'))
+ parser.add_argument(
+ '--vpnservice-id', metavar='VPNSERVICE',
required=True,
- help=_('Peer router identity for authentication. Can be '
- 'IPv4/IPv6 address, e-mail address, key id, or FQDN.'))
+ help=_('VPN service instance ID associated with this connection.'))
parser.add_argument(
- '--peer-cidr',
- action='append', dest='peer_cidrs',
+ '--ikepolicy-id', metavar='IKEPOLICY',
required=True,
- help=_('Remote subnet(s) in CIDR format'))
+ help=_('IKE policy ID associated with this connection.'))
parser.add_argument(
- '--psk',
+ '--ipsecpolicy-id', metavar='IPSECPOLICY',
required=True,
- help=_('Pre-Shared Key string'))
+ help=_('IPsec policy ID associated with this connection.'))
+ super(CreateIPsecSiteConnection, self).add_known_arguments(parser)
def args2body(self, parsed_args):
_vpnservice_id = neutronv20.find_resourceid_by_name_or_id(
@@ -124,69 +171,50 @@ 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)
- body = {'ipsec_site_connection': {
+ body = {
'vpnservice_id': _vpnservice_id,
'ikepolicy_id': _ikepolicy_id,
'ipsecpolicy_id': _ipsecpolicy_id,
- 'peer_address': parsed_args.peer_address,
- 'peer_id': parsed_args.peer_id,
- 'mtu': parsed_args.mtu,
- 'initiator': parsed_args.initiator,
- 'psk': parsed_args.psk,
'admin_state_up': parsed_args.admin_state_down,
- }, }
- if parsed_args.name:
- body['ipsec_site_connection'].update(
- {'name': parsed_args.name}
- )
- if parsed_args.description:
- body['ipsec_site_connection'].update(
- {'description': parsed_args.description}
- )
+ }
if parsed_args.tenant_id:
- body['ipsec_site_connection'].update(
- {'tenant_id': parsed_args.tenant_id}
- )
- if parsed_args.dpd:
- vpn_utils.validate_dpd_dict(parsed_args.dpd)
- body['ipsec_site_connection'].update({'dpd': parsed_args.dpd})
- if parsed_args.peer_cidrs:
- body['ipsec_site_connection'][
- 'peer_cidrs'] = parsed_args.peer_cidrs
+ body['tenant_id'] = parsed_args.tenant_id
- return body
+ 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)
-class UpdateIPsecSiteConnection(neutronv20.UpdateCommand):
- """Update a given IPsecSiteConnection."""
+class UpdateIPsecSiteConnection(IPsecSiteConnectionMixin,
+ neutronv20.UpdateCommand):
+ """Update a given IPsec site connection."""
resource = 'ipsec_site_connection'
- log = logging.getLogger(__name__ + '.UpdateIPsecSiteConnection')
+ help_resource = 'IPsec site connection'
def add_known_arguments(self, parser):
-
- parser.add_argument(
- '--dpd',
- metavar="action=ACTION,interval=INTERVAL,timeout=TIMEOUT",
- type=utils.str2dict,
- help=vpn_utils.dpd_help("IPsec Connection"))
+ 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 = {'ipsec_site_connection': {
- }, }
-
- if parsed_args.dpd:
- vpn_utils.validate_dpd_dict(parsed_args.dpd)
- body['ipsec_site_connection'].update({'dpd': parsed_args.dpd})
- return body
+ 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 IPsecSiteConnection."""
+ """Delete a given IPsec site connection."""
resource = 'ipsec_site_connection'
- log = logging.getLogger(__name__ + '.DeleteIPsecSiteConnection')
+ help_resource = 'IPsec site connection'
diff --git a/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py b/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py
index a8a43238c..ea70155b7 100644
--- a/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py
+++ b/neutronclient/neutron/v2_0/vpn/ipsecpolicy.py
@@ -13,21 +13,69 @@
# License for the specific language governing permissions and limitations
# under the License.
#
-# @author: Swaminathan Vasudevan, Hewlett-Packard.
-import logging
+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
-from neutronclient.openstack.common.gettextutils import _
+
+
+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 ipsecpolicies that belongs to a given tenant connection."""
+ """List IPsec policies that belong to a given tenant connection."""
resource = 'ipsecpolicy'
- log = logging.getLogger(__name__ + '.ListIPsecPolicy')
list_columns = ['id', 'name', 'auth_algorithm',
'encryption_algorithm', 'pfs']
_formatters = {}
@@ -36,102 +84,46 @@ class ListIPsecPolicy(neutronv20.ListCommand):
class ShowIPsecPolicy(neutronv20.ShowCommand):
- """Show information of a given ipsecpolicy."""
+ """Show information of a given IPsec policy."""
resource = 'ipsecpolicy'
- log = logging.getLogger(__name__ + '.ShowIPsecPolicy')
+ help_resource = 'IPsec policy'
class CreateIPsecPolicy(neutronv20.CreateCommand):
- """Create an ipsecpolicy."""
+ """Create an IPsec policy."""
resource = 'ipsecpolicy'
- log = logging.getLogger(__name__ + '.CreateIPsecPolicy')
+ help_resource = 'IPsec policy'
def add_known_arguments(self, parser):
- parser.add_argument(
- '--description',
- help=_('Description of the IPsecPolicy'))
- 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'],
- help=_('Authentication algorithm in lowercase, default:sha1'))
- parser.add_argument(
- '--encryption-algorithm',
- default='aes-128', choices=['3des',
- 'aes-128',
- 'aes-192',
- 'aes-256'],
- 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,
- help=vpn_utils.lifetime_help("IPsec"))
parser.add_argument(
'name', metavar='NAME',
- help=_('Name of the IPsecPolicy'))
+ help=_('Name of the IPsec policy.'))
+ add_common_args(parser)
def args2body(self, parsed_args):
-
- body = {'ipsecpolicy': {
- 'auth_algorithm': parsed_args.auth_algorithm,
- 'encryption_algorithm': parsed_args.encryption_algorithm,
- 'encapsulation_mode': parsed_args.encapsulation_mode,
- 'transform_protocol': parsed_args.transform_protocol,
- 'pfs': parsed_args.pfs,
- }, }
- if parsed_args.name:
- body['ipsecpolicy'].update({'name': parsed_args.name})
- if parsed_args.description:
- body['ipsecpolicy'].update(
- {'description': parsed_args.description}
- )
- if parsed_args.tenant_id:
- body['ipsecpolicy'].update({'tenant_id': parsed_args.tenant_id})
- if parsed_args.lifetime:
- vpn_utils.validate_lifetime_dict(parsed_args.lifetime)
- body['ipsecpolicy'].update({'lifetime': parsed_args.lifetime})
- return body
+ return {'ipsecpolicy': parse_common_args2body(parsed_args, body={})}
class UpdateIPsecPolicy(neutronv20.UpdateCommand):
- """Update a given ipsec policy."""
+ """Update a given IPsec policy."""
resource = 'ipsecpolicy'
- log = logging.getLogger(__name__ + '.UpdateIPsecPolicy')
+ help_resource = 'IPsec policy'
def add_known_arguments(self, parser):
parser.add_argument(
- '--lifetime',
- metavar="units=UNITS,value=VALUE",
- type=utils.str2dict,
- 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 = {'ipsecpolicy': {
- }, }
- if parsed_args.lifetime:
- vpn_utils.validate_lifetime_dict(parsed_args.lifetime)
- body['ipsecpolicy'].update({'lifetime': parsed_args.lifetime})
- return body
+ return {'ipsecpolicy': parse_common_args2body(parsed_args, body={})}
class DeleteIPsecPolicy(neutronv20.DeleteCommand):
- """Delete a given ipsecpolicy."""
+ """Delete a given IPsec policy."""
resource = 'ipsecpolicy'
- log = logging.getLogger(__name__ + '.DeleteIPsecPolicy')
+ help_resource = 'IPsec policy'
diff --git a/neutronclient/neutron/v2_0/vpn/utils.py b/neutronclient/neutron/v2_0/vpn/utils.py
index fa1091161..65d9c7c4b 100644
--- a/neutronclient/neutron/v2_0/vpn/utils.py
+++ b/neutronclient/neutron/v2_0/vpn/utils.py
@@ -13,15 +13,13 @@
# License for the specific language governing permissions and limitations
# under the License.
#
-# @author: Swaminathan Vasudevan, Hewlett-Packard.
-#
"""VPN Utilities and helper functions."""
+from neutronclient._i18n import _
from neutronclient.common import exceptions
-from neutronclient.openstack.common.gettextutils import _
dpd_supported_actions = ['hold', 'clear', 'restart',
'restart-by-peer', 'disabled']
@@ -37,14 +35,14 @@ def validate_dpd_dict(dpd_dict):
message = _(
"DPD Dictionary KeyError: "
"Reason-Invalid DPD key : "
- "'%(key)s' not in %(supported_key)s ") % {
+ "'%(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)s' not in %(supported_action)s") % {
'key_value': value,
'supported_action': dpd_supported_actions}
raise exceptions.CommandError(message)
@@ -56,7 +54,7 @@ def validate_dpd_dict(dpd_dict):
message = _(
"DPD Dictionary ValueError: "
"Reason-Invalid positive integer value: "
- "'%(key)s' = %(value)s ") % {
+ "'%(key)s' = %(value)s") % {
'key': key, 'value': value}
raise exceptions.CommandError(message)
else:
@@ -71,14 +69,14 @@ def validate_lifetime_dict(lifetime_dict):
message = _(
"Lifetime Dictionary KeyError: "
"Reason-Invalid unit key : "
- "'%(key)s' not in %(supported_key)s ") % {
+ "'%(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)s' not in %(supported_units)s") % {
'key_value': key, 'supported_units': lifetime_units}
raise exceptions.CommandError(message)
if key == 'value':
@@ -89,7 +87,7 @@ def validate_lifetime_dict(lifetime_dict):
message = _(
"Lifetime Dictionary ValueError: "
"Reason-Invalid value should be at least 60:"
- "'%(key_value)s' = %(value)s ") % {
+ "'%(key_value)s' = %(value)s") % {
'key_value': key, 'value': value}
raise exceptions.CommandError(message)
else:
@@ -98,14 +96,14 @@ def validate_lifetime_dict(lifetime_dict):
def lifetime_help(policy):
- lifetime = _("%s Lifetime Attributes."
- "'units'-seconds,default:seconds. "
+ 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. "
+ 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. "
diff --git a/neutronclient/neutron/v2_0/vpn/vpnservice.py b/neutronclient/neutron/v2_0/vpn/vpnservice.py
index 87bec2fee..dae531408 100644
--- a/neutronclient/neutron/v2_0/vpn/vpnservice.py
+++ b/neutronclient/neutron/v2_0/vpn/vpnservice.py
@@ -13,20 +13,30 @@
# License for the specific language governing permissions and limitations
# under the License.
#
-# @author: Swaminathan Vasudevan, Hewlett-Packard.
-#
-
-import logging
+from neutronclient._i18n import _
+from neutronclient.common import utils
from neutronclient.neutron import v2_0 as neutronv20
-from neutronclient.openstack.common.gettextutils import _
+
+
+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 VPNService configurations that belong to a given tenant."""
+ """List VPN service configurations that belong to a given tenant."""
resource = 'vpnservice'
- log = logging.getLogger(__name__ + '.ListVPNService')
list_columns = [
'id', 'name', 'router_id', 'status'
]
@@ -36,62 +46,72 @@ class ListVPNService(neutronv20.ListCommand):
class ShowVPNService(neutronv20.ShowCommand):
- """Show information of a given VPNService."""
+ """Show information of a given VPN service."""
resource = 'vpnservice'
- log = logging.getLogger(__name__ + '.ShowVPNService')
+ help_resource = 'VPN service'
class CreateVPNService(neutronv20.CreateCommand):
- """Create a VPNService."""
+ """Create a VPN service."""
resource = 'vpnservice'
- log = logging.getLogger(__name__ + '.CreateVPNService')
def add_known_arguments(self, parser):
parser.add_argument(
'--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 vpnservice'))
- parser.add_argument(
- '--description',
- help=_('Set a description for the vpnservice'))
+ help=_('Set admin state up to false.'))
parser.add_argument(
'router', metavar='ROUTER',
- help=_('Router unique identifier for the vpnservice'))
+ help=_('Router unique identifier for the VPN service.'))
parser.add_argument(
- 'subnet', metavar='SUBNET',
- help=_('Subnet unique identifier for the vpnservice deployment'))
+ 'subnet', nargs='?', metavar='SUBNET',
+ help=_('[DEPRECATED in Mitaka] Unique identifier for the local '
+ 'private subnet.'))
+ add_common_args(parser)
def args2body(self, parsed_args):
- _subnet_id = neutronv20.find_resourceid_by_name_or_id(
- self.get_client(), 'subnet',
- parsed_args.subnet)
+ if parsed_args.subnet:
+ _subnet_id = neutronv20.find_resourceid_by_name_or_id(
+ self.get_client(), 'subnet', parsed_args.subnet)
+ else:
+ _subnet_id = None
_router_id = neutronv20.find_resourceid_by_name_or_id(
self.get_client(), 'router',
parsed_args.router)
- body = {self.resource: {'subnet_id': _subnet_id,
- 'router_id': _router_id,
- 'admin_state_up': parsed_args.admin_state}, }
- neutronv20.update_dict(parsed_args, body[self.resource],
- ['name', 'description',
- 'tenant_id'])
-
- return body
+ body = {'subnet_id': _subnet_id,
+ 'router_id': _router_id,
+ 'admin_state_up': parsed_args.admin_state}
+ neutronv20.update_dict(parsed_args, body,
+ ['tenant_id'])
+ common_args2body(parsed_args, body)
+ return {self.resource: body}
class UpdateVPNService(neutronv20.UpdateCommand):
- """Update a given VPNService."""
+ """Update a given VPN service."""
resource = 'vpnservice'
- log = logging.getLogger(__name__ + '.UpdateVPNService')
+ 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 VPNService."""
+ """Delete a given VPN service."""
resource = 'vpnservice'
- log = logging.getLogger(__name__ + '.DeleteVPNService')
+ help_resource = 'VPN service'
diff --git a/neutronclient/openstack/common/gettextutils.py b/neutronclient/openstack/common/gettextutils.py
deleted file mode 100644
index 25432d677..000000000
--- a/neutronclient/openstack/common/gettextutils.py
+++ /dev/null
@@ -1,320 +0,0 @@
-# Copyright 2012 Red Hat, Inc.
-# Copyright 2013 IBM Corp.
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-"""
-gettext for openstack-common modules.
-
-Usual usage in an openstack.common module:
-
- from neutronclient.openstack.common.gettextutils import _
-"""
-
-import copy
-import gettext
-import logging
-import os
-import re
-import UserString
-
-from babel import localedata
-import six
-
-_localedir = os.environ.get('neutronclient'.upper() + '_LOCALEDIR')
-_t = gettext.translation('neutronclient', localedir=_localedir, fallback=True)
-
-_AVAILABLE_LANGUAGES = {}
-USE_LAZY = False
-
-
-def enable_lazy():
- """Convenience function for configuring _() to use lazy gettext
-
- Call this at the start of execution to enable the gettextutils._
- function to use lazy gettext functionality. This is useful if
- your project is importing _ directly instead of using the
- gettextutils.install() way of importing the _ function.
- """
- global USE_LAZY
- USE_LAZY = True
-
-
-def _(msg):
- if USE_LAZY:
- return Message(msg, 'neutronclient')
- else:
- return _t.ugettext(msg)
-
-
-def install(domain, lazy=False):
- """Install a _() function using the given translation domain.
-
- Given a translation domain, install a _() function using gettext's
- install() function.
-
- The main difference from gettext.install() is that we allow
- overriding the default localedir (e.g. /usr/share/locale) using
- a translation-domain-specific environment variable (e.g.
- NOVA_LOCALEDIR).
-
- :param domain: the translation domain
- :param lazy: indicates whether or not to install the lazy _() function.
- The lazy _() introduces a way to do deferred translation
- of messages by installing a _ that builds Message objects,
- instead of strings, which can then be lazily translated into
- any available locale.
- """
- if lazy:
- # NOTE(mrodden): Lazy gettext functionality.
- #
- # The following introduces a deferred way to do translations on
- # messages in OpenStack. We override the standard _() function
- # and % (format string) operation to build Message objects that can
- # later be translated when we have more information.
- #
- # Also included below is an example LocaleHandler that translates
- # Messages to an associated locale, effectively allowing many logs,
- # each with their own locale.
-
- def _lazy_gettext(msg):
- """Create and return a Message object.
-
- Lazy gettext function for a given domain, it is a factory method
- for a project/module to get a lazy gettext function for its own
- translation domain (i.e. nova, glance, cinder, etc.)
-
- Message encapsulates a string so that we can translate
- it later when needed.
- """
- return Message(msg, domain)
-
- import __builtin__
- __builtin__.__dict__['_'] = _lazy_gettext
- else:
- localedir = '%s_LOCALEDIR' % domain.upper()
- gettext.install(domain,
- localedir=os.environ.get(localedir),
- unicode=True)
-
-
-class Message(UserString.UserString, object):
- """Class used to encapsulate translatable messages."""
- def __init__(self, msg, domain):
- # _msg is the gettext msgid and should never change
- self._msg = msg
- self._left_extra_msg = ''
- self._right_extra_msg = ''
- self.params = None
- self.locale = None
- self.domain = domain
-
- @property
- def data(self):
- # NOTE(mrodden): this should always resolve to a unicode string
- # that best represents the state of the message currently
-
- localedir = os.environ.get(self.domain.upper() + '_LOCALEDIR')
- if self.locale:
- lang = gettext.translation(self.domain,
- localedir=localedir,
- languages=[self.locale],
- fallback=True)
- else:
- # use system locale for translations
- lang = gettext.translation(self.domain,
- localedir=localedir,
- fallback=True)
-
- full_msg = (self._left_extra_msg +
- lang.ugettext(self._msg) +
- self._right_extra_msg)
-
- if self.params is not None:
- full_msg = full_msg % self.params
-
- return six.text_type(full_msg)
-
- def _save_dictionary_parameter(self, dict_param):
- full_msg = self.data
- # look for %(blah) fields in string;
- # ignore %% and deal with the
- # case where % is first character on the line
- keys = re.findall('(?:[^%]|^)?%\((\w*)\)[a-z]', full_msg)
-
- # if we don't find any %(blah) blocks but have a %s
- if not keys and re.findall('(?:[^%]|^)%[a-z]', full_msg):
- # apparently the full dictionary is the parameter
- params = copy.deepcopy(dict_param)
- else:
- params = {}
- for key in keys:
- try:
- params[key] = copy.deepcopy(dict_param[key])
- except TypeError:
- # cast uncopyable thing to unicode string
- params[key] = unicode(dict_param[key])
-
- return params
-
- def _save_parameters(self, other):
- # we check for None later to see if
- # we actually have parameters to inject,
- # so encapsulate if our parameter is actually None
- if other is None:
- self.params = (other, )
- elif isinstance(other, dict):
- self.params = self._save_dictionary_parameter(other)
- else:
- # fallback to casting to unicode,
- # this will handle the problematic python code-like
- # objects that cannot be deep-copied
- try:
- self.params = copy.deepcopy(other)
- except TypeError:
- self.params = unicode(other)
-
- return self
-
- # overrides to be more string-like
- def __unicode__(self):
- return self.data
-
- def __str__(self):
- return self.data.encode('utf-8')
-
- def __getstate__(self):
- to_copy = ['_msg', '_right_extra_msg', '_left_extra_msg',
- 'domain', 'params', 'locale']
- new_dict = self.__dict__.fromkeys(to_copy)
- for attr in to_copy:
- new_dict[attr] = copy.deepcopy(self.__dict__[attr])
-
- return new_dict
-
- def __setstate__(self, state):
- for (k, v) in state.items():
- setattr(self, k, v)
-
- # operator overloads
- def __add__(self, other):
- copied = copy.deepcopy(self)
- copied._right_extra_msg += other.__str__()
- return copied
-
- def __radd__(self, other):
- copied = copy.deepcopy(self)
- copied._left_extra_msg += other.__str__()
- return copied
-
- def __mod__(self, other):
- # do a format string to catch and raise
- # any possible KeyErrors from missing parameters
- self.data % other
- copied = copy.deepcopy(self)
- return copied._save_parameters(other)
-
- def __mul__(self, other):
- return self.data * other
-
- def __rmul__(self, other):
- return other * self.data
-
- def __getitem__(self, key):
- return self.data[key]
-
- def __getslice__(self, start, end):
- return self.data.__getslice__(start, end)
-
- def __getattribute__(self, name):
- # NOTE(mrodden): handle lossy operations that we can't deal with yet
- # These override the UserString implementation, since UserString
- # uses our __class__ attribute to try and build a new message
- # after running the inner data string through the operation.
- # At that point, we have lost the gettext message id and can just
- # safely resolve to a string instead.
- ops = ['capitalize', 'center', 'decode', 'encode',
- 'expandtabs', 'ljust', 'lstrip', 'replace', 'rjust', 'rstrip',
- 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
- if name in ops:
- return getattr(self.data, name)
- else:
- return UserString.UserString.__getattribute__(self, name)
-
-
-def get_available_languages(domain):
- """Lists the available languages for the given translation domain.
-
- :param domain: the domain to get languages for
- """
- if domain in _AVAILABLE_LANGUAGES:
- return copy.copy(_AVAILABLE_LANGUAGES[domain])
-
- localedir = '%s_LOCALEDIR' % domain.upper()
- find = lambda x: gettext.find(domain,
- localedir=os.environ.get(localedir),
- languages=[x])
-
- # NOTE(mrodden): en_US should always be available (and first in case
- # order matters) since our in-line message strings are en_US
- language_list = ['en_US']
- # NOTE(luisg): Babel <1.0 used a function called list(), which was
- # renamed to locale_identifiers() in >=1.0, the requirements master list
- # requires >=0.9.6, uncapped, so defensively work with both. We can remove
- # this check when the master list updates to >=1.0, and all projects udpate
- list_identifiers = (getattr(localedata, 'list', None) or
- getattr(localedata, 'locale_identifiers'))
- locale_identifiers = list_identifiers()
- for i in locale_identifiers:
- if find(i) is not None:
- language_list.append(i)
- _AVAILABLE_LANGUAGES[domain] = language_list
- return copy.copy(language_list)
-
-
-def get_localized_message(message, user_locale):
- """Gets a localized version of the given message in the given locale."""
- if isinstance(message, Message):
- if user_locale:
- message.locale = user_locale
- return unicode(message)
- else:
- return message
-
-
-class LocaleHandler(logging.Handler):
- """Handler that can have a locale associated to translate Messages.
-
- A quick example of how to utilize the Message class above.
- LocaleHandler takes a locale and a target logging.Handler object
- to forward LogRecord objects to after translating the internal Message.
- """
-
- def __init__(self, locale, target):
- """Initialize a LocaleHandler
-
- :param locale: locale to use for translating messages
- :param target: logging.Handler object to forward
- LogRecord objects to after translation
- """
- logging.Handler.__init__(self)
- self.locale = locale
- self.target = target
-
- def emit(self, record):
- if isinstance(record.msg, Message):
- # set the locale and resolve to a string
- record.msg.locale = self.locale
-
- self.target.emit(record)
diff --git a/neutronclient/openstack/common/importutils.py b/neutronclient/openstack/common/importutils.py
deleted file mode 100644
index 7ab07b88e..000000000
--- a/neutronclient/openstack/common/importutils.py
+++ /dev/null
@@ -1,67 +0,0 @@
-# Copyright 2011 OpenStack Foundation.
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-"""
-Import related utilities and helper functions.
-"""
-
-import sys
-import traceback
-from neutronclient.openstack.common.gettextutils import _
-
-
-def import_class(import_str):
- """Returns a class from a string including module and class."""
- mod_str, _sep, class_str = import_str.rpartition('.')
- try:
- __import__(mod_str)
- return getattr(sys.modules[mod_str], class_str)
- except (ValueError, AttributeError):
- raise ImportError(_('Class %s cannot be found (%s)') %
- (class_str,
- traceback.format_exception(*sys.exc_info())))
-
-
-def import_object(import_str, *args, **kwargs):
- """Import a class and return an instance of it."""
- return import_class(import_str)(*args, **kwargs)
-
-
-def import_object_ns(name_space, import_str, *args, **kwargs):
- """Tries to import object from default namespace.
-
- Imports a class and return an instance of it, first by trying
- to find the class in a default namespace, then failing back to
- a full path if not found in the default namespace.
- """
- import_value = "%s.%s" % (name_space, import_str)
- try:
- return import_class(import_value)(*args, **kwargs)
- except ImportError:
- return import_class(import_str)(*args, **kwargs)
-
-
-def import_module(import_str):
- """Import a module."""
- __import__(import_str)
- return sys.modules[import_str]
-
-
-def try_import(import_str, default=None):
- """Try to import a module and if it fails return default."""
- try:
- return import_module(import_str)
- except ImportError:
- return default
diff --git a/neutronclient/openstack/common/jsonutils.py b/neutronclient/openstack/common/jsonutils.py
deleted file mode 100644
index 2930dc33b..000000000
--- a/neutronclient/openstack/common/jsonutils.py
+++ /dev/null
@@ -1,171 +0,0 @@
-# Copyright 2010 United States Government as represented by the
-# Administrator of the National Aeronautics and Space Administration.
-# Copyright 2011 Justin Santa Barbara
-# 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.
-
-'''
-JSON related utilities.
-
-This module provides a few things:
-
- 1) A handy function for getting an object down to something that can be
- JSON serialized. See to_primitive().
-
- 2) Wrappers around loads() and dumps(). The dumps() wrapper will
- automatically use to_primitive() for you if needed.
-
- 3) This sets up anyjson to use the loads() and dumps() wrappers if anyjson
- is available.
-'''
-
-
-import datetime
-import functools
-import inspect
-import itertools
-import json
-import types
-import xmlrpclib
-
-import six
-
-from neutronclient.openstack.common import importutils
-from neutronclient.openstack.common import timeutils
-
-netaddr = importutils.try_import("netaddr")
-
-_nasty_type_tests = [inspect.ismodule, inspect.isclass, inspect.ismethod,
- inspect.isfunction, inspect.isgeneratorfunction,
- inspect.isgenerator, inspect.istraceback, inspect.isframe,
- inspect.iscode, inspect.isbuiltin, inspect.isroutine,
- inspect.isabstract]
-
-_simple_types = (types.NoneType, int, basestring, bool, float, long)
-
-
-def to_primitive(value, convert_instances=False, convert_datetime=True,
- level=0, max_depth=3):
- """Convert a complex object into primitives.
-
- Handy for JSON serialization. We can optionally handle instances,
- but since this is a recursive function, we could have cyclical
- data structures.
-
- To handle cyclical data structures we could track the actual objects
- visited in a set, but not all objects are hashable. Instead we just
- track the depth of the object inspections and don't go too deep.
-
- Therefore, convert_instances=True is lossy ... be aware.
-
- """
- # handle obvious types first - order of basic types determined by running
- # full tests on nova project, resulting in the following counts:
- # 572754
- # 460353
- # 379632
- # 274610
- # 199918
- # 114200
- # 51817
- # 26164
- # 6491
- # 283
- # 19
- if isinstance(value, _simple_types):
- return value
-
- if isinstance(value, datetime.datetime):
- if convert_datetime:
- return timeutils.strtime(value)
- else:
- return value
-
- # value of itertools.count doesn't get caught by nasty_type_tests
- # and results in infinite loop when list(value) is called.
- if type(value) == itertools.count:
- return six.text_type(value)
-
- # FIXME(vish): Workaround for LP bug 852095. Without this workaround,
- # tests that raise an exception in a mocked method that
- # has a @wrap_exception with a notifier will fail. If
- # we up the dependency to 0.5.4 (when it is released) we
- # can remove this workaround.
- if getattr(value, '__module__', None) == 'mox':
- return 'mock'
-
- if level > max_depth:
- return '?'
-
- # The try block may not be necessary after the class check above,
- # but just in case ...
- try:
- recursive = functools.partial(to_primitive,
- convert_instances=convert_instances,
- convert_datetime=convert_datetime,
- level=level,
- max_depth=max_depth)
- if isinstance(value, dict):
- return dict((k, recursive(v)) for k, v in value.iteritems())
- elif isinstance(value, (list, tuple)):
- return [recursive(lv) for lv in value]
-
- # It's not clear why xmlrpclib created their own DateTime type, but
- # for our purposes, make it a datetime type which is explicitly
- # handled
- if isinstance(value, xmlrpclib.DateTime):
- value = datetime.datetime(*tuple(value.timetuple())[:6])
-
- if convert_datetime and isinstance(value, datetime.datetime):
- return timeutils.strtime(value)
- elif hasattr(value, 'iteritems'):
- return recursive(dict(value.iteritems()), level=level + 1)
- elif hasattr(value, '__iter__'):
- return recursive(list(value))
- elif convert_instances and hasattr(value, '__dict__'):
- # Likely an instance of something. Watch for cycles.
- # Ignore class member vars.
- return recursive(value.__dict__, level=level + 1)
- elif netaddr and isinstance(value, netaddr.IPAddress):
- return six.text_type(value)
- else:
- if any(test(value) for test in _nasty_type_tests):
- return six.text_type(value)
- return value
- except TypeError:
- # Class objects are tricky since they may define something like
- # __iter__ defined but it isn't callable as list().
- return six.text_type(value)
-
-
-def dumps(value, default=to_primitive, **kwargs):
- return json.dumps(value, default=default, **kwargs)
-
-
-def loads(s):
- return json.loads(s)
-
-
-def load(s):
- return json.load(s)
-
-
-try:
- import anyjson
-except ImportError:
- pass
-else:
- anyjson._modules.append((__name__, 'dumps', TypeError,
- 'loads', ValueError, 'load'))
- anyjson.force_implementation(__name__)
diff --git a/neutronclient/openstack/common/strutils.py b/neutronclient/openstack/common/strutils.py
deleted file mode 100644
index b2e4dcb35..000000000
--- a/neutronclient/openstack/common/strutils.py
+++ /dev/null
@@ -1,216 +0,0 @@
-# Copyright 2011 OpenStack Foundation.
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-"""
-System-level utilities and helper functions.
-"""
-
-import re
-import sys
-import unicodedata
-
-import six
-
-from neutronclient.openstack.common.gettextutils import _ # noqa
-
-
-# Used for looking up extensions of text
-# to their 'multiplied' byte amount
-BYTE_MULTIPLIERS = {
- '': 1,
- 't': 1024 ** 4,
- 'g': 1024 ** 3,
- 'm': 1024 ** 2,
- 'k': 1024,
-}
-BYTE_REGEX = re.compile(r'(^-?\d+)(\D*)')
-
-TRUE_STRINGS = ('1', 't', 'true', 'on', 'y', 'yes')
-FALSE_STRINGS = ('0', 'f', 'false', 'off', 'n', 'no')
-
-SLUGIFY_STRIP_RE = re.compile(r"[^\w\s-]")
-SLUGIFY_HYPHENATE_RE = re.compile(r"[-\s]+")
-
-
-def int_from_bool_as_string(subject):
- """Interpret a string as a boolean and return either 1 or 0.
-
- Any string value in:
-
- ('True', 'true', 'On', 'on', '1')
-
- is interpreted as a boolean True.
-
- Useful for JSON-decoded stuff and config file parsing
- """
- return bool_from_string(subject) and 1 or 0
-
-
-def bool_from_string(subject, strict=False):
- """Interpret a string as a boolean.
-
- A case-insensitive match is performed such that strings matching 't',
- 'true', 'on', 'y', 'yes', or '1' are considered True and, when
- `strict=False`, anything else is considered False.
-
- Useful for JSON-decoded stuff and config file parsing.
-
- If `strict=True`, unrecognized values, including None, will raise a
- ValueError which is useful when parsing values passed in from an API call.
- Strings yielding False are 'f', 'false', 'off', 'n', 'no', or '0'.
- """
- if not isinstance(subject, six.string_types):
- subject = str(subject)
-
- lowered = subject.strip().lower()
-
- if lowered in TRUE_STRINGS:
- return True
- elif lowered in FALSE_STRINGS:
- return False
- elif strict:
- acceptable = ', '.join(
- "'%s'" % s for s in sorted(TRUE_STRINGS + FALSE_STRINGS))
- msg = _("Unrecognized value '%(val)s', acceptable values are:"
- " %(acceptable)s") % {'val': subject,
- 'acceptable': acceptable}
- raise ValueError(msg)
- else:
- return False
-
-
-def safe_decode(text, incoming=None, errors='strict'):
- """Decodes incoming str using `incoming` if they're not already unicode.
-
- :param incoming: Text's current encoding
- :param errors: Errors handling policy. See here for valid
- values http://docs.python.org/2/library/codecs.html
- :returns: text or a unicode `incoming` encoded
- representation of it.
- :raises TypeError: If text is not an isntance of str
- """
- if not isinstance(text, six.string_types):
- raise TypeError("%s can't be decoded" % type(text))
-
- if isinstance(text, six.text_type):
- return text
-
- if not incoming:
- incoming = (sys.stdin.encoding or
- sys.getdefaultencoding())
-
- try:
- return text.decode(incoming, errors)
- except UnicodeDecodeError:
- # Note(flaper87) If we get here, it means that
- # sys.stdin.encoding / sys.getdefaultencoding
- # didn't return a suitable encoding to decode
- # text. This happens mostly when global LANG
- # var is not set correctly and there's no
- # default encoding. In this case, most likely
- # python will use ASCII or ANSI encoders as
- # default encodings but they won't be capable
- # of decoding non-ASCII characters.
- #
- # Also, UTF-8 is being used since it's an ASCII
- # extension.
- return text.decode('utf-8', errors)
-
-
-def safe_encode(text, incoming=None,
- encoding='utf-8', errors='strict'):
- """Encodes incoming str/unicode using `encoding`.
-
- If incoming is not specified, text is expected to be encoded with
- current python's default encoding. (`sys.getdefaultencoding`)
-
- :param incoming: Text's current encoding
- :param encoding: Expected encoding for text (Default UTF-8)
- :param errors: Errors handling policy. See here for valid
- values http://docs.python.org/2/library/codecs.html
- :returns: text or a bytestring `encoding` encoded
- representation of it.
- :raises TypeError: If text is not an isntance of str
- """
- if not isinstance(text, six.string_types):
- raise TypeError(_("%s can't be encoded") % type(text).capitalize())
-
- if not incoming:
- incoming = (sys.stdin.encoding or
- sys.getdefaultencoding())
-
- if isinstance(text, six.text_type):
- return text.encode(encoding, errors)
- elif text and encoding != incoming:
- # Decode text before encoding it with `encoding`
- text = safe_decode(text, incoming, errors)
- return text.encode(encoding, errors)
-
- return text
-
-
-def to_bytes(text, default=0):
- """Converts a string into an integer of bytes.
-
- Looks at the last characters of the text to determine
- what conversion is needed to turn the input text into a byte number.
- Supports "B, K(B), M(B), G(B), and T(B)". (case insensitive)
-
- :param text: String input for bytes size conversion.
- :param default: Default return value when text is blank.
-
- """
- match = BYTE_REGEX.search(text)
- if match:
- magnitude = int(match.group(1))
- mult_key_org = match.group(2)
- if not mult_key_org:
- return magnitude
- elif text:
- msg = _('Invalid string format: %s') % text
- raise TypeError(msg)
- else:
- return default
- mult_key = mult_key_org.lower().replace('b', '', 1)
- multiplier = BYTE_MULTIPLIERS.get(mult_key)
- if multiplier is None:
- msg = _('Unknown byte multiplier: %s') % mult_key_org
- raise TypeError(msg)
- return magnitude * multiplier
-
-
-def to_slug(value, incoming=None, errors="strict"):
- """Normalize string.
-
- Convert to lowercase, remove non-word characters, and convert spaces
- to hyphens.
-
- Inspired by Django's `slugify` filter.
-
- :param value: Text to slugify
- :param incoming: Text's current encoding
- :param errors: Errors handling policy. See here for valid
- values http://docs.python.org/2/library/codecs.html
- :returns: slugified unicode representation of `value`
- :raises TypeError: If text is not an instance of str
- """
- value = safe_decode(value, incoming, errors)
- # NOTE(aababilov): no need to use safe_(encode|decode) here:
- # encodings are always "ascii", error handling is always "ignore"
- # and types are always known (first: unicode; second: str)
- value = unicodedata.normalize("NFKD", value).encode(
- "ascii", "ignore").decode("ascii")
- value = SLUGIFY_STRIP_RE.sub("", value).strip().lower()
- return SLUGIFY_HYPHENATE_RE.sub("-", value)
diff --git a/neutronclient/openstack/common/timeutils.py b/neutronclient/openstack/common/timeutils.py
deleted file mode 100644
index 14fc4fbd8..000000000
--- a/neutronclient/openstack/common/timeutils.py
+++ /dev/null
@@ -1,186 +0,0 @@
-# Copyright 2011 OpenStack Foundation.
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-"""
-Time related utilities and helper functions.
-"""
-
-import calendar
-import datetime
-
-import iso8601
-import six
-
-
-# ISO 8601 extended time format with microseconds
-_ISO8601_TIME_FORMAT_SUBSECOND = '%Y-%m-%dT%H:%M:%S.%f'
-_ISO8601_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S'
-PERFECT_TIME_FORMAT = _ISO8601_TIME_FORMAT_SUBSECOND
-
-
-def isotime(at=None, subsecond=False):
- """Stringify time in ISO 8601 format."""
- if not at:
- at = utcnow()
- st = at.strftime(_ISO8601_TIME_FORMAT
- if not subsecond
- else _ISO8601_TIME_FORMAT_SUBSECOND)
- tz = at.tzinfo.tzname(None) if at.tzinfo else 'UTC'
- st += ('Z' if tz == 'UTC' else tz)
- return st
-
-
-def parse_isotime(timestr):
- """Parse time from ISO 8601 format."""
- try:
- return iso8601.parse_date(timestr)
- except iso8601.ParseError as e:
- raise ValueError(unicode(e))
- except TypeError as e:
- raise ValueError(unicode(e))
-
-
-def strtime(at=None, fmt=PERFECT_TIME_FORMAT):
- """Returns formatted utcnow."""
- if not at:
- at = utcnow()
- return at.strftime(fmt)
-
-
-def parse_strtime(timestr, fmt=PERFECT_TIME_FORMAT):
- """Turn a formatted time back into a datetime."""
- return datetime.datetime.strptime(timestr, fmt)
-
-
-def normalize_time(timestamp):
- """Normalize time in arbitrary timezone to UTC naive object."""
- offset = timestamp.utcoffset()
- if offset is None:
- return timestamp
- return timestamp.replace(tzinfo=None) - offset
-
-
-def is_older_than(before, seconds):
- """Return True if before is older than seconds."""
- if isinstance(before, six.string_types):
- before = parse_strtime(before).replace(tzinfo=None)
- return utcnow() - before > datetime.timedelta(seconds=seconds)
-
-
-def is_newer_than(after, seconds):
- """Return True if after is newer than seconds."""
- if isinstance(after, six.string_types):
- after = parse_strtime(after).replace(tzinfo=None)
- return after - utcnow() > datetime.timedelta(seconds=seconds)
-
-
-def utcnow_ts():
- """Timestamp version of our utcnow function."""
- return calendar.timegm(utcnow().timetuple())
-
-
-def utcnow():
- """Overridable version of utils.utcnow."""
- if utcnow.override_time:
- try:
- return utcnow.override_time.pop(0)
- except AttributeError:
- return utcnow.override_time
- return datetime.datetime.utcnow()
-
-
-def iso8601_from_timestamp(timestamp):
- """Returns a iso8601 formated date from timestamp."""
- return isotime(datetime.datetime.utcfromtimestamp(timestamp))
-
-
-utcnow.override_time = None
-
-
-def set_time_override(override_time=datetime.datetime.utcnow()):
- """Overrides utils.utcnow.
-
- Make it return a constant time or a list thereof, one at a time.
- """
- utcnow.override_time = override_time
-
-
-def advance_time_delta(timedelta):
- """Advance overridden time using a datetime.timedelta."""
- assert(not utcnow.override_time is None)
- try:
- for dt in utcnow.override_time:
- dt += timedelta
- except TypeError:
- utcnow.override_time += timedelta
-
-
-def advance_time_seconds(seconds):
- """Advance overridden time by seconds."""
- advance_time_delta(datetime.timedelta(0, seconds))
-
-
-def clear_time_override():
- """Remove the overridden time."""
- utcnow.override_time = None
-
-
-def marshall_now(now=None):
- """Make an rpc-safe datetime with microseconds.
-
- Note: tzinfo is stripped, but not required for relative times.
- """
- if not now:
- now = utcnow()
- return dict(day=now.day, month=now.month, year=now.year, hour=now.hour,
- minute=now.minute, second=now.second,
- microsecond=now.microsecond)
-
-
-def unmarshall_time(tyme):
- """Unmarshall a datetime dict."""
- return datetime.datetime(day=tyme['day'],
- month=tyme['month'],
- year=tyme['year'],
- hour=tyme['hour'],
- minute=tyme['minute'],
- second=tyme['second'],
- microsecond=tyme['microsecond'])
-
-
-def delta_seconds(before, after):
- """Return the difference between two timing objects.
-
- Compute the difference in seconds between two date, time, or
- datetime objects (as a float, to microsecond resolution).
- """
- delta = after - before
- try:
- return delta.total_seconds()
- except AttributeError:
- return ((delta.days * 24 * 3600) + delta.seconds +
- float(delta.microseconds) / (10 ** 6))
-
-
-def is_soon(dt, window):
- """Determines if time is going to happen in the next window seconds.
-
- :params dt: the time
- :params window: minimum seconds to remain to consider the time not soon
-
- :return: True if expiration is within the given duration
- """
- soon = (utcnow() + datetime.timedelta(seconds=window))
- return normalize_time(dt) <= soon
diff --git a/neutronclient/tests/unit/vpn/__init__.py b/neutronclient/osc/__init__.py
similarity index 100%
rename from neutronclient/tests/unit/vpn/__init__.py
rename to neutronclient/osc/__init__.py
diff --git a/neutronclient/osc/plugin.py b/neutronclient/osc/plugin.py
new file mode 100644
index 000000000..c98099f2a
--- /dev/null
+++ b/neutronclient/osc/plugin.py
@@ -0,0 +1,63 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# 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 plugin.
+
+from osc_lib import utils
+
+LOG = logging.getLogger(__name__)
+
+DEFAULT_API_VERSION = '2.0'
+API_VERSION_OPTION = 'os_network_api_version'
+# NOTE(rtheis): API_NAME must NOT be set to 'network' since
+# 'network' is owned by OSC! The OSC 'network' client uses
+# the OpenStack SDK.
+API_NAME = 'neutronclient'
+API_VERSIONS = {
+ '2.0': 'neutronclient.v2_0.client.Client',
+ '2': 'neutronclient.v2_0.client.Client',
+}
+
+
+def make_client(instance):
+ """Returns an neutron client."""
+ neutron_client = utils.get_client_class(
+ API_NAME,
+ instance._api_version[API_NAME],
+ API_VERSIONS)
+ LOG.debug('Instantiating neutron client: %s', neutron_client)
+
+ # TODO(amotoki): Check the following arguments need to be passed
+ # to neutronclient class. Check keystoneauth code.
+ # - 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=not instance.verify,
+ ca_cert=instance.cacert)
+ return client
+
+
+def build_option_parser(parser):
+ """Hook to add global options"""
+
+ # NOTE(amotoki): At now we register no option.
+ # OSC itself has an option for Network API version # and we refer to it.
+ return parser
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/__init__.py b/neutronclient/osc/v2/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/neutronclient/osc/v2/dynamic_routing/__init__.py b/neutronclient/osc/v2/dynamic_routing/__init__.py
new file mode 100644
index 000000000..e69de29bb
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/__init__.py b/neutronclient/osc/v2/fwaas/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/neutronclient/common/command.py b/neutronclient/osc/v2/fwaas/constants.py
similarity index 54%
rename from neutronclient/common/command.py
rename to neutronclient/osc/v2/fwaas/constants.py
index 2cd32935a..a5a74af73 100644
--- a/neutronclient/common/command.py
+++ b/neutronclient/osc/v2/fwaas/constants.py
@@ -1,4 +1,4 @@
-# Copyright 2012 OpenStack Foundation.
+# Copyright 2016-2017 FUJITSU LIMITED
# All Rights Reserved
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -14,27 +14,15 @@
# under the License.
#
-"""
-OpenStack base command
-"""
-
-from cliff import command
-
-
-class OpenStackCommand(command.Command):
- """Base class for OpenStack commands
- """
-
- api = None
-
- def run(self, parsed_args):
- if not self.api:
- return
- else:
- return super(OpenStackCommand, self).run(parsed_args)
-
- def get_data(self, parsed_args):
- pass
-
- def take_action(self, parsed_args):
- return self.get_data(parsed_args)
+# 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/lbaas/__init__.py b/neutronclient/osc/v2/lbaas/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/neutronclient/osc/v2/logging/__init__.py b/neutronclient/osc/v2/logging/__init__.py
new file mode 100644
index 000000000..e69de29bb
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/osc/v2/sfc/__init__.py b/neutronclient/osc/v2/sfc/__init__.py
new file mode 100644
index 000000000..e69de29bb
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/osc/v2/subnet_onboard/__init__.py b/neutronclient/osc/v2/subnet_onboard/__init__.py
new file mode 100644
index 000000000..e69de29bb
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/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/__init__.py b/neutronclient/osc/v2/vpnaas/__init__.py
new file mode 100644
index 000000000..e69de29bb
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 b86b36990..000000000
--- a/neutronclient/shell.py
+++ /dev/null
@@ -1,692 +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 logging
-import os
-import sys
-
-from cliff import app
-from cliff import commandmanager
-
-from neutronclient.common import clientmanager
-from neutronclient.common import exceptions as exc
-from neutronclient.common import utils
-from neutronclient.neutron.v2_0 import agent
-from neutronclient.neutron.v2_0 import agentscheduler
-from neutronclient.neutron.v2_0 import credential
-from neutronclient.neutron.v2_0 import extension
-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 import vip as lb_vip
-from neutronclient.neutron.v2_0 import metering
-from neutronclient.neutron.v2_0 import netpartition
-from neutronclient.neutron.v2_0 import network
-from neutronclient.neutron.v2_0 import networkprofile
-from neutronclient.neutron.v2_0.nsx import networkgateway
-from neutronclient.neutron.v2_0.nsx import qos_queue
-from neutronclient.neutron.v2_0 import policyprofile
-from neutronclient.neutron.v2_0 import port
-from neutronclient.neutron.v2_0 import quota
-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.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.openstack.common.gettextutils import _
-from neutronclient.openstack.common import strutils
-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)
- cmd.values_specs = (index == -1 and _values_specs or values_specs)
- return cmd.run(known_args)
-
-
-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', '')
-
-
-COMMAND_V2 = {
- '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,
- 'port-list': port.ListPort,
- 'port-show': port.ShowPort,
- 'port-create': port.CreatePort,
- 'port-delete': port.DeletePort,
- 'port-update': port.UpdatePort,
- 'quota-list': quota.ListQuota,
- 'quota-show': quota.ShowQuota,
- '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,
- '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
- ),
- 'queue-create': qos_queue.CreateQoSQueue,
- 'queue-delete': qos_queue.DeleteQoSQueue,
- 'queue-show': qos_queue.ShowQoSQueue,
- 'queue-list': qos_queue.ListQoSQueue,
- 'agent-list': agent.ListAgent,
- 'agent-show': agent.ShowAgent,
- 'agent-delete': agent.DeleteAgent,
- 'agent-update': agent.UpdateAgent,
- 'net-gateway-create': networkgateway.CreateNetworkGateway,
- 'net-gateway-update': networkgateway.UpdateNetworkGateway,
- 'net-gateway-delete': networkgateway.DeleteNetworkGateway,
- 'net-gateway-show': networkgateway.ShowNetworkGateway,
- 'net-gateway-list': networkgateway.ListNetworkGateway,
- 'net-gateway-connect': networkgateway.ConnectNetworkGateway,
- 'net-gateway-disconnect': networkgateway.DisconnectNetworkGateway,
- 'gateway-device-create': networkgateway.CreateGatewayDevice,
- 'gateway-device-update': networkgateway.UpdateGatewayDevice,
- 'gateway-device-delete': networkgateway.DeleteGatewayDevice,
- 'gateway-device-show': networkgateway.ShowGatewayDevice,
- 'gateway-device-list': networkgateway.ListGatewayDevice,
- '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,
- '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,
- 'cisco-credential-list': credential.ListCredential,
- 'cisco-credential-show': credential.ShowCredential,
- 'cisco-credential-create': credential.CreateCredential,
- 'cisco-credential-delete': credential.DeleteCredential,
- 'cisco-network-profile-list': networkprofile.ListNetworkProfile,
- 'cisco-network-profile-show': networkprofile.ShowNetworkProfile,
- 'cisco-network-profile-create': networkprofile.CreateNetworkProfile,
- 'cisco-network-profile-delete': networkprofile.DeleteNetworkProfile,
- 'cisco-network-profile-update': networkprofile.UpdateNetworkProfile,
- 'cisco-policy-profile-list': policyprofile.ListPolicyProfile,
- 'cisco-policy-profile-show': policyprofile.ShowPolicyProfile,
- 'cisco-policy-profile-update': policyprofile.UpdatePolicyProfile,
- '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-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,
- 'nuage-netpartition-list': netpartition.ListNetPartition,
- 'nuage-netpartition-show': netpartition.ShowNetPartition,
- 'nuage-netpartition-create': netpartition.CreateNetPartition,
- 'nuage-netpartition-delete': netpartition.DeleteNetPartition,
-}
-
-COMMANDS = {'2.0': COMMAND_V2}
-
-
-class HelpAction(argparse.Action):
- """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)
-
- # 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. Can be repeated.'))
- 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"))
- # Global arguments
- parser.add_argument(
- '--os-auth-strategy', metavar='',
- default=env('OS_AUTH_STRATEGY', default='keystone'),
- help=_('Authentication strategy (Env: OS_AUTH_STRATEGY'
- ', default keystone). For now, any other value will'
- ' disable the authentication'))
- parser.add_argument(
- '--os_auth_strategy',
- help=argparse.SUPPRESS)
-
- parser.add_argument(
- '--os-auth-url', metavar='',
- default=env('OS_AUTH_URL'),
- help=_('Authentication URL (Env: OS_AUTH_URL)'))
- parser.add_argument(
- '--os_auth_url',
- help=argparse.SUPPRESS)
-
- parser.add_argument(
- '--os-tenant-name', metavar='',
- default=env('OS_TENANT_NAME'),
- help=_('Authentication tenant name (Env: OS_TENANT_NAME)'))
- parser.add_argument(
- '--os_tenant_name',
- help=argparse.SUPPRESS)
-
- parser.add_argument(
- '--os-tenant-id', metavar='',
- default=env('OS_TENANT_ID'),
- help=_('Authentication tenant name (Env: OS_TENANT_ID)'))
-
- parser.add_argument(
- '--os-username', metavar='',
- default=utils.env('OS_USERNAME'),
- help=_('Authentication username (Env: OS_USERNAME)'))
- parser.add_argument(
- '--os_username',
- help=argparse.SUPPRESS)
-
- parser.add_argument(
- '--os-password', metavar='',
- default=utils.env('OS_PASSWORD'),
- help=_('Authentication password (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 (Env: OS_REGION_NAME)'))
- parser.add_argument(
- '--os_region_name',
- help=argparse.SUPPRESS)
-
- parser.add_argument(
- '--os-token', metavar='',
- default=env('OS_TOKEN'),
- help=_('Defaults to env[OS_TOKEN]'))
- parser.add_argument(
- '--os_token',
- help=argparse.SUPPRESS)
-
- parser.add_argument(
- '--service-type', metavar='',
- default=env('OS_NETWORK_SERVICE_TYPE', default='network'),
- help=_('Defaults to env[OS_NETWORK_SERVICE_TYPE] or network.'))
-
- parser.add_argument(
- '--endpoint-type', metavar='',
- default=env('OS_ENDPOINT_TYPE', default='publicURL'),
- help=_('Defaults to env[OS_ENDPOINT_TYPE] or publicURL.'))
-
- 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(
- '--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(
- '--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."))
-
- return parser
-
- 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 command_name, command in self.command_manager:
- commands.add(command_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 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':
- 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(unicode(err))
- raise
- else:
- self.log.error(unicode(err))
- return 1
- result = 1
- if self.interactive_mode:
- _argv = [sys.argv[0]]
- sys.argv = _argv
- result = self.interact()
- else:
- result = self.run_subcommand(remainder)
- return result
-
- 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)
- err = None
- result = 1
- 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 Exception as err:
- if self.options.verbose_level == self.DEBUG_LEVEL:
- self.log.exception(unicode(err))
- else:
- self.log.error(unicode(err))
- try:
- self.clean_up(cmd, result, err)
- except Exception as err2:
- if self.options.verbose_level == self.DEBUG_LEVEL:
- self.log.exception(unicode(err2))
- else:
- self.log.error(_('Could not clean up: %s'), unicode(err2))
- if self.options.verbose_level == self.DEBUG_LEVEL:
- raise
- else:
- try:
- self.clean_up(cmd, result, None)
- except Exception as err3:
- if self.options.verbose_level == self.DEBUG_LEVEL:
- self.log.exception(unicode(err3))
- else:
- self.log.error(_('Could not clean up: %s'), unicode(err3))
- return result
-
- def authenticate_user(self):
- """Make sure the user has provided all of the authentication
- info we need.
- """
- if self.options.os_auth_strategy == 'keystone':
- if self.options.os_token or self.options.os_url:
- # Token flow auth takes priority
- if not self.options.os_token:
- raise exc.CommandError(
- _("You must provide a token via"
- " either --os-token or env[OS_TOKEN]"))
-
- if not self.options.os_url:
- raise exc.CommandError(
- _("You must provide a service URL via"
- " either --os-url or env[OS_URL]"))
-
- else:
- # Validate password flow auth
- if not self.options.os_username:
- raise exc.CommandError(
- _("You must provide a username via"
- " either --os-username or env[OS_USERNAME]"))
-
- if not self.options.os_password:
- raise exc.CommandError(
- _("You must provide a password via"
- " either --os-password or env[OS_PASSWORD]"))
-
- if (not self.options.os_tenant_name
- and not self.options.os_tenant_id):
- raise exc.CommandError(
- _("You must provide a tenant_name or tenant_id via"
- " --os-tenant-name, env[OS_TENANT_NAME]"
- " --os-tenant-id, or via env[OS_TENANT_ID]"))
-
- if not self.options.os_auth_url:
- raise exc.CommandError(
- _("You must provide an auth url via"
- " either --os-auth-url or via env[OS_AUTH_URL]"))
- else: # not keystone
- if not self.options.os_url:
- raise exc.CommandError(
- _("You must provide a service URL via"
- " either --os-url or env[OS_URL]"))
-
- self.client_manager = clientmanager.ClientManager(
- token=self.options.os_token,
- url=self.options.os_url,
- auth_url=self.options.os_auth_url,
- tenant_name=self.options.os_tenant_name,
- tenant_id=self.options.os_tenant_id,
- username=self.options.os_username,
- password=self.options.os_password,
- region_name=self.options.os_region_name,
- api_version=self.api_version,
- auth_strategy=self.options.os_auth_strategy,
- service_type=self.options.service_type,
- endpoint_type=self.options.endpoint_type,
- insecure=self.options.insecure,
- ca_cert=self.options.os_cacert,
- 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 clean_up(self, cmd, result, err):
- self.log.debug('clean_up %s', cmd.__class__.__name__)
- if err:
- self.log.debug(_('Got an error: %s'), unicode(err))
-
- 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)
- console.setLevel(console_level)
- if logging.DEBUG == console_level:
- formatter = logging.Formatter(self.DEBUG_MESSAGE_FORMAT)
- else:
- formatter = logging.Formatter(self.CONSOLE_MESSAGE_FORMAT)
- console.setFormatter(formatter)
- root_logger.addHandler(console)
- return
-
-
-def main(argv=sys.argv[1:]):
- try:
- return NeutronShell(NEUTRON_API_VERSION).run(map(strutils.safe_decode,
- argv))
- except exc.NeutronClientException:
- return 1
- except Exception as e:
- print(unicode(e))
- return 1
-
-
-if __name__ == "__main__":
- sys.exit(main(sys.argv[1:]))
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 1d24a3342..000000000
--- a/neutronclient/tests/unit/fw/test_cli20_firewall.py
+++ /dev/null
@@ -1,125 +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.
-#
-# @author: KC Wang, Big Switch Networks Inc.
-#
-
-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,
- '--shared',
- '--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,
- shared=True, admin_state_up=False,
- tenant_id=tenant_id)
-
- 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_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)
-
-
-class CLITestV20FirewallXML(CLITestV20FirewallJSON):
- format = 'xml'
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 d217c91ce..000000000
--- a/neutronclient/tests/unit/fw/test_cli20_firewallpolicy.py
+++ /dev/null
@@ -1,213 +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.
-#
-# @author: KC Wang, Big Switch Networks Inc.
-#
-
-import sys
-
-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 all params set."""
- resource = 'firewall_policy'
- cmd = firewallpolicy.CreateFirewallPolicy(test_cli20.MyApp(sys.stdout),
- None)
- name = 'my-name'
- description = 'my-desc'
- firewall_rules_arg = 'rule_id1 rule_id2'
- firewall_rules_res = ['rule_id1', 'rule_id2']
- tenant_id = 'my-tenant'
- my_id = 'myid'
- args = ['--description', description,
- '--shared',
- '--firewall-rules', firewall_rules_arg,
- '--audited',
- '--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,
- 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_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))
- args.extend(['--request-format', self.format])
- 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))
- args.extend(['--request-format', self.format])
- self.mox.ReplayAll()
- cmd_parser = cmd.get_parser(resource + "_remove_rule")
- shell.run_command(cmd, cmd_parser, args)
- self.mox.VerifyAll()
- self.mox.UnsetStubs()
-
-
-class CLITestV20FirewallPolicyXML(CLITestV20FirewallPolicyJSON):
- format = 'xml'
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 30c8a0fd5..000000000
--- a/neutronclient/tests/unit/fw/test_cli20_firewallrule.py
+++ /dev/null
@@ -1,180 +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.
-#
-# @author: KC Wang, Big Switch Networks Inc.
-#
-
-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'
- enabled_flag = '--enabled' if enabled else '--disabled'
- args = ['--tenant-id', tenant_id,
- '--admin-state-up',
- '--protocol', protocol,
- '--action', action,
- enabled_flag]
- 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)
-
- 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'):
- """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'
- action = 'allow'
- tenant_id = 'my-tenant'
- my_id = 'myid'
- args = ['--description', description,
- '--shared',
- '--protocol', protocol,
- '--source-ip-address', source_ip,
- '--destination-ip-address', destination_ip,
- '--source-port', source_port,
- '--destination-port', destination_port,
- '--action', action,
- '--enabled',
- '--admin-state-up',
- '--tenant-id', tenant_id]
- position_names = []
- position_values = []
- if protocol == 'any':
- protocol = None
- self._test_create_resource(resource, cmd, name, my_id, args,
- position_names, position_values,
- description=description, shared=True,
- protocol=protocol,
- 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_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', })
-
- def test_update_firewall_rule_protocol(self):
- """firewall-rule-update myid --protocol any."""
- resource = 'firewall_rule'
- cmd = firewallrule.UpdateFirewallRule(test_cli20.MyApp(sys.stdout),
- None)
- self._test_update_resource(resource, cmd, 'myid',
- ['myid', '--protocol', 'any'],
- {'protocol': None, })
-
- 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)
-
-
-class CLITestV20FirewallRuleXML(CLITestV20FirewallRuleJSON):
- format = 'xml'
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 ab2986e43..000000000
--- a/neutronclient/tests/unit/lb/test_cli20_healthmonitor.py
+++ /dev/null
@@ -1,218 +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.
-#
-# @author: Ilya Shakhat, Mirantis Inc.
-#
-
-import sys
-
-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_show_healthmonitor_id_name(self):
- """lb-healthmonitor-show."""
- resource = 'health_monitor'
- 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'])
-
- 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()
-
-
-class CLITestV20LbHealthmonitorXML(CLITestV20LbHealthmonitorJSON):
- format = 'xml'
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 b3f4cc87b..000000000
--- a/neutronclient/tests/unit/lb/test_cli20_member.py
+++ /dev/null
@@ -1,133 +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.
-#
-# @author: Ilya Shakhat, Mirantis Inc.
-#
-
-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_show_member_id_name(self):
- """lb-member-show."""
- resource = 'member'
- cmd = member.ShowMember(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_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)
-
-
-class CLITestV20LbMemberXML(CLITestV20LbMemberJSON):
- format = 'xml'
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 56def65af..000000000
--- a/neutronclient/tests/unit/lb/test_cli20_pool.py
+++ /dev/null
@@ -1,172 +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.
-#
-# @author: Ilya Shakhat, Mirantis Inc.
-#
-
-import sys
-
-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)
-
-
-class CLITestV20LbPoolXML(CLITestV20LbPoolJSON):
- format = 'xml'
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 fb501415e..000000000
--- a/neutronclient/tests/unit/lb/test_cli20_vip.py
+++ /dev/null
@@ -1,213 +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.
-#
-# @author: Ilya Shakhat, Mirantis Inc.
-#
-
-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)
-
-
-class CLITestV20LbVipXML(CLITestV20LbVipJSON):
- format = 'xml'
diff --git a/neutronclient/tests/unit/osc/__init__.py b/neutronclient/tests/unit/osc/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/neutronclient/tests/unit/osc/v2/__init__.py b/neutronclient/tests/unit/osc/v2/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/neutronclient/tests/unit/osc/v2/dynamic_routing/__init__.py b/neutronclient/tests/unit/osc/v2/dynamic_routing/__init__.py
new file mode 100644
index 000000000..e69de29bb
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
new file mode 100644
index 000000000..7fec7347c
--- /dev/null
+++ b/neutronclient/tests/unit/osc/v2/fakes.py
@@ -0,0 +1,50 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# 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 cliff import columns as cliff_columns
+from osc_lib.tests import utils
+
+
+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.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/lbaas/__init__.py b/neutronclient/tests/unit/osc/v2/lbaas/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/neutronclient/tests/unit/osc/v2/logging/__init__.py b/neutronclient/tests/unit/osc/v2/logging/__init__.py
new file mode 100644
index 000000000..e69de29bb
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/osc/v2/sfc/__init__.py b/neutronclient/tests/unit/osc/v2/sfc/__init__.py
new file mode 100644
index 000000000..e69de29bb
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/osc/v2/subnet_onboard/__init__.py b/neutronclient/tests/unit/osc/v2/subnet_onboard/__init__.py
new file mode 100644
index 000000000..e69de29bb
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/vpnaas/__init__.py b/neutronclient/tests/unit/osc/v2/vpnaas/__init__.py
new file mode 100644
index 000000000..e69de29bb
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/test_auth.py b/neutronclient/tests/unit/test_auth.py
deleted file mode 100644
index b81d23614..000000000
--- a/neutronclient/tests/unit/test_auth.py
+++ /dev/null
@@ -1,450 +0,0 @@
-# Copyright 2012 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 copy
-import httplib2
-import json
-import uuid
-
-import mox
-import testtools
-
-from neutronclient import client
-from neutronclient.common import exceptions
-from neutronclient.common import utils
-
-
-USERNAME = 'testuser'
-TENANT_NAME = 'testtenant'
-TENANT_ID = 'testtenantid'
-PASSWORD = 'password'
-AUTH_URL = 'authurl'
-ENDPOINT_URL = 'localurl'
-ENDPOINT_OVERRIDE = 'otherurl'
-TOKEN = 'tokentoken'
-REGION = 'RegionTest'
-
-KS_TOKEN_RESULT = {
- 'access': {
- 'token': {'id': TOKEN,
- 'expires': '2012-08-11T07:49:01Z',
- 'tenant': {'id': str(uuid.uuid1())}},
- 'user': {'id': str(uuid.uuid1())},
- 'serviceCatalog': [
- {'endpoints_links': [],
- 'endpoints': [{'adminURL': ENDPOINT_URL,
- 'internalURL': ENDPOINT_URL,
- 'publicURL': ENDPOINT_URL,
- 'region': REGION}],
- 'type': 'network',
- 'name': 'Neutron Service'}
- ]
- }
-}
-
-ENDPOINTS_RESULT = {
- 'endpoints': [{
- 'type': 'network',
- 'name': 'Neutron Service',
- 'region': REGION,
- 'adminURL': ENDPOINT_URL,
- 'internalURL': ENDPOINT_URL,
- 'publicURL': ENDPOINT_URL
- }]
-}
-
-
-class CLITestAuthKeystone(testtools.TestCase):
-
- # Auth Body expected when using tenant name
- auth_type = 'tenantName'
-
- def setUp(self):
- """Prepare the test environment."""
- super(CLITestAuthKeystone, self).setUp()
- self.mox = mox.Mox()
- self.client = client.HTTPClient(username=USERNAME,
- tenant_name=TENANT_NAME,
- password=PASSWORD,
- auth_url=AUTH_URL,
- region_name=REGION)
- self.addCleanup(self.mox.VerifyAll)
- self.addCleanup(self.mox.UnsetStubs)
-
- def test_reused_token_get_auth_info(self):
- """Test that Client.get_auth_info() works even if client was
- instantiated with predefined token.
- """
- client_ = client.HTTPClient(username=USERNAME,
- tenant_id=TENANT_ID,
- tenant_name=TENANT_NAME,
- token=TOKEN,
- password=PASSWORD,
- auth_url=AUTH_URL,
- region_name=REGION)
- expected = {'auth_token': TOKEN,
- 'auth_tenant_id': None,
- 'auth_user_id': None,
- 'endpoint_url': self.client.endpoint_url}
- self.assertEqual(client_.get_auth_info(), expected)
-
- def test_get_token(self):
- self.mox.StubOutWithMock(self.client, "request")
-
- res200 = self.mox.CreateMock(httplib2.Response)
- res200.status = 200
-
- self.client.request(
- AUTH_URL + '/tokens', 'POST',
- body=mox.StrContains(self.auth_type), headers=mox.IsA(dict)
- ).AndReturn((res200, json.dumps(KS_TOKEN_RESULT)))
- self.client.request(
- mox.StrContains(ENDPOINT_URL + '/resource'), 'GET',
- headers=mox.ContainsKeyValue('X-Auth-Token', TOKEN)
- ).AndReturn((res200, ''))
- self.mox.ReplayAll()
-
- self.client.do_request('/resource', 'GET')
- self.assertEqual(self.client.endpoint_url, ENDPOINT_URL)
- self.assertEqual(self.client.auth_token, TOKEN)
-
- def test_refresh_token(self):
- self.mox.StubOutWithMock(self.client, "request")
-
- self.client.auth_token = TOKEN
- self.client.endpoint_url = ENDPOINT_URL
-
- res200 = self.mox.CreateMock(httplib2.Response)
- res200.status = 200
- res401 = self.mox.CreateMock(httplib2.Response)
- res401.status = 401
-
- # If a token is expired, neutron server retruns 401
- self.client.request(
- mox.StrContains(ENDPOINT_URL + '/resource'), 'GET',
- headers=mox.ContainsKeyValue('X-Auth-Token', TOKEN)
- ).AndReturn((res401, ''))
- self.client.request(
- AUTH_URL + '/tokens', 'POST',
- body=mox.IsA(str), headers=mox.IsA(dict)
- ).AndReturn((res200, json.dumps(KS_TOKEN_RESULT)))
- self.client.request(
- mox.StrContains(ENDPOINT_URL + '/resource'), 'GET',
- headers=mox.ContainsKeyValue('X-Auth-Token', TOKEN)
- ).AndReturn((res200, ''))
- self.mox.ReplayAll()
- self.client.do_request('/resource', 'GET')
-
- def test_refresh_token_no_auth_url(self):
- self.mox.StubOutWithMock(self.client, "request")
- self.client.auth_url = None
-
- self.client.auth_token = TOKEN
- self.client.endpoint_url = ENDPOINT_URL
-
- res401 = self.mox.CreateMock(httplib2.Response)
- res401.status = 401
-
- # If a token is expired, neutron server returns 401
- self.client.request(
- mox.StrContains(ENDPOINT_URL + '/resource'), 'GET',
- headers=mox.ContainsKeyValue('X-Auth-Token', TOKEN)
- ).AndReturn((res401, ''))
- self.mox.ReplayAll()
- self.assertRaises(exceptions.NoAuthURLProvided,
- self.client.do_request,
- '/resource',
- 'GET')
-
- def test_get_endpoint_url_with_invalid_auth_url(self):
- # Handle the case when auth_url is not provided
- self.client.auth_url = None
- self.assertRaises(exceptions.NoAuthURLProvided,
- self.client._get_endpoint_url)
-
- def test_get_endpoint_url(self):
- self.mox.StubOutWithMock(self.client, "request")
-
- self.client.auth_token = TOKEN
-
- res200 = self.mox.CreateMock(httplib2.Response)
- res200.status = 200
-
- self.client.request(
- mox.StrContains(AUTH_URL + '/tokens/%s/endpoints' % TOKEN), 'GET',
- headers=mox.IsA(dict)
- ).AndReturn((res200, json.dumps(ENDPOINTS_RESULT)))
- self.client.request(
- mox.StrContains(ENDPOINT_URL + '/resource'), 'GET',
- headers=mox.ContainsKeyValue('X-Auth-Token', TOKEN)
- ).AndReturn((res200, ''))
- self.mox.ReplayAll()
- self.client.do_request('/resource', 'GET')
-
- def test_use_given_endpoint_url(self):
- self.client = client.HTTPClient(
- username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD,
- auth_url=AUTH_URL, region_name=REGION,
- endpoint_url=ENDPOINT_OVERRIDE)
- self.assertEqual(self.client.endpoint_url, ENDPOINT_OVERRIDE)
-
- self.mox.StubOutWithMock(self.client, "request")
-
- self.client.auth_token = TOKEN
-
- res200 = self.mox.CreateMock(httplib2.Response)
- res200.status = 200
-
- self.client.request(
- mox.StrContains(ENDPOINT_OVERRIDE + '/resource'), 'GET',
- headers=mox.ContainsKeyValue('X-Auth-Token', TOKEN)
- ).AndReturn((res200, ''))
- self.mox.ReplayAll()
- self.client.do_request('/resource', 'GET')
- self.assertEqual(self.client.endpoint_url, ENDPOINT_OVERRIDE)
-
- def test_get_endpoint_url_other(self):
- self.client = client.HTTPClient(
- username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD,
- auth_url=AUTH_URL, region_name=REGION, endpoint_type='otherURL')
- self.mox.StubOutWithMock(self.client, "request")
-
- self.client.auth_token = TOKEN
-
- res200 = self.mox.CreateMock(httplib2.Response)
- res200.status = 200
-
- self.client.request(
- mox.StrContains(AUTH_URL + '/tokens/%s/endpoints' % TOKEN), 'GET',
- headers=mox.IsA(dict)
- ).AndReturn((res200, json.dumps(ENDPOINTS_RESULT)))
- self.mox.ReplayAll()
- self.assertRaises(exceptions.EndpointTypeNotFound,
- self.client.do_request,
- '/resource',
- 'GET')
-
- def test_get_endpoint_url_failed(self):
- self.mox.StubOutWithMock(self.client, "request")
-
- self.client.auth_token = TOKEN
-
- res200 = self.mox.CreateMock(httplib2.Response)
- res200.status = 200
- res401 = self.mox.CreateMock(httplib2.Response)
- res401.status = 401
-
- self.client.request(
- mox.StrContains(AUTH_URL + '/tokens/%s/endpoints' % TOKEN), 'GET',
- headers=mox.IsA(dict)
- ).AndReturn((res401, ''))
- self.client.request(
- AUTH_URL + '/tokens', 'POST',
- body=mox.IsA(str), headers=mox.IsA(dict)
- ).AndReturn((res200, json.dumps(KS_TOKEN_RESULT)))
- self.client.request(
- mox.StrContains(ENDPOINT_URL + '/resource'), 'GET',
- headers=mox.ContainsKeyValue('X-Auth-Token', TOKEN)
- ).AndReturn((res200, ''))
- self.mox.ReplayAll()
- self.client.do_request('/resource', 'GET')
-
- def test_url_for(self):
- resources = copy.deepcopy(KS_TOKEN_RESULT)
-
- endpoints = resources['access']['serviceCatalog'][0]['endpoints'][0]
- endpoints['publicURL'] = 'public'
- endpoints['internalURL'] = 'internal'
- endpoints['adminURL'] = 'admin'
- catalog = client.ServiceCatalog(resources)
-
- # endpoint_type not specified
- url = catalog.url_for(attr='region',
- filter_value=REGION)
- self.assertEqual('public', url)
-
- # endpoint type specified (3 cases)
- url = catalog.url_for(attr='region',
- filter_value=REGION,
- endpoint_type='adminURL')
- self.assertEqual('admin', url)
-
- url = catalog.url_for(attr='region',
- filter_value=REGION,
- endpoint_type='publicURL')
- self.assertEqual('public', url)
-
- url = catalog.url_for(attr='region',
- filter_value=REGION,
- endpoint_type='internalURL')
- self.assertEqual('internal', url)
-
- # endpoint_type requested does not exist.
- self.assertRaises(exceptions.EndpointTypeNotFound,
- catalog.url_for,
- attr='region',
- filter_value=REGION,
- endpoint_type='privateURL')
-
- # Test scenario with url_for when the service catalog only has publicURL.
- def test_url_for_only_public_url(self):
- resources = copy.deepcopy(KS_TOKEN_RESULT)
- catalog = client.ServiceCatalog(resources)
-
- # Remove endpoints from the catalog.
- endpoints = resources['access']['serviceCatalog'][0]['endpoints'][0]
- del endpoints['internalURL']
- del endpoints['adminURL']
- endpoints['publicURL'] = 'public'
-
- # Use publicURL when specified explicitly.
- url = catalog.url_for(attr='region',
- filter_value=REGION,
- endpoint_type='publicURL')
- self.assertEqual('public', url)
-
- # Use publicURL when specified explicitly.
- url = catalog.url_for(attr='region',
- filter_value=REGION)
- self.assertEqual('public', url)
-
- # Test scenario with url_for when the service catalog only has adminURL.
- def test_url_for_only_admin_url(self):
- resources = copy.deepcopy(KS_TOKEN_RESULT)
- catalog = client.ServiceCatalog(resources)
- endpoints = resources['access']['serviceCatalog'][0]['endpoints'][0]
- del endpoints['internalURL']
- del endpoints['publicURL']
- endpoints['adminURL'] = 'admin'
-
- # Use publicURL when specified explicitly.
- url = catalog.url_for(attr='region',
- filter_value=REGION,
- endpoint_type='adminURL')
- self.assertEqual('admin', url)
-
- # But not when nothing is specified.
- self.assertRaises(exceptions.EndpointTypeNotFound,
- catalog.url_for,
- attr='region',
- filter_value=REGION)
-
- def test_endpoint_type(self):
- resources = copy.deepcopy(KS_TOKEN_RESULT)
- endpoints = resources['access']['serviceCatalog'][0]['endpoints'][0]
- endpoints['internalURL'] = 'internal'
- endpoints['adminURL'] = 'admin'
- endpoints['publicURL'] = 'public'
-
- # Test default behavior is to choose public.
- self.client = client.HTTPClient(
- username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD,
- auth_url=AUTH_URL, region_name=REGION)
-
- self.client._extract_service_catalog(resources)
- self.assertEqual(self.client.endpoint_url, 'public')
-
- # Test admin url
- self.client = client.HTTPClient(
- username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD,
- auth_url=AUTH_URL, region_name=REGION, endpoint_type='adminURL')
-
- self.client._extract_service_catalog(resources)
- self.assertEqual(self.client.endpoint_url, 'admin')
-
- # Test public url
- self.client = client.HTTPClient(
- username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD,
- auth_url=AUTH_URL, region_name=REGION, endpoint_type='publicURL')
-
- self.client._extract_service_catalog(resources)
- self.assertEqual(self.client.endpoint_url, 'public')
-
- # Test internal url
- self.client = client.HTTPClient(
- username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD,
- auth_url=AUTH_URL, region_name=REGION, endpoint_type='internalURL')
-
- self.client._extract_service_catalog(resources)
- self.assertEqual(self.client.endpoint_url, 'internal')
-
- # Test url that isn't found in the service catalog
- self.client = client.HTTPClient(
- username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD,
- auth_url=AUTH_URL, region_name=REGION, endpoint_type='privateURL')
-
- self.assertRaises(exceptions.EndpointTypeNotFound,
- self.client._extract_service_catalog,
- resources)
-
- def test_strip_credentials_from_log(self):
- def verify_no_credentials(kwargs):
- return ('REDACTED' in kwargs['body']) and (
- self.client.password not in kwargs['body'])
-
- def verify_credentials(body):
- return 'REDACTED' not in body and self.client.password in body
-
- self.mox.StubOutWithMock(self.client, "request")
- self.mox.StubOutWithMock(utils, "http_log_req")
-
- res200 = self.mox.CreateMock(httplib2.Response)
- res200.status = 200
-
- utils.http_log_req(mox.IgnoreArg(), mox.IgnoreArg(), mox.Func(
- verify_no_credentials))
- self.client.request(
- mox.IsA(str), mox.IsA(str), body=mox.Func(verify_credentials),
- headers=mox.IgnoreArg()
- ).AndReturn((res200, json.dumps(KS_TOKEN_RESULT)))
- utils.http_log_req(mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg())
- self.client.request(
- mox.IsA(str), mox.IsA(str), headers=mox.IsA(dict)
- ).AndReturn((res200, ''))
- self.mox.ReplayAll()
-
- self.client.do_request('/resource', 'GET')
-
-
-class CLITestAuthKeystoneWithId(CLITestAuthKeystone):
-
- # Auth Body expected when using tenant Id
- auth_type = 'tenantId'
-
- def setUp(self):
- """Prepare the test environment."""
- super(CLITestAuthKeystoneWithId, self).setUp()
- self.client = client.HTTPClient(username=USERNAME,
- tenant_id=TENANT_ID,
- password=PASSWORD,
- auth_url=AUTH_URL,
- region_name=REGION)
-
-
-class CLITestAuthKeystoneWithIdandName(CLITestAuthKeystone):
-
- # Auth Body expected when using tenant Id
- auth_type = 'tenantId'
-
- def setUp(self):
- """Prepare the test environment."""
- super(CLITestAuthKeystoneWithIdandName, self).setUp()
- self.client = client.HTTPClient(username=USERNAME,
- tenant_id=TENANT_ID,
- tenant_name=TENANT_NAME,
- password=PASSWORD,
- auth_url=AUTH_URL,
- region_name=REGION)
diff --git a/neutronclient/tests/unit/test_casual_args.py b/neutronclient/tests/unit/test_casual_args.py
index f1cf201b5..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)
@@ -78,6 +85,11 @@ def test_arg(self):
self.assertEqual('value1',
neutronV20.parse_args_to_dict(_specs)['arg1'])
+ def test_arg_invalid_syntax(self):
+ _specs = ['--tag=t', '---arg1', 'value1']
+ self.assertRaises(exceptions.CommandError,
+ neutronV20.parse_args_to_dict, _specs)
+
def test_dict_arg(self):
_specs = ['--tag=t', '--arg1', 'type=dict', 'key1=value1,key2=value2']
arg1 = neutronV20.parse_args_to_dict(_specs)['arg1']
@@ -97,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 47b4a115d..000000000
--- a/neutronclient/tests/unit/test_cli20.py
+++ /dev/null
@@ -1,611 +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 urllib
-
-import fixtures
-import mox
-import testtools
-
-from neutronclient.common import constants
-from neutronclient.common import exceptions
-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'
-
-
-class FakeStdout:
-
- def __init__(self):
- self.content = []
-
- def write(self, text):
- self.content.append(text)
-
- def make_string(self):
- result = ''
- for line in self.content:
- result = result + line
- return result
-
-
-class MyResp(object):
- def __init__(self, status):
- self.status = status
-
-
-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):
- return str(self) == rhs
-
- 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 lhs.iteritems():
- 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(testtools.TestCase):
-
- format = 'json'
- test_id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'
- id_field = 'id'
-
- def _find_resourceid(self, client, resource, name_or_id):
- return name_or_id
-
- def _get_attr_metadata(self):
- return self.metadata
- client.Client.EXTED_PLURALS.update(constants.PLURALS)
- client.Client.EXTED_PLURALS.update({'tags': 'tag'})
- return {'plurals': client.Client.EXTED_PLURALS,
- 'xmlns': constants.XML_NS_V20,
- constants.EXT_NS: {'prefix': 'http://xxxx.yy.com'}}
-
- def setUp(self, plurals={}):
- """Prepare the test environment."""
- super(CLITestV20Base, self).setUp()
- client.Client.EXTED_PLURALS.update(constants.PLURALS)
- client.Client.EXTED_PLURALS.update(plurals)
- self.metadata = {'plurals': client.Client.EXTED_PLURALS,
- 'xmlns': constants.XML_NS_V20,
- constants.EXT_NS: {'prefix':
- 'http://xxxx.yy.com'}}
- self.mox = mox.Mox()
- self.endurl = ENDURL
- self.fake_stdout = FakeStdout()
- self.useFixture(fixtures.MonkeyPatch('sys.stdout', self.fake_stdout))
- self.useFixture(fixtures.MonkeyPatch(
- 'neutronclient.neutron.v2_0.find_resourceid_by_name_or_id',
- self._find_resourceid))
- self.useFixture(fixtures.MonkeyPatch(
- 'neutronclient.neutron.v2_0.find_resourceid_by_id',
- self._find_resourceid))
- self.useFixture(fixtures.MonkeyPatch(
- 'neutronclient.v2_0.client.Client.get_attr_metadata',
- self._get_attr_metadata))
- self.client = client.Client(token=TOKEN, endpoint_url=self.endurl)
-
- 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,
- **kwargs):
- self.mox.StubOutWithMock(cmd, "get_client")
- self.mox.StubOutWithMock(self.client.httpclient, "request")
- cmd.get_client().MultipleTimes().AndReturn(self.client)
- non_admin_status_resources = ['subnet', 'floatingip', 'security_group',
- 'security_group_rule', 'qos_queue',
- 'network_gateway', 'gateway_device',
- 'credential', 'network_profile',
- 'policy_profile', 'ikepolicy',
- 'ipsecpolicy', 'metering_label',
- 'metering_label_rule', 'net_partition']
- if (resource in 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})
- self.client.format = self.format
- resstr = self.client.serialize(ress)
- # url method body
- resource_plural = neutronV2_0._get_resource_plural(resource,
- self.client)
- path = getattr(self.client, resource_plural + "_path")
- # Work around for LP #1217791. XML deserializer called from
- # MyComparator does not decodes XML string correctly.
- if self.format == 'json':
- mox_body = MyComparator(body, self.client)
- else:
- mox_body = self.client.serialize(body)
- 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))
- args.extend(['--request-format', self.format])
- self.mox.ReplayAll()
- cmd_parser = cmd.get_parser('create_' + 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 name:
- self.assertIn(name, _str)
-
- def _test_list_columns(self, cmd, resources_collection,
- resources_out, args=['-f', 'json']):
- self.mox.StubOutWithMock(cmd, "get_client")
- self.mox.StubOutWithMock(self.client.httpclient, "request")
- cmd.get_client().MultipleTimes().AndReturn(self.client)
- self.client.format = self.format
- resstr = self.client.serialize(resources_out)
-
- path = getattr(self.client, resources_collection + "_path")
- self.client.httpclient.request(
- end_url(path, format=self.format), 'GET',
- body=None,
- headers=mox.ContainsKeyValue(
- 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr))
- args.extend(['--request-format', self.format])
- self.mox.ReplayAll()
- cmd_parser = cmd.get_parser("list_" + resources_collection)
- 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):
- self.mox.StubOutWithMock(cmd, "get_client")
- self.mox.StubOutWithMock(self.client.httpclient, "request")
- cmd.get_client().MultipleTimes().AndReturn(self.client)
- if response_contents is None:
- contents = [{self.id_field: 'myid1', },
- {self.id_field: 'myid2', }, ]
- else:
- contents = response_contents
- reses = {resources: contents}
- self.client.format = self.format
- resstr = self.client.serialize(reses)
- # url method body
- query = ""
- args = base_args if base_args is not None else []
- if detail:
- args.append('-D')
- args.extend(['--request-format', self.format])
- 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 isinstance(tag, unicode):
- tag = urllib.quote(tag.encode('utf-8'))
- if query:
- query += "&tag=" + tag
- else:
- query = "tag=" + tag
- 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'
- fields_1.extend(fields_2)
- for field in fields_1:
- 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 += ['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, resources + "_path")
- 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_" + 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):
- 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")
- 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', }]}
- self.client.format = self.format
- resstr1 = self.client.serialize(reses1)
- resstr2 = self.client.serialize(reses2)
- self.client.httpclient.request(
- end_url(path, "", format=self.format), 'GET',
- body=None,
- headers=mox.ContainsKeyValue(
- 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr1))
- self.client.httpclient.request(
- end_url(path, fake_query, format=self.format), 'GET',
- body=None,
- headers=mox.ContainsKeyValue(
- 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr2))
- self.mox.ReplayAll()
- cmd_parser = cmd.get_parser("list_" + resources)
- args = ['--request-format', self.format]
- shell.run_command(cmd, cmd_parser, args)
- self.mox.VerifyAll()
- self.mox.UnsetStubs()
-
- def _test_update_resource(self, resource, cmd, myid, args, extrafields):
- self.mox.StubOutWithMock(cmd, "get_client")
- self.mox.StubOutWithMock(self.client.httpclient, "request")
- cmd.get_client().MultipleTimes().AndReturn(self.client)
- body = {resource: extrafields}
- path = getattr(self.client, resource + "_path")
- self.client.format = self.format
- # Work around for LP #1217791. XML deserializer called from
- # MyComparator does not decodes XML string correctly.
- if self.format == 'json':
- mox_body = MyComparator(body, self.client)
- else:
- mox_body = self.client.serialize(body)
- self.client.httpclient.request(
- MyUrlComparator(end_url(path % myid, format=self.format),
- self.client),
- 'PUT',
- body=mox_body,
- headers=mox.ContainsKeyValue(
- 'X-Auth-Token', TOKEN)).AndReturn((MyResp(204), None))
- args.extend(['--request-format', self.format])
- self.mox.ReplayAll()
- cmd_parser = cmd.get_parser("update_" + 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=[]):
- 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 = {resource:
- {self.id_field: myid,
- 'name': 'myname', }, }
- self.client.format = self.format
- resstr = self.client.serialize(expected_res)
- path = getattr(self.client, resource + "_path")
- self.client.httpclient.request(
- end_url(path % myid, query, format=self.format), 'GET',
- body=None,
- headers=mox.ContainsKeyValue(
- 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr))
- args.extend(['--request-format', self.format])
- self.mox.ReplayAll()
- cmd_parser = cmd.get_parser("show_" + 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_delete_resource(self, resource, cmd, myid, args):
- self.mox.StubOutWithMock(cmd, "get_client")
- self.mox.StubOutWithMock(self.client.httpclient, "request")
- cmd.get_client().MultipleTimes().AndReturn(self.client)
- path = getattr(self.client, resource + "_path")
- self.client.httpclient.request(
- end_url(path % myid, format=self.format), 'DELETE',
- body=None,
- headers=mox.ContainsKeyValue(
- 'X-Auth-Token', TOKEN)).AndReturn((MyResp(204), None))
- args.extend(['--request-format', self.format])
- self.mox.ReplayAll()
- cmd_parser = cmd.get_parser("delete_" + 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_update_resource_action(self, resource, cmd, myid, action, args,
- body, retval=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, 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))
- args.extend(['--request-format', self.format])
- self.mox.ReplayAll()
- cmd_parser = cmd.get_parser("delete_" + 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 ClientV2UnicodeTestJson(CLITestV20Base):
- def test_do_request(self):
- self.client.format = self.format
- self.mox.StubOutWithMock(self.client.httpclient, "request")
- unicode_text = u'\u7f51\u7edc'
- # url with unicode
- action = u'/test'
- expected_action = action.encode('utf-8')
- # query string with unicode
- params = {'test': unicode_text}
- expect_query = urllib.urlencode({'test':
- unicode_text.encode('utf-8')})
- # request body with unicode
- body = params
- expect_body = self.client.serialize(body)
- # headers with unicode
- self.client.httpclient.auth_token = unicode_text
- expected_auth_token = unicode_text.encode('utf-8')
-
- 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), expect_body))
-
- self.mox.ReplayAll()
- res_body = self.client.do_request('PUT', action, body=body,
- params=params)
- self.mox.VerifyAll()
- self.mox.UnsetStubs()
-
- # test response with unicode
- self.assertEqual(res_body, body)
-
-
-class ClientV2UnicodeTestXML(ClientV2UnicodeTestJson):
- format = 'xml'
-
-
-class CLITestV20ExceptionHandler(CLITestV20Base):
-
- def _test_exception_handler_v20(
- self, expected_exception, status_code, expected_msg,
- error_type=None, error_msg=None, error_detail=None,
- error_content=None):
- if error_content is None:
- error_content = {'NeutronError': {'type': error_type,
- 'message': error_msg,
- 'detail': error_detail}}
-
- e = self.assertRaises(expected_exception,
- client.exception_handler_v20,
- status_code, error_content)
- self.assertEqual(status_code, e.status_code)
-
- 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, '')
-
- def test_exception_handler_v20_neutron_known_error(self):
- error_msg = 'Network not found'
- error_detail = 'Network detail'
- self._test_exception_handler_v20(
- exceptions.NetworkNotFoundClient, 404,
- error_msg + '\n' + error_detail,
- 'NetworkNotFound', error_msg, error_detail)
-
- 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)
-
- def test_exception_handler_v20_neutron_unknown_error(self):
- error_msg = 'Unknown error'
- error_detail = 'This is detail'
- self._test_exception_handler_v20(
- exceptions.NeutronClientException, 400,
- error_msg + '\n' + error_detail,
- 'UnknownError', error_msg, error_detail)
-
- def test_exception_handler_v20_bad_neutron_error(self):
- error_content = {'NeutronError': {'unknown_key': 'UNKNOWN'}}
- self._test_exception_handler_v20(
- exceptions.NeutronClientException, 500,
- expected_msg={'unknown_key': 'UNKNOWN'},
- error_content=error_content)
-
- def test_exception_handler_v20_error_dict_contains_message(self):
- error_content = {'message': 'This is an error message'}
- self._test_exception_handler_v20(
- exceptions.NeutronClientException, 500,
- expected_msg='This is an error message',
- error_content=error_content)
-
- def test_exception_handler_v20_error_dict_not_contain_message(self):
- error_content = {'error': 'This is an error message'}
- expected_msg = '%s-%s' % (500, error_content)
- self._test_exception_handler_v20(
- exceptions.NeutronClientException, 500,
- expected_msg=expected_msg,
- error_content=error_content)
-
- def test_exception_handler_v20_default_fallback(self):
- error_content = 'This is an error message'
- expected_msg = '%s-%s' % (500, error_content)
- self._test_exception_handler_v20(
- exceptions.NeutronClientException, 500,
- expected_msg=expected_msg,
- error_content=error_content)
diff --git a/neutronclient/tests/unit/test_cli20_agenschedulers.py b/neutronclient/tests/unit/test_cli20_agenschedulers.py
deleted file mode 100644
index c0a807053..000000000
--- a/neutronclient/tests/unit/test_cli20_agenschedulers.py
+++ /dev/null
@@ -1,50 +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.
-#
-# @author: Oleg Bondarev, Mirantis Inc.
-#
-
-import sys
-
-from neutronclient.neutron.v2_0 import agentscheduler
-from neutronclient.tests.unit import test_cli20
-
-
-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 CLITestV20LBaaSAgentSchedulerXML(CLITestV20LBaaSAgentScheduler):
- format = 'xml'
diff --git a/neutronclient/tests/unit/test_cli20_agents.py b/neutronclient/tests/unit/test_cli20_agents.py
deleted file mode 100644
index 1e75a92bb..000000000
--- a/neutronclient/tests/unit/test_cli20_agents.py
+++ /dev/null
@@ -1,56 +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.
-#
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-import sys
-
-from neutronclient.common import utils
-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 = utils.loads(_str)
- self.assertEqual(1, len(returned_agents))
- ag = returned_agents[0]
- self.assertEqual(3, len(ag))
- self.assertEqual("alive", ag.keys()[2])
-
- 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 = utils.loads(_str)
- self.assertEqual(1, len(returned_agents))
- ag = returned_agents[0]
- self.assertEqual(1, len(ag))
- self.assertEqual("alive", ag.keys()[0])
- self.assertEqual(smile, ag.values()[0])
diff --git a/neutronclient/tests/unit/test_cli20_credential.py b/neutronclient/tests/unit/test_cli20_credential.py
deleted file mode 100644
index 53e8ecc8a..000000000
--- a/neutronclient/tests/unit/test_cli20_credential.py
+++ /dev/null
@@ -1,79 +0,0 @@
-# Copyright 2013 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.
-#
-# @author: Kyle Mestery, Cisco Systems, Inc.
-#
-
-import sys
-
-from neutronclient.neutron.v2_0 import credential
-from neutronclient.tests.unit import test_cli20
-
-
-class CLITestV20Credential(test_cli20.CLITestV20Base):
-
- def test_create_credential(self):
- """Create credential: myid."""
- resource = 'credential'
- cmd = credential.CreateCredential(test_cli20.MyApp(sys.stdout), None)
- name = 'myname'
- myid = 'myid'
- type = 'mytype'
- args = [name, type]
- position_names = ['credential_name', 'type']
- position_values = [name, type]
- self._test_create_resource(resource, cmd, name, myid, args,
- position_names, position_values)
-
- def test_list_credentials_detail(self):
- """List credentials: -D."""
- resources = 'credentials'
- cmd = credential.ListCredential(test_cli20.MyApp(sys.stdout), None)
- contents = [{'credential_name': 'myname', 'type': 'mytype'}]
- self._test_list_resources(resources, cmd, True,
- response_contents=contents)
-
- def test_list_credential_known_option_after_unknown(self):
- """List credential: -- --tags a b --request-format xml."""
- resources = 'credentials'
- cmd = credential.ListCredential(test_cli20.MyApp(sys.stdout), None)
- contents = [{'credential_name': 'myname', 'type': 'mytype'}]
- self._test_list_resources(resources, cmd, tags=['a', 'b'],
- response_contents=contents)
-
- def test_list_credential_fields(self):
- """List credential: --fields a --fields b -- --fields c d."""
- resources = 'credentials'
- cmd = credential.ListCredential(test_cli20.MyApp(sys.stdout), None)
- contents = [{'credential_name': 'myname', 'type': 'mytype'}]
- self._test_list_resources(resources, cmd,
- fields_1=['a', 'b'], fields_2=['c', 'd'],
- response_contents=contents)
-
- def test_show_credential(self):
- """Show credential: --fields id --fields name myid."""
- resource = 'credential'
- cmd = credential.ShowCredential(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_credential(self):
- """Delete credential: myid."""
- resource = 'credential'
- cmd = credential.DeleteCredential(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_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 3a98e2cf6..000000000
--- a/neutronclient/tests/unit/test_cli20_floatingips.py
+++ /dev/null
@@ -1,137 +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):
- 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]
- position_names = ['floating_network_id']
- position_values = [name]
- self._test_create_resource(resource, cmd, name, myid, args,
- position_names, position_values)
-
- 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_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"}
- )
-
-
-class CLITestV20FloatingIpsXML(CLITestV20FloatingIpsJSON):
- format = 'xml'
diff --git a/neutronclient/tests/unit/test_cli20_metering.py b/neutronclient/tests/unit/test_cli20_metering.py
deleted file mode 100644
index 995c29b6f..000000000
--- a/neutronclient/tests/unit/test_cli20_metering.py
+++ /dev/null
@@ -1,102 +0,0 @@
-# Copyright (C) 2013 eNovance SAS
-#
-# Author: Sylvain Afchain
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# 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):
- 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'])
-
-
-class CLITestV20MeteringXML(CLITestV20MeteringJSON):
- format = 'xml'
diff --git a/neutronclient/tests/unit/test_cli20_network.py b/neutronclient/tests/unit/test_cli20_network.py
deleted file mode 100644
index 5e9be2a69..000000000
--- a/neutronclient/tests/unit/test_cli20_network.py
+++ /dev/null
@@ -1,532 +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
-
-import mox
-
-from neutronclient.common import exceptions
-from neutronclient.common import utils
-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_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_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_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.end_url(path, query), '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=[]):
- 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)
-
- 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.end_url(path, query), '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.get_data(parsed_args)
- self.mox.VerifyAll()
- self.mox.UnsetStubs()
- _result = [x for x in result[1]]
- self.assertEqual(len(_result), len(expected))
- for res, exp in zip(_result, expected):
- self.assertEqual(len(res), len(exp))
- for a, b in zip(res, exp):
- self.assertEqual(a, b)
-
- 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 = utils.loads(_str)
- self.assertEqual(1, len(returned_networks))
- net = returned_networks[0]
- self.assertEqual(1, len(net))
- self.assertEqual("id", net.keys()[0])
-
- 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 = utils.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.end_url(path, query), '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)
- fields_1.extend(fields_2)
- for field in fields_1:
- 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.end_url(path, query), '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'],
- {'name': 'myname', 'tags': ['a', 'b'], }
- )
-
- 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_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_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.end_url(path, 'fields=id&fields=cidr' + filters),
- '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, "_check_uri_length")
- self.client._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._check_uri_length(mox.IgnoreArg()).AndReturn(None)
- self.client.httpclient.request(
- test_cli20.end_url(path,
- 'fields=id&fields=cidr%s' % filters),
- 'GET',
- body=None,
- headers=mox.ContainsKeyValue(
- 'X-Auth-Token', test_cli20.TOKEN)).AndReturn(response)
-
- self._test_extend_list(mox_calls)
-
-
-class CLITestV20NetworkXML(CLITestV20NetworkJSON):
- format = 'xml'
diff --git a/neutronclient/tests/unit/test_cli20_networkprofile.py b/neutronclient/tests/unit/test_cli20_networkprofile.py
deleted file mode 100644
index 21e386282..000000000
--- a/neutronclient/tests/unit/test_cli20_networkprofile.py
+++ /dev/null
@@ -1,110 +0,0 @@
-# Copyright 2013 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.
-#
-# @author: Kyle Mestery, Cisco Systems, Inc.
-#
-
-import sys
-
-from neutronclient.neutron.v2_0 import networkprofile
-from neutronclient.tests.unit import test_cli20
-
-
-class CLITestV20NetworkProfile(test_cli20.CLITestV20Base):
-
- def test_create_networkprofile(self):
- """Create networkprofile: myid."""
- resource = 'network_profile'
- cmd = networkprofile.CreateNetworkProfile(test_cli20.
- MyApp(sys.stdout), None)
- name = 'myname'
- myid = 'myid'
- segment_type = 'vlan'
- args = [name, segment_type]
- position_names = ['name', 'segment_type']
- position_values = [name, segment_type]
- self._test_create_resource(resource, cmd, name, myid, args,
- position_names, position_values)
-
- def test_list_networkprofile_detail(self):
- """List networkprofile: -D."""
- resources = 'network_profiles'
- cmd = networkprofile.ListNetworkProfile(test_cli20.MyApp(sys.stdout),
- None)
- contents = [{'name': 'myname', 'segment_type': 'vlan'}]
- self._test_list_resources(resources, cmd, True,
- response_contents=contents)
-
- def test_list_networkprofile_known_option_after_unknown(self):
- """List networkprofile: -- --tags a b --request-format xml."""
- resources = 'network_profiles'
- cmd = networkprofile.ListNetworkProfile(test_cli20.MyApp(sys.stdout),
- None)
- contents = [{'name': 'myname', 'segment_type': 'vlan'}]
- self._test_list_resources(resources, cmd, tags=['a', 'b'],
- response_contents=contents)
-
- def test_list_networkprofile_fields(self):
- """List networkprofile: --fields a --fields b -- --fields c d."""
- resources = 'network_profiles'
- cmd = networkprofile.ListNetworkProfile(test_cli20.MyApp(sys.stdout),
- None)
- contents = [{'name': 'myname', 'segment_type': 'vlan'}]
- self._test_list_resources(resources, cmd,
- fields_1=['a', 'b'], fields_2=['c', 'd'],
- response_contents=contents)
-
- def test_show_networkprofile(self):
- """Show networkprofile: --fields id --fields name myid."""
- resource = 'network_profile'
- cmd = networkprofile.ShowNetworkProfile(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_networkprofile(self):
- """Delete networkprofile: myid."""
- resource = 'network_profile'
- cmd = networkprofile.DeleteNetworkProfile(test_cli20.
- MyApp(sys.stdout), None)
- myid = 'myid'
- args = [myid]
- self._test_delete_resource(resource, cmd, myid, args)
-
- def test_create_networkprofile_trunk(self):
- """Create networkprofile: myid."""
- resource = 'network_profile'
- cmd = networkprofile.CreateNetworkProfile(test_cli20.
- MyApp(sys.stdout), None)
- name = 'myname'
- myid = 'myid'
- segment_type = 'trunk'
- args = [name, segment_type, '--sub_type', 'vlan']
- position_names = ['name', 'segment_type', ]
- position_values = [name, segment_type, ]
- self._test_create_resource(resource, cmd, name, myid, args,
- position_names, position_values,
- sub_type='vlan')
-
- def test_list_networkprofile_trunk_detail(self):
- """List networkprofile: -D."""
- resources = 'network_profiles'
- cmd = networkprofile.ListNetworkProfile(test_cli20.MyApp(sys.stdout),
- None)
- contents = [{'name': 'myname', 'segment_type': 'trunk',
- '--sub_type': 'vlan'}]
- self._test_list_resources(resources, cmd, True,
- response_contents=contents)
diff --git a/neutronclient/tests/unit/test_cli20_nsx_networkgateway.py b/neutronclient/tests/unit/test_cli20_nsx_networkgateway.py
deleted file mode 100644
index 2ff321e8e..000000000
--- a/neutronclient/tests/unit/test_cli20_nsx_networkgateway.py
+++ /dev/null
@@ -1,262 +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
-
-import mox
-
-from neutronclient.neutron.v2_0.nsx import networkgateway as nwgw
-from neutronclient.tests.unit import test_cli20
-
-
-class CLITestV20NetworkGatewayJSON(test_cli20.CLITestV20Base):
-
- gw_resource = "network_gateway"
- dev_resource = "gateway_device"
-
- def setUp(self):
- super(CLITestV20NetworkGatewayJSON, self).setUp(
- plurals={'devices': 'device',
- 'network_gateways': 'network_gateway'})
-
- def test_create_gateway(self):
- cmd = nwgw.CreateNetworkGateway(test_cli20.MyApp(sys.stdout), None)
- name = 'gw-test'
- myid = 'myid'
- args = [name, ]
- position_names = ['name', ]
- position_values = [name, ]
- self._test_create_resource(self.gw_resource, cmd, name, myid, args,
- position_names, position_values)
-
- def test_create_gateway_with_tenant(self):
- cmd = nwgw.CreateNetworkGateway(test_cli20.MyApp(sys.stdout), None)
- name = 'gw-test'
- myid = 'myid'
- args = ['--tenant_id', 'tenantid', name]
- position_names = ['name', ]
- position_values = [name, ]
- self._test_create_resource(self.gw_resource, cmd, name, myid, args,
- position_names, position_values,
- tenant_id='tenantid')
-
- def test_create_gateway_with_device(self):
- cmd = nwgw.CreateNetworkGateway(test_cli20.MyApp(sys.stdout), None)
- name = 'gw-test'
- myid = 'myid'
- args = ['--device', 'device_id=test', name, ]
- position_names = ['name', ]
- position_values = [name, ]
- self._test_create_resource(self.gw_resource, cmd, name, myid, args,
- position_names, position_values,
- devices=[{'device_id': 'test'}])
-
- def test_list_gateways(self):
- resources = '%ss' % self.gw_resource
- cmd = nwgw.ListNetworkGateway(test_cli20.MyApp(sys.stdout), None)
- self._test_list_resources(resources, cmd, True)
-
- def test_update_gateway(self):
- cmd = nwgw.UpdateNetworkGateway(test_cli20.MyApp(sys.stdout), None)
- self._test_update_resource(self.gw_resource, cmd, 'myid',
- ['myid', '--name', 'higuain'],
- {'name': 'higuain'})
-
- def test_delete_gateway(self):
- cmd = nwgw.DeleteNetworkGateway(test_cli20.MyApp(sys.stdout), None)
- myid = 'myid'
- args = [myid]
- self._test_delete_resource(self.gw_resource, cmd, myid, args)
-
- def test_show_gateway(self):
- cmd = nwgw.ShowNetworkGateway(test_cli20.MyApp(sys.stdout), None)
- args = ['--fields', 'id', '--fields', 'name', self.test_id]
- self._test_show_resource(self.gw_resource, cmd, self.test_id, args,
- ['id', 'name'])
-
- def test_connect_network_to_gateway(self):
- cmd = nwgw.ConnectNetworkGateway(test_cli20.MyApp(sys.stdout), None)
- args = ['gw_id', 'net_id',
- '--segmentation-type', 'edi',
- '--segmentation-id', '7']
- self._test_update_resource_action(self.gw_resource, cmd, 'gw_id',
- 'connect_network',
- args,
- {'network_id': 'net_id',
- 'segmentation_type': 'edi',
- 'segmentation_id': '7'})
-
- def test_disconnect_network_from_gateway(self):
- cmd = nwgw.DisconnectNetworkGateway(test_cli20.MyApp(sys.stdout), None)
- args = ['gw_id', 'net_id',
- '--segmentation-type', 'edi',
- '--segmentation-id', '7']
- self._test_update_resource_action(self.gw_resource, cmd, 'gw_id',
- 'disconnect_network',
- args,
- {'network_id': 'net_id',
- 'segmentation_type': 'edi',
- 'segmentation_id': '7'})
-
- def _test_create_gateway_device(self,
- name,
- connector_type,
- connector_ip,
- client_certificate=None,
- client_certificate_file=None,
- must_raise=False):
- cmd = nwgw.CreateGatewayDevice(test_cli20.MyApp(sys.stdout), None)
- myid = 'myid'
- extra_body = {'connector_type': connector_type,
- 'connector_ip': connector_ip,
- 'client_certificate': client_certificate}
- self.mox.StubOutWithMock(nwgw, 'read_cert_file')
- if client_certificate_file:
- nwgw.read_cert_file(mox.IgnoreArg()).AndReturn('xyz')
- extra_body['client_certificate'] = 'xyz'
- self.mox.ReplayAll()
- position_names = ['name', ]
- position_values = [name, ]
- args = []
- for (k, v) in extra_body.iteritems():
- if (k == 'client_certificate' and client_certificate_file):
- v = client_certificate_file
- k = 'client_certificate_file'
- # Append argument only if value for it was specified
- if v:
- args.extend(['--%s' % k.replace('_', '-'), v])
- # The following is just for verifying the call fails as expected when
- # both certificate and certificate file are specified. The extra
- # argument added is client-certificate since the loop above added
- # client-certificate-file
- if client_certificate_file and client_certificate:
- args.extend(['--client-certificate', client_certificate_file])
- args.append(name)
- if must_raise:
- self.assertRaises(
- SystemExit, self._test_create_resource,
- self.dev_resource, cmd, name, myid, args,
- position_names, position_values, extra_body=extra_body)
- else:
- self._test_create_resource(
- self.dev_resource, cmd, name, myid, args,
- position_names, position_values, extra_body=extra_body)
- self.mox.UnsetStubs()
-
- def test_create_gateway_device(self):
- self._test_create_gateway_device('dev_test', 'stt', '1.1.1.1', 'xyz')
-
- def test_create_gateway_device_with_certfile(self):
- self._test_create_gateway_device('dev_test', 'stt', '1.1.1.1',
- client_certificate_file='some_file')
-
- def test_create_gateway_device_invalid_connector_type_fails(self):
- self._test_create_gateway_device('dev_test', 'ciccio',
- '1.1.1.1', client_certificate='xyz',
- must_raise=True)
-
- def test_create_gateway_device_missing_connector_ip_fails(self):
- self._test_create_gateway_device('dev_test', 'stt',
- None, client_certificate='xyz',
- must_raise=True)
-
- def test_create_gateway_device_missing_certificates_fails(self):
- self._test_create_gateway_device('dev_test', 'stt', '1.1.1.1',
- must_raise=True)
-
- def test_create_gateway_device_with_cert_and_cert_file_fails(self):
- self._test_create_gateway_device('dev_test', 'stt', '1.1.1.1',
- client_certificate='xyz',
- client_certificate_file='some_file',
- must_raise=True)
-
- def _test_update_gateway_device(self,
- name=None,
- connector_type=None,
- connector_ip=None,
- client_certificate=None,
- client_certificate_file=None,
- must_raise=False):
- cmd = nwgw.UpdateGatewayDevice(test_cli20.MyApp(sys.stdout), None)
- myid = 'myid'
- extra_body = {}
- self.mox.StubOutWithMock(nwgw, 'read_cert_file')
- if client_certificate_file:
- nwgw.read_cert_file(mox.IgnoreArg()).AndReturn('xyz')
- self.mox.ReplayAll()
- args = [myid]
-
- def process_arg(argname, arg):
- if arg:
- extra_body[argname] = arg
- args.extend(['--%s' % argname.replace('_', '-'), arg])
-
- process_arg('name', name)
- process_arg('connector_type', connector_type)
- process_arg('connector_ip', connector_ip)
- process_arg('client_certificate', client_certificate)
- if client_certificate_file:
- extra_body['client_certificate'] = 'xyz'
- args.extend(['--client-certificate-file',
- client_certificate_file])
- if must_raise:
- self.assertRaises(
- SystemExit, self._test_update_resource,
- self.dev_resource, cmd, myid, args,
- extrafields=extra_body)
- else:
- self._test_update_resource(
- self.dev_resource, cmd, myid, args,
- extrafields=extra_body)
- self.mox.UnsetStubs()
-
- def test_update_gateway_device(self):
- self._test_update_gateway_device('dev_test', 'stt', '1.1.1.1', 'xyz')
-
- def test_update_gateway_device_partial_body(self):
- self._test_update_gateway_device(name='dev_test',
- connector_type='stt')
-
- def test_update_gateway_device_with_certfile(self):
- self._test_update_gateway_device('dev_test', 'stt', '1.1.1.1',
- client_certificate_file='some_file')
-
- def test_update_gateway_device_invalid_connector_type_fails(self):
- self._test_update_gateway_device('dev_test', 'ciccio',
- '1.1.1.1', client_certificate='xyz',
- must_raise=True)
-
- def test_update_gateway_device_with_cert_and_cert_file_fails(self):
- self._test_update_gateway_device('dev_test', 'stt', '1.1.1.1',
- client_certificate='xyz',
- client_certificate_file='some_file',
- must_raise=True)
-
- def test_delete_gateway_device(self):
- cmd = nwgw.DeleteGatewayDevice(test_cli20.MyApp(sys.stdout), None)
- myid = 'myid'
- args = [myid]
- self._test_delete_resource(self.dev_resource, cmd, myid, args)
-
- def test_show_gateway_device(self):
- cmd = nwgw.ShowGatewayDevice(test_cli20.MyApp(sys.stdout), None)
- args = ['--fields', 'id', '--fields', 'name', self.test_id]
- self._test_show_resource(self.dev_resource, cmd, self.test_id, args,
- ['id', 'name'])
-
-
-class CLITestV20NetworkGatewayXML(CLITestV20NetworkGatewayJSON):
- format = 'xml'
diff --git a/neutronclient/tests/unit/test_cli20_nsx_queue.py b/neutronclient/tests/unit/test_cli20_nsx_queue.py
deleted file mode 100644
index f65087347..000000000
--- a/neutronclient/tests/unit/test_cli20_nsx_queue.py
+++ /dev/null
@@ -1,86 +0,0 @@
-#!/usr/bin/env python
-# Copyright 2013 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.neutron.v2_0.nsx import qos_queue as qos
-from neutronclient.tests.unit import test_cli20
-
-
-class CLITestV20QosQueueJSON(test_cli20.CLITestV20Base):
- def setUp(self):
- super(CLITestV20QosQueueJSON, self).setUp(
- plurals={'qos_queues': 'qos_queue'})
-
- def test_create_qos_queue(self):
- """Create a qos queue."""
- resource = 'qos_queue'
- cmd = qos.CreateQoSQueue(
- test_cli20.MyApp(sys.stdout), None)
- myid = 'myid'
- name = 'my_queue'
- default = False
- args = ['--default', default, name]
- position_names = ['name', 'default']
- position_values = [name, default]
- self._test_create_resource(resource, cmd, name, myid, args,
- position_names, position_values)
-
- def test_create_qos_queue_all_values(self):
- """Create a qos queue."""
- resource = 'qos_queue'
- cmd = qos.CreateQoSQueue(
- test_cli20.MyApp(sys.stdout), None)
- myid = 'myid'
- name = 'my_queue'
- default = False
- min = '10'
- max = '40'
- qos_marking = 'untrusted'
- dscp = '0'
- args = ['--default', default, '--min', min, '--max', max,
- '--qos-marking', qos_marking, '--dscp', dscp, name]
- position_names = ['name', 'default', 'min', 'max', 'qos_marking',
- 'dscp']
- position_values = [name, default, min, max, qos_marking, dscp]
- self._test_create_resource(resource, cmd, name, myid, args,
- position_names, position_values)
-
- def test_list_qos_queue(self):
- resources = "qos_queues"
- cmd = qos.ListQoSQueue(
- test_cli20.MyApp(sys.stdout), None)
- self._test_list_resources(resources, cmd, True)
-
- def test_show_qos_queue_id(self):
- resource = 'qos_queue'
- cmd = qos.ShowQoSQueue(
- 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_delete_qos_queue(self):
- resource = 'qos_queue'
- cmd = qos.DeleteQoSQueue(
- test_cli20.MyApp(sys.stdout), None)
- myid = 'myid'
- args = [myid]
- self._test_delete_resource(resource, cmd, myid, args)
-
-
-class CLITestV20QosQueueXML(CLITestV20QosQueueJSON):
- format = 'xml'
diff --git a/neutronclient/tests/unit/test_cli20_nuage_netpartition.py b/neutronclient/tests/unit/test_cli20_nuage_netpartition.py
deleted file mode 100644
index b62d29688..000000000
--- a/neutronclient/tests/unit/test_cli20_nuage_netpartition.py
+++ /dev/null
@@ -1,59 +0,0 @@
-# Copyright 2014 Alcatel-Lucent USA Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-#
-# @author: Ronak Shah, Nuage Networks, Alcatel-Lucent USA Inc.
-
-import sys
-
-from neutronclient.neutron.v2_0 import netpartition
-from neutronclient.tests.unit import test_cli20
-
-
-class CLITestV20NetPartitionJSON(test_cli20.CLITestV20Base):
- resource = 'net_partition'
-
- def test_create_netpartition(self):
- cmd = netpartition.CreateNetPartition(test_cli20.MyApp(sys.stdout),
- None)
- name = 'myname'
- myid = 'myid'
- args = [name, ]
- position_names = ['name', ]
- position_values = [name, ]
- self._test_create_resource(self.resource, cmd, name, myid, args,
- position_names, position_values)
-
- def test_list_netpartitions(self):
- resources = '%ss' % self.resource
- cmd = netpartition.ListNetPartition(test_cli20.MyApp(sys.stdout),
- None)
- self._test_list_resources(resources, cmd, True)
-
- def test_show_netpartition(self):
- cmd = netpartition.ShowNetPartition(test_cli20.MyApp(sys.stdout),
- None)
- args = ['--fields', 'id', '--fields', 'name', self.test_id]
- self._test_show_resource(self.resource, cmd, self.test_id, args,
- ['id', 'name'])
-
- def test_delete_netpartition(self):
- cmd = netpartition.DeleteNetPartition(test_cli20.MyApp(sys.stdout),
- None)
- myid = 'myid'
- args = [myid]
- self._test_delete_resource(self.resource, cmd, myid, args)
-
-
-class CLITestV20NetPartitionXML(CLITestV20NetPartitionJSON):
- format = 'xml'
diff --git a/neutronclient/tests/unit/test_cli20_policyprofile.py b/neutronclient/tests/unit/test_cli20_policyprofile.py
deleted file mode 100644
index 791b840ba..000000000
--- a/neutronclient/tests/unit/test_cli20_policyprofile.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# Copyright 2013 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.
-#
-# @author: Kyle Mestery, Cisco Systems, Inc.
-#
-
-import sys
-
-from neutronclient.neutron.v2_0 import policyprofile
-from neutronclient.tests.unit import test_cli20
-
-
-class CLITestV20PolicyProfile(test_cli20.CLITestV20Base):
-
- def test_list_policyprofile_detail(self):
- """List policyprofile: -D."""
- resources = 'policy_profiles'
- cmd = policyprofile.ListPolicyProfile(test_cli20.MyApp(sys.stdout),
- None)
- contents = [{'name': 'myname', 'segment_type': 'vlan'}]
- self._test_list_resources(resources, cmd, True,
- response_contents=contents)
-
- def test_list_policyprofile_known_option_after_unknown(self):
- """List policyprofile: -- --tags a b --request-format xml."""
- resources = 'policy_profiles'
- cmd = policyprofile.ListPolicyProfile(test_cli20.MyApp(sys.stdout),
- None)
- contents = [{'name': 'myname', 'segment_type': 'vlan'}]
- self._test_list_resources(resources, cmd, tags=['a', 'b'],
- response_contents=contents)
-
- def test_list_policyprofile_fields(self):
- """List policyprofile: --fields a --fields b -- --fields c d."""
- resources = 'policy_profiles'
- cmd = policyprofile.ListPolicyProfile(test_cli20.MyApp(sys.stdout),
- None)
- contents = [{'name': 'myname', 'segment_type': 'vlan'}]
- self._test_list_resources(resources, cmd,
- fields_1=['a', 'b'], fields_2=['c', 'd'],
- response_contents=contents)
-
- def test_show_policyprofile(self):
- """Show policyprofile: --fields id --fields name myid."""
- resource = 'policy_profile'
- cmd = policyprofile.ShowPolicyProfile(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_policyprofile(self):
- """Update policyprofile: myid --name myname --tags a b."""
- resource = 'policy_profile'
- cmd = policyprofile.UpdatePolicyProfile(test_cli20.MyApp(sys.stdout),
- None)
- self._test_update_resource(resource, cmd, 'myid',
- ['myid', '--name', 'myname',
- '--tags', 'a', 'b'],
- {'name': 'myname', 'tags': ['a', 'b'], }
- )
diff --git a/neutronclient/tests/unit/test_cli20_port.py b/neutronclient/tests/unit/test_cli20_port.py
deleted file mode 100644
index 0f41f247f..000000000
--- a/neutronclient/tests/unit/test_cli20_port.py
+++ /dev/null
@@ -1,416 +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
-
-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]
- position_names = ['network_id']
- position_values = []
- 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(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_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_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-group', 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_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_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)
- fields_1.extend(fields_2)
- for field in fields_1:
- 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.end_url(path, query % myid), '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 --tags a b."""
- resource = 'port'
- cmd = port.UpdatePort(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_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_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_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)
-
-
-class CLITestV20PortXML(CLITestV20PortJSON):
- format = 'xml'
diff --git a/neutronclient/tests/unit/test_cli20_router.py b/neutronclient/tests/unit/test_cli20_router.py
deleted file mode 100644
index 274819ce2..000000000
--- a/neutronclient/tests/unit/test_cli20_router.py
+++ /dev/null
@@ -1,220 +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, ]
- position_names = ['name', ]
- position_values = [name, ]
- self._test_create_resource(resource, cmd, name, myid, args,
- position_names, position_values)
-
- 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 test_create_router_distributed(self):
- """Create router: --distributed myname."""
- resource = 'router'
- cmd = router.CreateRouter(test_cli20.MyApp(sys.stdout), None)
- name = 'myname'
- myid = 'myid'
- args = ['--distributed', name, ]
- position_names = ['name', ]
- position_values = [name, ]
- self._test_create_resource(resource, cmd, name, myid, args,
- position_names, position_values,
- distributed=True)
-
- 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'],
- {'name': 'myname'}
- )
-
- 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_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": {}}
- )
-
-
-class CLITestV20RouterXML(CLITestV20RouterJSON):
- format = 'xml'
diff --git a/neutronclient/tests/unit/test_cli20_securitygroup.py b/neutronclient/tests/unit/test_cli20_securitygroup.py
deleted file mode 100644
index 3ffd0232b..000000000
--- a/neutronclient/tests/unit/test_cli20_securitygroup.py
+++ /dev/null
@@ -1,331 +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 mox
-
-from neutronclient.neutron.v2_0 import securitygroup
-from neutronclient.tests.unit import test_cli20
-
-
-class CLITestV20SecurityGroupsJSON(test_cli20.CLITestV20Base):
- 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]
- 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_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, data=None, expected=None,
- args=[], conv=True,
- query_field=False):
- 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.end_url(path, query), 'GET',
- body=None,
- headers=mox.ContainsKeyValue(
- 'X-Auth-Token', test_cli20.TOKEN)).AndReturn(resp)
-
- # Setup the default data
- _data = {'cols': ['id', 'security_group_id', 'remote_group_id'],
- 'data': [('ruleid1', 'myid1', 'myid1'),
- ('ruleid2', 'myid2', 'myid3'),
- ('ruleid3', 'myid2', 'myid2')]}
- _expected = {'cols': ['id', 'security_group', 'remote_group'],
- 'data': [('ruleid1', 'group1', 'group1'),
- ('ruleid2', 'group2', 'group3'),
- ('ruleid3', 'group2', 'group2')]}
- if data is None:
- data = _data
- list_data = [dict(zip(data['cols'], d)) for d in data['data']]
- if expected is None:
- expected = {}
- expected['cols'] = expected.get('cols', _expected['cols'])
- expected['data'] = expected.get('data', _expected['data'])
-
- 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_field:
- query = '&'.join(['fields=' + f for f in data['cols']])
- setup_list_stub('security_group_rules', list_data, query)
- if conv:
- cmd.get_client().AndReturn(self.client)
- sec_ids = set()
- for n in data['data']:
- sec_ids.add(n[1])
- sec_ids.add(n[2])
- 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.get_data(parsed_args)
- self.mox.VerifyAll()
- self.mox.UnsetStubs()
- # Check columns
- self.assertEqual(result[0], expected['cols'])
- # Check data
- _result = [x for x in result[1]]
- self.assertEqual(len(_result), len(expected['data']))
- for res, exp in zip(_result, expected['data']):
- self.assertEqual(len(res), len(exp))
- self.assertEqual(res, exp)
-
- def test_list_security_group_rules_extend_source_id(self):
- self._test_list_security_group_rules_extend()
-
- def test_list_security_group_rules_extend_no_nameconv(self):
- expected = {'cols': ['id', 'security_group_id', 'remote_group_id'],
- 'data': [('ruleid1', 'myid1', 'myid1'),
- ('ruleid2', 'myid2', 'myid3'),
- ('ruleid3', 'myid2', 'myid2')]}
- args = ['--no-nameconv']
- self._test_list_security_group_rules_extend(expected=expected,
- args=args, conv=False)
-
- def test_list_security_group_rules_extend_with_columns(self):
- args = '-c id -c security_group_id -c remote_group_id'.split()
- self._test_list_security_group_rules_extend(args=args)
-
- def test_list_security_group_rules_extend_with_columns_no_id(self):
- args = '-c id -c security_group -c remote_group'.split()
- self._test_list_security_group_rules_extend(args=args)
-
- def test_list_security_group_rules_extend_with_fields(self):
- args = '-F id -F security_group_id -F remote_group_id'.split()
- self._test_list_security_group_rules_extend(args=args,
- query_field=True)
-
- def test_list_security_group_rules_extend_with_fields_no_id(self):
- args = '-F id -F security_group -F remote_group'.split()
- self._test_list_security_group_rules_extend(args=args,
- query_field=True)
-
-
-class CLITestV20SecurityGroupsXML(CLITestV20SecurityGroupsJSON):
- format = 'xml'
diff --git a/neutronclient/tests/unit/test_cli20_servicetype.py b/neutronclient/tests/unit/test_cli20_servicetype.py
deleted file mode 100644
index 0429a1a44..000000000
--- a/neutronclient/tests/unit/test_cli20_servicetype.py
+++ /dev/null
@@ -1,61 +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.
-#
-# @author: Eugene Nikanorov, Mirantis Inc.
-#
-
-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)
-
-
-class CLITestV20ServiceProvidersXML(CLITestV20ServiceProvidersJSON):
- format = 'xml'
diff --git a/neutronclient/tests/unit/test_cli20_subnet.py b/neutronclient/tests/unit/test_cli20_subnet.py
deleted file mode 100644
index 57b5901fc..000000000
--- a/neutronclient/tests/unit/test_cli20_subnet.py
+++ /dev/null
@@ -1,401 +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 neutronclient.neutron.v2_0 import subnet
-from neutronclient.tests.unit import test_cli20
-
-
-class CLITestV20SubnetJSON(test_cli20.CLITestV20Base):
- 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 = 'cidrvalue'
- gateway = 'gatewayvalue'
- args = ['--gateway', gateway, netid, 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_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]
- try:
- self._test_create_resource(resource, cmd, name, myid, args,
- position_names, position_values)
- except Exception:
- return
- self.fail('No exception for bad gateway option')
-
- 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_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_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_known_option_after_unknown(self):
- """List subnets: -- --tags a b --request-format xml."""
- 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',
- '--tags', 'a', 'b'],
- {'name': 'myname', 'tags': ['a', 'b'], }
- )
-
- def test_update_subnet_known_option_before_id(self):
- """Update subnet: --request-format json myid --name myname."""
- # --request-format xml is known option
- resource = 'subnet'
- cmd = subnet.UpdateSubnet(test_cli20.MyApp(sys.stdout), None)
- self._test_update_resource(resource, cmd, 'myid',
- ['--request-format', 'json',
- 'myid', '--name', 'myname'],
- {'name': 'myname', }
- )
-
- def test_update_subnet_known_option_after_id(self):
- """Update subnet: myid --name myname --request-format json."""
- # --request-format xml is known option
- resource = 'subnet'
- cmd = subnet.UpdateSubnet(test_cli20.MyApp(sys.stdout), None)
- self._test_update_resource(resource, cmd, 'myid',
- ['myid', '--name', 'myname',
- '--request-format', 'json'],
- {'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)
-
-
-class CLITestV20SubnetXML(CLITestV20SubnetJSON):
- format = 'xml'
diff --git a/neutronclient/tests/unit/test_command_meta.py b/neutronclient/tests/unit/test_command_meta.py
new file mode 100644
index 000000000..dedc3dd1c
--- /dev/null
+++ b/neutronclient/tests/unit/test_command_meta.py
@@ -0,0 +1,41 @@
+# Copyright 2013 Intel
+# Copyright 2013 Isaku Yamahata
+#
+# 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
+
+import testtools
+
+from neutronclient.neutron import v2_0 as neutronV20
+
+
+class TestCommandMeta(testtools.TestCase):
+ def test_neutron_command_meta_defines_log(self):
+ class FakeCommand(neutronV20.NeutronCommand):
+ pass
+
+ self.assertTrue(hasattr(FakeCommand, 'log'))
+ self.assertIsInstance(FakeCommand.log, logging.getLoggerClass())
+ self.assertEqual(__name__ + ".FakeCommand", FakeCommand.log.name)
+
+ def test_neutron_command_log_defined_explicitly(self):
+ class FakeCommand(neutronV20.NeutronCommand):
+ log = None
+
+ 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 1114f33be..5680d4375 100644
--- a/neutronclient/tests/unit/test_http.py
+++ b/neutronclient/tests/unit/test_http.py
@@ -13,73 +13,145 @@
# License for the specific language governing permissions and limitations
# under the License.
-import httplib2
-import mox
+import abc
+
+from oslo_utils import uuidutils
+import osprofiler.profiler
+import osprofiler.web
+from requests_mock.contrib import fixture as mock_fixture
import testtools
-from neutronclient.client import HTTPClient
+from neutronclient import client
from neutronclient.common import exceptions
-from neutronclient.tests.unit.test_cli20 import MyResp
AUTH_TOKEN = 'test_token'
END_URL = 'test_url'
METHOD = 'GET'
URL = 'http://test.test:1234/v2.0/test'
+BODY = 'IAMFAKE'
+
+class TestHTTPClientMixin(object, metaclass=abc.ABCMeta):
-class TestHTTPClient(testtools.TestCase):
def setUp(self):
- super(TestHTTPClient, self).setUp()
+ super(TestHTTPClientMixin, self).setUp()
+
+ self.requests = self.useFixture(mock_fixture.Fixture())
+ self.http = self.initialize()
+
+ @abc.abstractmethod
+ def initialize(self):
+ """Return client class, instance."""
+
+ def _test_headers(self, expected_headers, **kwargs):
+ # Test headers.
+ self.requests.register_uri(METHOD, URL,
+ request_headers=expected_headers)
+ self.http.request(URL, METHOD, **kwargs)
+ self.assertEqual(kwargs.get('body'), self.requests.last_request.body)
+
+ def test_headers_without_body(self):
+ self._test_headers({'Accept': 'application/json'})
+
+ def test_headers_with_body(self):
+ headers = {'Accept': 'application/json',
+ 'Content-Type': 'application/json'}
+ self._test_headers(headers, body=BODY)
+
+ def test_headers_without_body_with_content_type(self):
+ headers = {'Accept': 'application/json'}
+ self._test_headers(headers, content_type='application/json')
+
+ def test_headers_with_body_with_content_type(self):
+ headers = {'Accept': 'application/json',
+ 'Content-Type': 'application/json'}
+ self._test_headers(headers, body=BODY, content_type='application/json')
+
+ def test_headers_defined_in_headers(self):
+ headers = {'Accept': 'application/json',
+ '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)
+
- self.mox = mox.Mox()
- self.mox.StubOutWithMock(httplib2.Http, 'request')
- self.addCleanup(self.mox.UnsetStubs)
+class TestHTTPClient(TestHTTPClientMixin, testtools.TestCase):
- self.http = HTTPClient(token=AUTH_TOKEN, endpoint_url=END_URL)
+ def initialize(self):
+ return client.HTTPClient(token=AUTH_TOKEN, endpoint_url=END_URL)
def test_request_error(self):
- httplib2.Http.request(
- URL, METHOD, headers=mox.IgnoreArg()
- ).AndRaise(Exception('error msg'))
- self.mox.ReplayAll()
+ def cb(*args, **kwargs):
+ raise Exception('error msg')
+ self.requests.get(URL, body=cb)
self.assertRaises(
exceptions.ConnectionFailed,
self.http._cs_request,
URL, METHOD
)
- self.mox.VerifyAll()
def test_request_success(self):
- rv_should_be = MyResp(200), 'test content'
+ text = 'test content'
+ self.requests.register_uri(METHOD, URL, text=text)
- httplib2.Http.request(
- URL, METHOD, headers=mox.IgnoreArg()
- ).AndReturn(rv_should_be)
- self.mox.ReplayAll()
-
- self.assertEqual(rv_should_be, self.http._cs_request(URL, METHOD))
- self.mox.VerifyAll()
+ resp, resp_text = self.http._cs_request(URL, METHOD)
+ self.assertEqual(200, resp.status_code)
+ self.assertEqual(text, resp_text)
def test_request_unauthorized(self):
- rv_should_be = MyResp(401), 'unauthorized message'
- httplib2.Http.request(
- URL, METHOD, headers=mox.IgnoreArg()
- ).AndReturn(rv_should_be)
- self.mox.ReplayAll()
-
+ text = 'unauthorized message'
+ self.requests.register_uri(METHOD, URL, status_code=401, text=text)
e = self.assertRaises(exceptions.Unauthorized,
self.http._cs_request, URL, METHOD)
- self.assertEqual('unauthorized message', e.message)
- self.mox.VerifyAll()
+ self.assertEqual(text, e.message)
def test_request_forbidden_is_returned_to_caller(self):
- rv_should_be = MyResp(403), 'forbidden message'
- httplib2.Http.request(
- URL, METHOD, headers=mox.IgnoreArg()
- ).AndReturn(rv_should_be)
- self.mox.ReplayAll()
-
- self.assertEqual(rv_should_be, self.http._cs_request(URL, METHOD))
- self.mox.VerifyAll()
+ text = 'forbidden message'
+ self.requests.register_uri(METHOD, URL, status_code=403, text=text)
+
+ 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 5866f3209..000000000
--- a/neutronclient/tests/unit/test_name_or_id.py
+++ /dev/null
@@ -1,131 +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
-
-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.end_url(path, "fields=id&id=" + _id), '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.end_url(path, "fields=id&id=" + _id), 'GET',
- body=None,
- headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN)
- ).AndReturn((test_cli20.MyResp(200), resstr1))
- self.client.httpclient.request(
- test_cli20.end_url(path, "fields=id&name=" + _id), '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.end_url(path, "fields=id&name=" + name), '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.end_url(path, "fields=id&name=" + name), 'GET',
- body=None,
- headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN)
- ).AndReturn((test_cli20.MyResp(200), resstr))
- self.mox.ReplayAll()
- try:
- neutronV20.find_resourceid_by_name_or_id(
- self.client, 'network', name)
- except exceptions.NeutronClientNoUniqueMatch as ex:
- self.assertIn('Multiple', ex.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.end_url(path, "fields=id&name=" + name), 'GET',
- body=None,
- headers=mox.ContainsKeyValue('X-Auth-Token', test_cli20.TOKEN)
- ).AndReturn((test_cli20.MyResp(200), resstr))
- self.mox.ReplayAll()
- try:
- neutronV20.find_resourceid_by_name_or_id(
- self.client, 'network', name)
- except exceptions.NeutronClientException as ex:
- self.assertIn('Unable to find', ex.message)
- self.assertEqual(404, ex.status_code)
diff --git a/neutronclient/tests/unit/test_quota.py b/neutronclient/tests/unit/test_quota.py
deleted file mode 100644
index 5364a412e..000000000
--- a/neutronclient/tests/unit/test_quota.py
+++ /dev/null
@@ -1,44 +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 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)
diff --git a/neutronclient/tests/unit/test_shell.py b/neutronclient/tests/unit/test_shell.py
deleted file mode 100644
index 23f1d2dfc..000000000
--- a/neutronclient/tests/unit/test_shell.py
+++ /dev/null
@@ -1,172 +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 cStringIO
-import os
-import re
-import sys
-
-import fixtures
-import mox
-import testtools
-from testtools import matchers
-
-from neutronclient.common import exceptions
-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/'
-
-
-class NoExitArgumentParser(argparse.ArgumentParser):
- def error(self, message):
- raise exceptions.CommandError(message)
-
-
-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}
-
- def _tolerant_shell(self, cmd):
- t_shell = openstack_shell.NeutronShell('2.0')
- t_shell.run(cmd.split())
-
- # Patch os.environ to avoid required auth info.
- def setUp(self):
- super(ShellTest, self).setUp()
- self.mox = mox.Mox()
- for var in self.FAKE_ENV:
- self.useFixture(
- fixtures.EnvironmentVariable(
- var, self.FAKE_ENV[var]))
-
- # Make a fake shell object, a helping wrapper to call it, and a quick
- # way of asserting that certain API calls were made.
- global shell, _shell, assert_called, assert_called_anytime
- _shell = openstack_shell.NeutronShell('2.0')
- shell = lambda cmd: _shell.run(cmd.split())
-
- def shell(self, argstr):
- orig = sys.stdout
- clean_env = {}
- _old_env, os.environ = os.environ, clean_env.copy()
- try:
- sys.stdout = cStringIO.StringIO()
- _shell = openstack_shell.NeutronShell('2.0')
- _shell.run(argstr.split())
- except SystemExit:
- exc_type, exc_value, exc_traceback = sys.exc_info()
- self.assertEqual(exc_value.code, 0)
- finally:
- out = sys.stdout.getvalue()
- sys.stdout.close()
- sys.stdout = orig
- os.environ = _old_env
- return out
-
- def test_run_unknown_command(self):
- openstack_shell.NeutronShell('2.0').run('fake')
-
- def test_help(self):
- required = 'usage:'
- help_text = self.shell('help')
- self.assertThat(
- help_text,
- matchers.MatchesRegex(required))
-
- def test_help_on_subcommand(self):
- required = [
- '.*?^usage: .* quota-list']
- stdout = self.shell('help quota-list')
- for r in required:
- self.assertThat(
- stdout,
- matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE))
-
- def test_help_command(self):
- required = 'usage:'
- help_text = self.shell('help network-create')
- self.assertThat(
- help_text,
- matchers.MatchesRegex(required))
-
- def test_unknown_auth_strategy(self):
- self.shell('--os-auth-strategy fake quota-list')
-
- def test_auth(self):
- self.shell(' --os-username test'
- ' --os-password test'
- ' --os-tenant-name test'
- ' --os-auth-url http://127.0.0.1:5000/'
- ' --os-auth-strategy keystone quota-list')
-
- def test_build_option_parser(self):
- neutron_shell = openstack_shell.NeutronShell('2.0')
- result = neutron_shell.build_option_parser('descr', '2.0')
- self.assertEqual(True, isinstance(result, argparse.ArgumentParser))
-
- def test_main_with_unicode(self):
- self.mox.StubOutClassWithMocks(openstack_shell, 'NeutronShell')
- qshell_mock = openstack_shell.NeutronShell('2.0')
- #self.mox.StubOutWithMock(qshell_mock, 'run')
- unicode_text = u'\u7f51\u7edc'
- argv = ['net-list', unicode_text, unicode_text.encode('utf-8')]
- qshell_mock.run([u'net-list', unicode_text,
- unicode_text]).AndReturn(0)
- self.mox.ReplayAll()
- ret = openstack_shell.main(argv=argv)
- self.mox.VerifyAll()
- self.mox.UnsetStubs()
- self.assertEqual(ret, 0)
-
- def test_endpoint_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.assertEqual('publicURL', namespace.endpoint_type)
-
- # --endpoint-type but not $OS_ENDPOINT_TYPE
- namespace = parser.parse_args(['--endpoint-type=admin'])
- self.assertEqual('admin', namespace.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.endpoint_type)
-
- # --endpoint-type and $OS_ENDPOINT_TYPE
- namespace = parser.parse_args(['--endpoint-type=admin'])
- self.assertEqual('admin', namespace.endpoint_type)
diff --git a/neutronclient/tests/unit/test_ssl.py b/neutronclient/tests/unit/test_ssl.py
deleted file mode 100644
index 9f3970dd5..000000000
--- a/neutronclient/tests/unit/test_ssl.py
+++ /dev/null
@@ -1,140 +0,0 @@
-# Copyright (C) 2013 OpenStack Foundation.
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import fixtures
-import httplib2
-import mox
-import testtools
-
-from neutronclient.client import HTTPClient
-from neutronclient.common.clientmanager import ClientManager
-from neutronclient.common import exceptions
-from neutronclient import shell as openstack_shell
-
-
-AUTH_TOKEN = 'test_token'
-END_URL = 'test_url'
-METHOD = 'GET'
-URL = 'http://test.test:1234/v2.0/'
-CA_CERT = '/tmp/test/path'
-
-
-class TestSSL(testtools.TestCase):
- def setUp(self):
- super(TestSSL, self).setUp()
-
- self.useFixture(fixtures.EnvironmentVariable('OS_TOKEN', AUTH_TOKEN))
- self.useFixture(fixtures.EnvironmentVariable('OS_URL', END_URL))
-
- self.mox = mox.Mox()
- self.addCleanup(self.mox.UnsetStubs)
-
- def test_ca_cert_passed(self):
- self.mox.StubOutWithMock(ClientManager, '__init__')
- self.mox.StubOutWithMock(openstack_shell.NeutronShell, 'interact')
-
- ClientManager.__init__(
- ca_cert=CA_CERT,
- # we are not really interested in other args
- api_version=mox.IgnoreArg(),
- auth_strategy=mox.IgnoreArg(),
- auth_url=mox.IgnoreArg(),
- service_type=mox.IgnoreArg(),
- endpoint_type=mox.IgnoreArg(),
- insecure=mox.IgnoreArg(),
- password=mox.IgnoreArg(),
- region_name=mox.IgnoreArg(),
- tenant_id=mox.IgnoreArg(),
- tenant_name=mox.IgnoreArg(),
- token=mox.IgnoreArg(),
- url=mox.IgnoreArg(),
- username=mox.IgnoreArg(),
- log_credentials=mox.IgnoreArg(),
- )
- openstack_shell.NeutronShell.interact().AndReturn(0)
- self.mox.ReplayAll()
-
- openstack_shell.NeutronShell('2.0').run(['--os-cacert', CA_CERT])
- self.mox.VerifyAll()
-
- def test_ca_cert_passed_as_env_var(self):
- self.useFixture(fixtures.EnvironmentVariable('OS_CACERT', CA_CERT))
-
- self.mox.StubOutWithMock(ClientManager, '__init__')
- self.mox.StubOutWithMock(openstack_shell.NeutronShell, 'interact')
-
- ClientManager.__init__(
- ca_cert=CA_CERT,
- # we are not really interested in other args
- api_version=mox.IgnoreArg(),
- auth_strategy=mox.IgnoreArg(),
- auth_url=mox.IgnoreArg(),
- service_type=mox.IgnoreArg(),
- endpoint_type=mox.IgnoreArg(),
- insecure=mox.IgnoreArg(),
- password=mox.IgnoreArg(),
- region_name=mox.IgnoreArg(),
- tenant_id=mox.IgnoreArg(),
- tenant_name=mox.IgnoreArg(),
- token=mox.IgnoreArg(),
- url=mox.IgnoreArg(),
- username=mox.IgnoreArg(),
- log_credentials=mox.IgnoreArg(),
- )
- openstack_shell.NeutronShell.interact().AndReturn(0)
- self.mox.ReplayAll()
-
- openstack_shell.NeutronShell('2.0').run([])
- self.mox.VerifyAll()
-
- def test_client_manager_properly_creates_httpclient_instance(self):
- self.mox.StubOutWithMock(HTTPClient, '__init__')
- HTTPClient.__init__(
- ca_cert=CA_CERT,
- # we are not really interested in other args
- auth_strategy=mox.IgnoreArg(),
- auth_url=mox.IgnoreArg(),
- endpoint_url=mox.IgnoreArg(),
- insecure=mox.IgnoreArg(),
- password=mox.IgnoreArg(),
- region_name=mox.IgnoreArg(),
- tenant_name=mox.IgnoreArg(),
- token=mox.IgnoreArg(),
- username=mox.IgnoreArg(),
- )
- self.mox.ReplayAll()
-
- version = {'network': '2.0'}
- ClientManager(ca_cert=CA_CERT,
- api_version=version,
- url=END_URL,
- token=AUTH_TOKEN).neutron
- self.mox.VerifyAll()
-
- def test_proper_exception_is_raised_when_cert_validation_fails(self):
- http = HTTPClient(token=AUTH_TOKEN, endpoint_url=END_URL)
-
- self.mox.StubOutWithMock(httplib2.Http, 'request')
- httplib2.Http.request(
- URL, METHOD, headers=mox.IgnoreArg()
- ).AndRaise(httplib2.SSLHandshakeError)
- self.mox.ReplayAll()
-
- self.assertRaises(
- exceptions.SslCertificateValidationError,
- http._cs_request,
- URL, METHOD
- )
- self.mox.VerifyAll()
diff --git a/neutronclient/tests/unit/test_utils.py b/neutronclient/tests/unit/test_utils.py
index 002ef07a7..4806682b6 100644
--- a/neutronclient/tests/unit/test_utils.py
+++ b/neutronclient/tests/unit/test_utils.py
@@ -13,8 +13,9 @@
# License for the specific language governing permissions and limitations
# under the License.
-import datetime
-import sys
+import argparse
+
+from oslo_utils import netutils
import testtools
@@ -37,6 +38,56 @@ def test_string_to_dictionary(self):
expected = {'key1': 'value1', 'key2': 'value2'}
self.assertEqual(expected, utils.str2dict(input_str))
+ def test_none_string_to_dictionary(self):
+ input_str = ''
+ expected = {}
+ self.assertEqual(expected, utils.str2dict(input_str))
+ input_str = None
+ expected = {}
+ self.assertEqual(expected, utils.str2dict(input_str))
+
+ def test_invalid_string_to_dictionary(self):
+ input_str = 'invalid'
+ 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',
+ optional_keys=['key1', 'key2']))
+ self.assertDictEqual({'key1': 'value1', 'key2': 'value2'},
+ utils.str2dict('key1=value1,key2=value2',
+ optional_keys=['key1', 'key2']))
+ e = self.assertRaises(argparse.ArgumentTypeError,
+ utils.str2dict,
+ 'key1=value1,key2=value2,key3=value3',
+ optional_keys=['key1', 'key2'])
+ self.assertEqual("Invalid key(s) 'key3' specified. "
+ "Valid key(s): 'key1, key2'.",
+ str(e))
+
+ def test_str2dict_required_keys(self):
+ self.assertDictEqual(
+ {'key1': 'value1', 'key2': 'value2', 'key3': 'value3'},
+ utils.str2dict('key1=value1,key2=value2,key3=value3',
+ required_keys=['key1', 'key2'],
+ optional_keys=['key3']))
+ self.assertDictEqual(
+ {'key1': 'value1', 'key2': 'value2'},
+ utils.str2dict('key1=value1,key2=value2',
+ required_keys=['key1', 'key2']))
+ e = self.assertRaises(argparse.ArgumentTypeError,
+ utils.str2dict, 'key1=value1',
+ required_keys=['key1', 'key2'])
+ self.assertEqual("Required key(s) 'key2' not specified.", str(e))
+
def test_get_dict_item_properties(self):
item = {'name': 'test_name', 'id': 'test_id'}
fields = ('name', 'id')
@@ -97,92 +148,13 @@ def __call__(self, *args, **kwargs):
act = utils.get_item_properties(item, fields, formatters=formatters)
self.assertEqual(('test_name', 'test_id', 'test', 'pass'), act)
-
-class JSONUtilsTestCase(testtools.TestCase):
- def test_dumps(self):
- self.assertEqual(utils.dumps({'a': 'b'}), '{"a": "b"}')
-
- def test_dumps_dict_with_date_value(self):
- x = datetime.datetime(1920, 2, 3, 4, 5, 6, 7)
- res = utils.dumps({1: 'a', 2: x})
- expected = '{"1": "a", "2": "1920-02-03 04:05:06.000007"}'
- self.assertEqual(expected, res)
-
- def test_dumps_dict_with_spaces(self):
- x = datetime.datetime(1920, 2, 3, 4, 5, 6, 7)
- res = utils.dumps({1: 'a ', 2: x})
- expected = '{"1": "a ", "2": "1920-02-03 04:05:06.000007"}'
- self.assertEqual(expected, res)
-
- def test_loads(self):
- self.assertEqual(utils.loads('{"a": "b"}'), {'a': 'b'})
-
-
-class ToPrimitiveTestCase(testtools.TestCase):
- def test_list(self):
- self.assertEqual(utils.to_primitive([1, 2, 3]), [1, 2, 3])
-
- def test_empty_list(self):
- self.assertEqual(utils.to_primitive([]), [])
-
- def test_tuple(self):
- self.assertEqual(utils.to_primitive((1, 2, 3)), [1, 2, 3])
-
- def test_empty_tuple(self):
- self.assertEqual(utils.to_primitive(()), [])
-
- def test_dict(self):
- self.assertEqual(
- utils.to_primitive(dict(a=1, b=2, c=3)),
- dict(a=1, b=2, c=3))
-
- def test_empty_dict(self):
- self.assertEqual(utils.to_primitive({}), {})
-
- def test_datetime(self):
- x = datetime.datetime(1920, 2, 3, 4, 5, 6, 7)
- self.assertEqual(
- utils.to_primitive(x),
- '1920-02-03 04:05:06.000007')
-
- def test_iter(self):
- x = range(1, 6)
- self.assertEqual(utils.to_primitive(x), [1, 2, 3, 4, 5])
-
- def test_iteritems(self):
- d = {'a': 1, 'b': 2, 'c': 3}
-
- class IterItemsClass(object):
- def iteritems(self):
- return d.iteritems()
-
- x = IterItemsClass()
- p = utils.to_primitive(x)
- self.assertEqual(p, {'a': 1, 'b': 2, 'c': 3})
-
- def test_nasties(self):
- def foo():
- pass
- x = [datetime, foo, dir]
- ret = utils.to_primitive(x)
- self.assertEqual(len(ret), 3)
-
- def test_to_primitive_dict_with_date_value(self):
- x = datetime.datetime(1920, 2, 3, 4, 5, 6, 7)
- res = utils.to_primitive({'a': x})
- self.assertEqual({'a': '1920-02-03 04:05:06.000007'}, res)
+ def test_is_cidr(self):
+ 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):
- def test_import_class(self):
- dt = utils.import_class('datetime.datetime')
- self.assertTrue(sys.modules['datetime'].datetime is dt)
-
- def test_import_bad_class(self):
- self.assertRaises(
- ImportError, utils.import_class,
- 'lol.u_mad.brah')
-
def test_get_client_class_invalid_version(self):
self.assertRaises(
exceptions.UnsupportedVersion,
diff --git a/neutronclient/tests/unit/test_validators.py b/neutronclient/tests/unit/test_validators.py
new file mode 100644
index 000000000..a92496baa
--- /dev/null
+++ b/neutronclient/tests/unit/test_validators.py
@@ -0,0 +1,101 @@
+# Copyright 2014 NEC Corporation
+# All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import testtools
+
+from neutronclient.common import exceptions
+from neutronclient.common import validators
+
+
+class FakeParsedArgs(object):
+ pass
+
+
+class ValidatorTest(testtools.TestCase):
+
+ def _test_validate_int(self, attr_val, attr_name='attr1',
+ min_value=1, max_value=10):
+ obj = FakeParsedArgs()
+ setattr(obj, attr_name, attr_val)
+ ret = validators.validate_int_range(obj, attr_name,
+ min_value, max_value)
+ # Come here only if there is no exception.
+ self.assertIsNone(ret)
+
+ def _test_validate_int_error(self, attr_val, expected_msg,
+ attr_name='attr1', expected_exc=None,
+ min_value=1, max_value=10):
+ if expected_exc is None:
+ expected_exc = exceptions.CommandError
+ e = self.assertRaises(expected_exc,
+ self._test_validate_int,
+ attr_val, attr_name, min_value, max_value)
+ self.assertEqual(expected_msg, str(e))
+
+ def test_validate_int_min_max(self):
+ self._test_validate_int(1)
+ self._test_validate_int(10)
+ self._test_validate_int('1')
+ self._test_validate_int('10')
+ self._test_validate_int('0x0a')
+
+ self._test_validate_int_error(
+ 0, 'attr1 "0" should be an integer [1:10].')
+ self._test_validate_int_error(
+ 11, 'attr1 "11" should be an integer [1:10].')
+ self._test_validate_int_error(
+ '0x10', 'attr1 "0x10" should be an integer [1:10].')
+
+ def test_validate_int_min_only(self):
+ self._test_validate_int(1, max_value=None)
+ self._test_validate_int(10, max_value=None)
+ self._test_validate_int(11, max_value=None)
+ self._test_validate_int_error(
+ 0, 'attr1 "0" should be an integer greater than or equal to 1.',
+ max_value=None)
+
+ def test_validate_int_max_only(self):
+ self._test_validate_int(0, min_value=None)
+ self._test_validate_int(1, min_value=None)
+ self._test_validate_int(10, min_value=None)
+ self._test_validate_int_error(
+ 11, 'attr1 "11" should be an integer smaller than or equal to 10.',
+ min_value=None)
+
+ def test_validate_int_no_limit(self):
+ self._test_validate_int(0, min_value=None, max_value=None)
+ self._test_validate_int(1, min_value=None, max_value=None)
+ self._test_validate_int(10, min_value=None, max_value=None)
+ self._test_validate_int(11, min_value=None, max_value=None)
+ self._test_validate_int_error(
+ 'abc', 'attr1 "abc" should be an integer.',
+ min_value=None, max_value=None)
+
+ def _test_validate_subnet(self, attr_val, attr_name='attr1'):
+ obj = FakeParsedArgs()
+ setattr(obj, attr_name, attr_val)
+ ret = validators.validate_ip_subnet(obj, attr_name)
+ # Come here only if there is no exception.
+ self.assertIsNone(ret)
+
+ def test_validate_ip_subnet(self):
+ self._test_validate_subnet('192.168.2.0/24')
+ self._test_validate_subnet('192.168.2.3/20')
+ self._test_validate_subnet('192.168.2.1')
+
+ e = self.assertRaises(exceptions.CommandError,
+ self._test_validate_subnet,
+ '192.168.2.256')
+ self.assertEqual('attr1 "192.168.2.256" is not a valid CIDR.', str(e))
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 fc5a1a4d0..000000000
--- a/neutronclient/tests/unit/vpn/test_cli20_ikepolicy.py
+++ /dev/null
@@ -1,212 +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.
-#
-# @author: Swaminathan Vasudevan, Hewlett Packard.
-
-import sys
-
-from neutronclient.neutron.v2_0.vpn import ikepolicy
-from neutronclient.tests.unit import test_cli20
-
-
-class CLITestV20VpnIkePolicyJSON(test_cli20.CLITestV20Base):
-
- def test_create_ikepolicy_all_params(self):
- """vpn-ikepolicy-create all params."""
- 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'
- 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,
- },
- }
-
- self._test_create_resource(resource, cmd, name, my_id, args,
- position_names, position_values,
- extra_body=extra_body)
-
- 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):
- 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]
- try:
- self._test_create_resource(resource, cmd, name, my_id, args,
- position_names, position_values)
- except Exception:
- return
- self.fail("IKEPolicy Lifetime Error")
-
- def test_create_ikepolicy_with_invalid_lifetime_keys(self):
- lifetime = 'uts=seconds,val=20000'
- self._test_lifetime_values(lifetime)
-
- 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)
-
-
-class CLITestV20VpnIkePolicyXML(CLITestV20VpnIkePolicyJSON):
- format = 'xml'
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 077db1e16..000000000
--- a/neutronclient/tests/unit/vpn/test_cli20_ipsec_site_connection.py
+++ /dev/null
@@ -1,307 +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.
-#
-# @author: Swaminathan Vasudevan, Hewlett Packard.
-
-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):
-
- def test_create_ipsec_site_connection_all_params(self):
- """ipsecsite-connection-create all params."""
- 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_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'
- peer_cidrs = ['192.168.3.0/24', '192.168.2.0/24']
- 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,
- '--peer-cidr', '192.168.3.0/24',
- '--peer-cidr', '192.168.2.0/24']
-
- position_names = ['tenant_id', 'admin_state_up',
- 'peer_address', 'peer_id', 'peer_cidrs',
- 'psk', 'mtu', 'initiator',
- 'vpnservice_id', 'ikepolicy_id',
- 'ipsecpolicy_id']
-
- position_values = [tenant_id, admin_state, peer_address,
- peer_id, peer_cidrs, 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_dpd_values(self, dpd):
- """ipsecsite-connection-create with invalid dpd values."""
- 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'
-
- 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]
- self.assertRaises(
- exceptions.CommandError,
- self._test_create_resource,
- resource, cmd, name, my_id, args,
- position_names, position_values)
-
- def test_invalid_mtu(self):
- """ipsecsite-connection-create with invalid dpd values."""
- 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 = '67'
- 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'
-
- 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']
-
- 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]
- self.assertRaises(
- exceptions.CommandError,
- self._test_create_resource,
- resource, cmd, name, my_id, args,
- position_names, position_values)
-
- def test_create_ipsec_site_connection_with_invalid_dpd_keys(self):
- dpd = 'act=restart,interval=30,time=120'
- self._test_dpd_values(dpd)
-
- def test_create_ipsec_site_connection_with_invalid_dpd_values(self):
- dpd = 'action=hold,interval=30,timeout=-1'
- self._test_dpd_values(dpd)
-
- 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'])
-
-
-class CLITestV20IPsecSiteConnectionXML(CLITestV20IPsecSiteConnectionJSON):
- format = 'xml'
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 ca413a053..000000000
--- a/neutronclient/tests/unit/vpn/test_cli20_ipsecpolicy.py
+++ /dev/null
@@ -1,209 +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.
-#
-# @author: Swaminathan Vasudevan, Hewlett Packard.
-
-import sys
-
-from neutronclient.neutron.v2_0.vpn import ipsecpolicy
-from neutronclient.tests.unit import test_cli20
-
-
-class CLITestV20VpnIpsecPolicyJSON(test_cli20.CLITestV20Base):
-
- def test_create_ipsecpolicy_all_params(self):
- """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 = 'sha1'
- 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,
- },
- }
-
- self._test_create_resource(resource, cmd, name, my_id, args,
- position_names, position_values,
- extra_body=extra_body)
-
- 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):
- 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]
- try:
- self._test_create_resource(resource, cmd, name, my_id, args,
- position_names, position_values)
- except Exception:
- return
- self.fail("IPsecPolicy Lifetime Error")
-
- def test_create_ipsecpolicy_with_invalid_lifetime_keys(self):
- lifetime = 'uts=seconds,val=20000'
- self._test_lifetime_values(lifetime)
-
- def test_create_ipsecpolicy_with_invalide_lifetime_values(self):
- lifetime = 'units=minutes,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)
-
-
-class CLITestV20VpnIpsecPolicyXML(CLITestV20VpnIpsecPolicyJSON):
- format = 'xml'
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 8afd081dc..000000000
--- a/neutronclient/tests/unit/vpn/test_cli20_vpnservice.py
+++ /dev/null
@@ -1,138 +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.
-#
-# @author: Swaminathan Vasudevan, Hewlett Packard.
-
-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_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)
-
-
-class CLITestV20VpnServiceXML(CLITestV20VpnServiceJSON):
- format = 'xml'
diff --git a/neutronclient/tests/unit/vpn/test_utils.py b/neutronclient/tests/unit/vpn/test_utils.py
deleted file mode 100644
index 7b815a55a..000000000
--- a/neutronclient/tests/unit/vpn/test_utils.py
+++ /dev/null
@@ -1,131 +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.
-#
-# @author: Swaminathan Vasudevan, Hewlett Packard.
-
-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 7805bc1a4..82676b1c1 100644
--- a/neutronclient/v2_0/client.py
+++ b/neutronclient/v2_0/client.py
@@ -1,4 +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
@@ -14,51 +16,48 @@
# under the License.
#
-import httplib
+import inspect
+import itertools
import logging
+import re
import time
-import urllib
+import urllib.parse as urlparse
-import six.moves.urllib.parse as urlparse
+import debtcollector.renames
+from keystoneauth1 import exceptions as ksa_exc
+import requests
+from neutronclient._i18n import _
from neutronclient import client
-from neutronclient.common import _
-from neutronclient.common import constants
from neutronclient.common import exceptions
+from neutronclient.common import extension as client_extension
from neutronclient.common import serializer
from neutronclient.common import utils
_logger = logging.getLogger(__name__)
-NEUTRON_ERRORS = {
- 'NetworkNotFound': exceptions.NetworkNotFoundClient,
- 'NetworkInUse': exceptions.NetworkInUseClient,
- 'PortNotFound': exceptions.PortNotFoundClient,
- 'RequestedStateInvalid': exceptions.StateInvalidClient,
- 'PortInUse': exceptions.PortInUseClient,
- 'IpAddressInUse': exceptions.IpAddressInUseClient,
- 'AlreadyAttached': exceptions.AlreadyAttachedClient,
- 'IpAddressGenerationFailure': exceptions.IpAddressGenerationFailureClient,
- 'ExternalIpAddressExhausted': exceptions.ExternalIpAddressExhaustedClient,
-}
+HEX_ELEM = '[0-9A-Fa-f]'
+UUID_PATTERN = '-'.join([HEX_ELEM + '{8}', HEX_ELEM + '{4}',
+ HEX_ELEM + '{4}', HEX_ELEM + '{4}',
+ HEX_ELEM + '{12}'])
def exception_handler_v20(status_code, error_content):
- """Exception handler for API v2.0 client
+ """Exception handler for API v2.0 client.
- This routine generates the appropriate
- Neutron exception according to the contents of the
- response body
+ This routine generates the appropriate Neutron exception according to
+ the contents of the response body.
- :param status_code: HTTP error status code
- :param error_content: deserialized body of error response
+ :param status_code: HTTP error status code
+ :param error_content: deserialized body of error response
"""
error_dict = None
+ request_ids = error_content.request_ids
if isinstance(error_content, dict):
error_dict = error_content.get('NeutronError')
# Find real error type
- bad_neutron_error_flag = False
+ client_exc = None
if error_dict:
# If Neutron key is found, it will definitely contain
# a 'message' and 'type' keys?
@@ -67,59 +66,138 @@ def exception_handler_v20(status_code, error_content):
error_message = error_dict['message']
if error_dict['detail']:
error_message += "\n" + error_dict['detail']
+ # If corresponding exception is defined, use it.
+ client_exc = getattr(exceptions, '%sClient' % error_type, None)
except Exception:
- bad_neutron_error_flag = True
- if not bad_neutron_error_flag:
- try:
- # raise the appropriate error!
- raise NEUTRON_ERRORS[error_type](message=error_message,
- status_code=status_code)
- except KeyError:
- raise exceptions.NeutronClientException(
- status_code=status_code, message=error_message)
- else:
- raise exceptions.NeutronClientException(status_code=status_code,
- message=error_dict)
+ error_message = "%s" % error_dict
else:
- message = None
+ error_message = None
if isinstance(error_content, dict):
- message = error_content.get('message')
- if message:
- raise exceptions.NeutronClientException(status_code=status_code,
- message=message)
+ error_message = error_content.get('message')
+ if not error_message:
+ # If we end up here the exception was not a neutron error
+ error_message = "%s-%s" % (status_code, error_content)
+
+ # If an exception corresponding to the error type is not found,
+ # look up per status-code client exception.
+ if not client_exc:
+ client_exc = exceptions.HTTP_EXCEPTION_MAP.get(status_code)
+ # If there is no exception per status-code,
+ # Use NeutronClientException as fallback.
+ if not client_exc:
+ client_exc = exceptions.NeutronClientException
+
+ raise client_exc(message=error_message,
+ status_code=status_code,
+ request_ids=request_ids)
+
+
+class _RequestIdMixin(object):
+ """Wrapper class to expose x-openstack-request-id to the caller."""
+ def _request_ids_setup(self):
+ self._request_ids = []
+
+ @property
+ def request_ids(self):
+ return self._request_ids
+
+ def _append_request_ids(self, resp):
+ """Add request_ids as an attribute to the object
+
+ :param resp: Response object or list of Response objects
+ """
+ if isinstance(resp, list):
+ # Add list of request_ids if response is of type list.
+ for resp_obj in resp:
+ self._append_request_id(resp_obj)
+ elif resp is not None:
+ # Add request_ids if response contains single object.
+ self._append_request_id(resp)
+
+ def _append_request_id(self, resp):
+ if isinstance(resp, requests.Response):
+ # Extract 'x-openstack-request-id' from headers if
+ # response is a Response object.
+ request_id = resp.headers.get('x-openstack-request-id')
+ else:
+ # If resp is of type string.
+ request_id = resp
+ if request_id:
+ self._request_ids.append(request_id)
- # If we end up here the exception was not a neutron error
- msg = "%s-%s" % (status_code, error_content)
- raise exceptions.NeutronClientException(status_code=status_code,
- message=msg)
+class _DictWithMeta(dict, _RequestIdMixin):
+ def __init__(self, values, resp):
+ super(_DictWithMeta, self).__init__(values)
+ self._request_ids_setup()
+ self._append_request_ids(resp)
-class APIParamsCall(object):
- """A Decorator to add support for format and tenant overriding
- and filters
- """
- def __init__(self, function):
- self.function = function
- def __get__(self, instance, owner):
- def with_params(*args, **kwargs):
- _format = instance.format
- if 'format' in kwargs:
- instance.format = kwargs['format']
- ret = self.function(instance, *args, **kwargs)
- instance.format = _format
- return ret
- return with_params
+class _TupleWithMeta(tuple, _RequestIdMixin):
+ def __new__(cls, values, resp):
+ return super(_TupleWithMeta, cls).__new__(cls, values)
+
+ def __init__(self, values, resp):
+ self._request_ids_setup()
+ self._append_request_ids(resp)
+
+
+class _StrWithMeta(str, _RequestIdMixin):
+ def __new__(cls, value, resp):
+ return super(_StrWithMeta, cls).__new__(cls, value)
+
+ def __init__(self, values, resp):
+ self._request_ids_setup()
+ self._append_request_ids(resp)
+
+
+class _GeneratorWithMeta(_RequestIdMixin):
+ def __init__(self, paginate_func, collection, path, **params):
+ self.paginate_func = paginate_func
+ self.collection = collection
+ self.path = path
+ self.params = params
+ self.generator = None
+ self._request_ids_setup()
+
+ def _paginate(self):
+ for r in self.paginate_func(
+ self.collection, self.path, **self.params):
+ yield r, r.request_ids
+
+ def __iter__(self):
+ return self
+
+ # Python 3 compatibility
+ def __next__(self):
+ return self.next()
+
+ def next(self):
+ if not self.generator:
+ self.generator = self._paginate()
+ try:
+ obj, req_id = next(self.generator)
+ self._append_request_ids(req_id)
+ except StopIteration:
+ raise StopIteration()
+
+ return obj
-class Client(object):
+
+class ClientBase(object):
"""Client for the OpenStack Neutron v2.0 API.
:param string username: Username for authentication. (optional)
+ :param string user_id: User ID for authentication. (optional)
:param string password: Password for authentication. (optional)
:param string token: Token for authentication. (optional)
- :param string tenant_name: Tenant name. (optional)
- :param string tenant_id: Tenant id. (optional)
+ :param string tenant_name: DEPRECATED! Use project_name instead.
+ :param string project_name: Project name. (optional)
+ :param string tenant_id: DEPRECATED! Use project_id instead.
+ :param string project_id: Project id. (optional)
+ :param string auth_strategy: 'keystone' by default, 'noauth' for no
+ authentication against keystone. (optional)
:param string auth_url: Keystone service endpoint for authorization.
:param string service_type: Network service type to pull from the
keystone catalog (e.g. 'network') (optional)
@@ -135,39 +213,333 @@ class Client(object):
:param integer timeout: Allows customization of the timeout for client
http requests. (optional)
:param bool insecure: SSL certificate validation. (optional)
+ :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).
+ :param bool raise_errors: If True then exceptions caused by connection
+ failure are propagated to the caller.
+ (default: True)
+ :param session: Keystone client auth session to use. (optional)
+ :param auth: Keystone auth plugin to use. (optional)
Example::
from neutronclient.v2_0 import client
neutron = client.Client(username=USER,
password=PASS,
- tenant_name=TENANT_NAME,
+ project_name=PROJECT_NAME,
auth_url=KEYSTONE_URL)
nets = neutron.list_networks()
...
-
"""
+ # API has no way to report plurals, so we have to hard code them
+ # This variable should be overridden by a child class.
+ EXTED_PLURALS = {}
+
+ @debtcollector.renames.renamed_kwarg(
+ 'tenant_id', 'project_id', replace=True)
+ 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.action_prefix = "/v%s" % (self.version)
+ self.retry_interval = 1
+
+ def _handle_fault_response(self, status_code, response_body, resp):
+ # Create exception with HTTP status code and message
+ _logger.debug("Error message: %s", response_body)
+ # Add deserialized error message to exception arguments
+ try:
+ des_error_body = self.deserialize(response_body, status_code)
+ except Exception:
+ # If unable to deserialized body it is probably not a
+ # Neutron error
+ des_error_body = {'message': response_body}
+ error_body = self._convert_into_with_meta(des_error_body, resp)
+ # Raise the appropriate exception
+ exception_handler_v20(status_code, error_body)
+
+ def do_request(self, method, action, body=None, headers=None, params=None):
+ # Add format and project_id
+ action = self.action_prefix + action
+ if isinstance(params, dict) and params:
+ params = utils.safe_encode_dict(params)
+ action += '?' + urlparse.urlencode(params, doseq=1)
+
+ if body:
+ body = self.serialize(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,
+ requests.codes.created,
+ requests.codes.accepted,
+ requests.codes.no_content):
+ data = self.deserialize(replybody, status_code)
+ return self._convert_into_with_meta(data, resp)
+ else:
+ if not replybody:
+ replybody = resp.reason
+ self._handle_fault_response(status_code, replybody, resp)
+
+ def get_auth_info(self):
+ return self.httpclient.get_auth_info()
+
+ def serialize(self, data):
+ """Serializes a dictionary into JSON.
+
+ A dictionary with a single key can be passed and it can contain any
+ structure.
+ """
+ if data is None:
+ return None
+ elif isinstance(data, dict):
+ return serializer.Serializer().serialize(data)
+ else:
+ raise Exception(_("Unable to serialize object of type = '%s'") %
+ type(data))
+
+ def deserialize(self, data, status_code):
+ """Deserializes a JSON string into a dictionary."""
+ if not data:
+ return data
+ return serializer.Serializer().deserialize(
+ data)['body']
+
+ def retry_request(self, method, action, body=None,
+ headers=None, params=None):
+ """Call do_request with the default retry configuration.
+
+ Only idempotent requests should retry failed connection attempts.
+ :raises: ConnectionFailed if the maximum # of retries is exceeded
+ """
+ max_attempts = self.retries + 1
+ for i in range(max_attempts):
+ try:
+ return self.do_request(method, action, body=body,
+ headers=headers, params=params)
+ 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')
+ time.sleep(self.retry_interval)
+ elif self.raise_errors:
+ raise
+
+ if self.retries:
+ msg = (_("Failed to connect to Neutron server after %d attempts")
+ % max_attempts)
+ else:
+ msg = _("Failed to connect Neutron server")
+
+ raise exceptions.ConnectionFailed(reason=msg)
+
+ def delete(self, action, body=None, headers=None, params=None):
+ return self.retry_request("DELETE", action, body=body,
+ headers=headers, params=params)
+
+ def get(self, action, body=None, headers=None, params=None):
+ return self.retry_request("GET", action, body=body,
+ headers=headers, params=params)
+
+ def post(self, action, body=None, headers=None, params=None):
+ # Do not retry POST requests to avoid the orphan objects problem.
+ return self.do_request("POST", action, body=body,
+ headers=headers, params=params)
+
+ def put(self, action, body=None, headers=None, params=None):
+ return self.retry_request("PUT", action, body=body,
+ headers=headers, params=params)
+
+ def list(self, collection, path, retrieve_all=True, **params):
+ if retrieve_all:
+ res = []
+ request_ids = []
+ for r in self._pagination(collection, path, **params):
+ res.extend(r[collection])
+ request_ids.extend(r.request_ids)
+ return _DictWithMeta({collection: res}, request_ids)
+ else:
+ return _GeneratorWithMeta(self._pagination, collection,
+ path, **params)
+
+ def _pagination(self, collection, path, **params):
+ if params.get('page_reverse', False):
+ linkrel = 'previous'
+ else:
+ linkrel = 'next'
+ next = True
+ while next:
+ res = self.get(path, params=params)
+ yield res
+ next = False
+ try:
+ for link in res['%s_links' % collection]:
+ if link['rel'] == linkrel:
+ query_str = urlparse.urlparse(link['href']).query
+ params = urlparse.parse_qs(query_str)
+ next = True
+ break
+ except KeyError:
+ break
+
+ def _convert_into_with_meta(self, item, resp):
+ if item:
+ if isinstance(item, dict):
+ return _DictWithMeta(item, resp)
+ elif isinstance(item, str):
+ return _StrWithMeta(item, resp)
+ else:
+ return _TupleWithMeta((), resp)
+
+ def get_resource_plural(self, resource):
+ for k in self.EXTED_PLURALS:
+ if self.EXTED_PLURALS[k] == resource:
+ return k
+ return resource + 's'
+
+ def find_resource_by_id(self, resource, resource_id, cmd_resource=None,
+ parent_id=None, fields=None):
+ if not cmd_resource:
+ cmd_resource = resource
+ cmd_resource_plural = self.get_resource_plural(cmd_resource)
+ resource_plural = self.get_resource_plural(resource)
+ # TODO(amotoki): Use show_%s instead of list_%s
+ obj_lister = getattr(self, "list_%s" % cmd_resource_plural)
+ # perform search by id only if we are passing a valid UUID
+ match = re.match(UUID_PATTERN, resource_id)
+ collection = resource_plural
+ if match:
+ params = {'id': resource_id}
+ if fields:
+ params['fields'] = fields
+ if parent_id:
+ data = obj_lister(parent_id, **params)
+ else:
+ data = obj_lister(**params)
+ if data and data[collection]:
+ return data[collection][0]
+ not_found_message = (_("Unable to find %(resource)s with id "
+ "'%(id)s'") %
+ {'resource': resource, 'id': resource_id})
+ # 404 is raised by exceptions.NotFound to simulate serverside behavior
+ raise exceptions.NotFound(message=not_found_message)
+
+ def _find_resource_by_name(self, resource, name, project_id=None,
+ cmd_resource=None, parent_id=None, fields=None):
+ if not cmd_resource:
+ cmd_resource = resource
+ cmd_resource_plural = self.get_resource_plural(cmd_resource)
+ resource_plural = self.get_resource_plural(resource)
+ obj_lister = getattr(self, "list_%s" % cmd_resource_plural)
+ params = {'name': name}
+ if fields:
+ params['fields'] = fields
+ if project_id:
+ params['tenant_id'] = project_id
+ if parent_id:
+ data = obj_lister(parent_id, **params)
+ else:
+ data = obj_lister(**params)
+ collection = resource_plural
+ info = data[collection]
+ if len(info) > 1:
+ raise exceptions.NeutronClientNoUniqueMatch(resource=resource,
+ name=name)
+ elif len(info) == 0:
+ not_found_message = (_("Unable to find %(resource)s with name "
+ "'%(name)s'") %
+ {'resource': resource, 'name': name})
+ # 404 is raised by exceptions.NotFound
+ # to simulate serverside behavior
+ raise exceptions.NotFound(message=not_found_message)
+ else:
+ return info[0]
+
+ def find_resource(self, resource, name_or_id, project_id=None,
+ cmd_resource=None, parent_id=None, fields=None):
+ try:
+ return self.find_resource_by_id(resource, name_or_id,
+ cmd_resource, parent_id, fields)
+ except exceptions.NotFound:
+ try:
+ return self._find_resource_by_name(
+ resource, name_or_id, project_id,
+ cmd_resource, parent_id, fields)
+ except exceptions.NotFound:
+ not_found_message = (_("Unable to find %(resource)s with name "
+ "or id '%(name_or_id)s'") %
+ {'resource': resource,
+ 'name_or_id': name_or_id})
+ raise exceptions.NotFound(
+ message=not_found_message)
+
+
+class Client(ClientBase):
+
networks_path = "/networks"
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"
+ address_scope_path = "/address-scopes/%s"
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"
vpnservice_path = "/vpn/vpnservices/%s"
ipsecpolicies_path = "/vpn/ipsecpolicies"
@@ -176,6 +548,24 @@ class Client(object):
ikepolicy_path = "/vpn/ikepolicies/%s"
ipsec_site_connections_path = "/vpn/ipsec-site-connections"
ipsec_site_connection_path = "/vpn/ipsec-site-connections/%s"
+
+ lbaas_loadbalancers_path = "/lbaas/loadbalancers"
+ lbaas_loadbalancer_path = "/lbaas/loadbalancers/%s"
+ lbaas_loadbalancer_path_stats = "/lbaas/loadbalancers/%s/stats"
+ lbaas_loadbalancer_path_status = "/lbaas/loadbalancers/%s/statuses"
+ lbaas_listeners_path = "/lbaas/listeners"
+ lbaas_listener_path = "/lbaas/listeners/%s"
+ lbaas_l7policies_path = "/lbaas/l7policies"
+ lbaas_l7policy_path = lbaas_l7policies_path + "/%s"
+ lbaas_l7rules_path = lbaas_l7policy_path + "/rules"
+ lbaas_l7rule_path = lbaas_l7rules_path + "/%s"
+ lbaas_pools_path = "/lbaas/pools"
+ lbaas_pool_path = "/lbaas/pools/%s"
+ lbaas_healthmonitors_path = "/lbaas/healthmonitors"
+ lbaas_healthmonitor_path = "/lbaas/healthmonitors/%s"
+ lbaas_members_path = lbaas_pool_path + "/members"
+ lbaas_member_path = lbaas_pool_path + "/members/%s"
+
vips_path = "/lb/vips"
vip_path = "/lb/vips/%s"
pools_path = "/lb/pools"
@@ -197,24 +587,19 @@ class Client(object):
gateway_devices_path = "/gateway-devices"
gateway_device_path = "/gateway-devices/%s"
service_providers_path = "/service-providers"
- credentials_path = "/credentials"
- credential_path = "/credentials/%s"
- network_profiles_path = "/network_profiles"
- network_profile_path = "/network_profiles/%s"
- network_profile_bindings_path = "/network_profile_bindings"
- policy_profiles_path = "/policy_profiles"
- policy_profile_path = "/policy_profiles/%s"
- policy_profile_bindings_path = "/policy_profile_bindings"
metering_labels_path = "/metering/metering-labels"
metering_label_path = "/metering/metering-labels/%s"
metering_label_rules_path = "/metering/metering-label-rules"
metering_label_rule_path = "/metering/metering-label-rules/%s"
+
DHCP_NETS = '/dhcp-networks'
DHCP_AGENTS = '/dhcp-agents'
L3_ROUTERS = '/l3-routers'
L3_AGENTS = '/l3-agents'
LOADBALANCER_POOLS = '/loadbalancer-pools'
LOADBALANCER_AGENT = '/loadbalancer-agent'
+ AGENT_LOADBALANCERS = '/agent-loadbalancers'
+ LOADBALANCER_HOSTING_AGENT = '/loadbalancer-hosting-agent'
firewall_rules_path = "/fw/firewall_rules"
firewall_rule_path = "/fw/firewall_rules/%s"
firewall_policies_path = "/fw/firewall_policies"
@@ -223,8 +608,77 @@ class Client(object):
firewall_policy_remove_path = "/fw/firewall_policies/%s/remove_rule"
firewalls_path = "/fw/firewalls"
firewall_path = "/fw/firewalls/%s"
- net_partitions_path = "/net-partitions"
- net_partition_path = "/net-partitions/%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"
+ flavor_path = "/flavors/%s"
+ service_profiles_path = "/service_profiles"
+ service_profile_path = "/service_profiles/%s"
+ flavor_profile_bindings_path = flavor_path + service_profiles_path
+ flavor_profile_binding_path = flavor_path + service_profile_path
+ availability_zones_path = "/availability_zones"
+ auto_allocated_topology_path = "/auto-allocated-topology/%s"
+ BGP_DRINSTANCES = "/bgp-drinstances"
+ BGP_DRINSTANCE = "/bgp-drinstance/%s"
+ BGP_DRAGENTS = "/bgp-dragents"
+ BGP_DRAGENT = "/bgp-dragents/%s"
+ bgp_speakers_path = "/bgp-speakers"
+ bgp_speaker_path = "/bgp-speakers/%s"
+ bgp_peers_path = "/bgp-peers"
+ bgp_peer_path = "/bgp-peers/%s"
+ network_ip_availabilities_path = '/network-ip-availabilities'
+ network_ip_availability_path = '/network-ip-availabilities/%s'
+ tags_path = "/%s/%s/tags"
+ tag_path = "/%s/%s/tags/%s"
+ trunks_path = "/trunks"
+ trunk_path = "/trunks/%s"
+ 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',
@@ -233,10 +687,12 @@ class Client(object):
'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',
'vpnservices': 'vpnservice',
+ 'endpoint_groups': 'endpoint_group',
'vips': 'vip',
'pools': 'pool',
'members': 'member',
@@ -246,1082 +702,1930 @@ class Client(object):
'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',
- 'net_partitions': 'net_partition'
+ 'loadbalancers': 'loadbalancer',
+ 'listeners': 'listener',
+ 'l7rules': 'l7rule',
+ 'l7policies': 'l7policy',
+ 'lbaas_l7policies': 'lbaas_l7policy',
+ 'lbaas_pools': 'lbaas_pool',
+ 'lbaas_healthmonitors': 'lbaas_healthmonitor',
+ 'lbaas_members': 'lbaas_member',
+ 'healthmonitors': 'healthmonitor',
+ 'rbac_policies': 'rbac_policy',
+ 'address_scopes': 'address_scope',
+ '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',
+ 'flavors': 'flavor',
+ 'bgp_speakers': 'bgp_speaker',
+ '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',
}
- # 8192 Is the default max URI len for eventlet.wsgi.server
- MAX_URI_LEN = 8192
-
- def get_attr_metadata(self):
- if self.format == 'json':
- return {}
- old_request_format = self.format
- self.format = 'json'
- exts = self.list_extensions()['extensions']
- self.format = old_request_format
- ns = dict([(ext['alias'], ext['namespace']) for ext in exts])
- self.EXTED_PLURALS.update(constants.PLURALS)
- return {'plurals': self.EXTED_PLURALS,
- 'xmlns': constants.XML_NS_V20,
- constants.EXT_NS: ns}
-
- @APIParamsCall
+
+ def list_ext(self, collection, path, retrieve_all, **_params):
+ """Client extension hook for list."""
+ return self.list(collection, path, retrieve_all, **_params)
+
+ def show_ext(self, path, id, **_params):
+ """Client extension hook for show."""
+ return self.get(path % id, params=_params)
+
+ def create_ext(self, path, body=None):
+ """Client extension hook for create."""
+ return self.post(path, body=body)
+
+ def update_ext(self, path, id, body=None):
+ """Client extension hook for update."""
+ return self.put(path % id, body=body)
+
+ def delete_ext(self, path, id):
+ """Client extension hook for delete."""
+ return self.delete(path % id)
+
def get_quotas_tenant(self, **_params):
- """Fetch tenant info in server's context for
- following quota operation.
- """
+ """Fetch project info for following quota operation."""
return self.get(self.quota_path % 'tenant', params=_params)
- @APIParamsCall
def list_quotas(self, **_params):
- """Fetch all tenants' quotas."""
+ """Fetch all projects' quotas."""
return self.get(self.quotas_path, params=_params)
- @APIParamsCall
- def show_quota(self, tenant_id, **_params):
- """Fetch information of a certain tenant's quotas."""
- return self.get(self.quota_path % (tenant_id), params=_params)
+ @debtcollector.renames.renamed_kwarg(
+ 'tenant_id', 'project_id', replace=True)
+ 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):
+ """Fetch information of a certain project's default quotas."""
+ return self.get(self.quota_default_path % (project_id), params=_params)
- @APIParamsCall
- def update_quota(self, tenant_id, body=None):
- """Update a tenant's quotas."""
- return self.put(self.quota_path % (tenant_id), body=body)
+ @debtcollector.renames.renamed_kwarg(
+ 'tenant_id', 'project_id', replace=True)
+ def update_quota(self, project_id, body=None):
+ """Update a project's quotas."""
+ return self.put(self.quota_path % (project_id), body=body)
- @APIParamsCall
- def delete_quota(self, tenant_id):
- """Delete the specified tenant's quota values."""
- return self.delete(self.quota_path % (tenant_id))
+ @debtcollector.renames.renamed_kwarg(
+ 'tenant_id', 'project_id', replace=True)
+ def delete_quota(self, project_id):
+ """Delete the specified project's quota values."""
+ return self.delete(self.quota_path % (project_id))
- @APIParamsCall
def list_extensions(self, **_params):
- """Fetch a list of all exts on server side."""
+ """Fetch a list of all extensions on server side."""
return self.get(self.extensions_path, params=_params)
- @APIParamsCall
def show_extension(self, ext_alias, **_params):
- """Fetch a list of all exts on server side."""
+ """Fetches information of a certain extension."""
return self.get(self.extension_path % ext_alias, params=_params)
- @APIParamsCall
def list_ports(self, retrieve_all=True, **_params):
- """Fetches a list of all networks for a tenant."""
+ """Fetches a list of all ports for a project."""
# Pass filters in "params" argument to do_request
return self.list('ports', self.ports_path, retrieve_all,
**_params)
- @APIParamsCall
def show_port(self, port, **_params):
- """Fetches information of a certain network."""
+ """Fetches information of a certain port."""
return self.get(self.port_path % (port), params=_params)
- @APIParamsCall
def create_port(self, body=None):
"""Creates a new port."""
return self.post(self.ports_path, body=body)
- @APIParamsCall
- 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)
- @APIParamsCall
def delete_port(self, port):
"""Deletes the specified port."""
return self.delete(self.port_path % (port))
- @APIParamsCall
+ 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 tenant."""
+ """Fetches a list of all networks for a project."""
# Pass filters in "params" argument to do_request
return self.list('networks', self.networks_path, retrieve_all,
**_params)
- @APIParamsCall
def show_network(self, network, **_params):
"""Fetches information of a certain network."""
return self.get(self.network_path % (network), params=_params)
- @APIParamsCall
def create_network(self, body=None):
"""Creates a new network."""
return self.post(self.networks_path, body=body)
- @APIParamsCall
- 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)
- @APIParamsCall
def delete_network(self, network):
"""Deletes the specified network."""
return self.delete(self.network_path % (network))
- @APIParamsCall
def list_subnets(self, retrieve_all=True, **_params):
- """Fetches a list of all networks for a tenant."""
+ """Fetches a list of all subnets for a project."""
return self.list('subnets', self.subnets_path, retrieve_all,
**_params)
- @APIParamsCall
def show_subnet(self, subnet, **_params):
"""Fetches information of a certain subnet."""
return self.get(self.subnet_path % (subnet), params=_params)
- @APIParamsCall
def create_subnet(self, body=None):
"""Creates a new subnet."""
return self.post(self.subnets_path, body=body)
- @APIParamsCall
- 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)
- @APIParamsCall
def delete_subnet(self, subnet):
"""Deletes the specified subnet."""
return self.delete(self.subnet_path % (subnet))
- @APIParamsCall
+ def list_subnetpools(self, retrieve_all=True, **_params):
+ """Fetches a list of all subnetpools for a project."""
+ return self.list('subnetpools', self.subnetpools_path, retrieve_all,
+ **_params)
+
+ def show_subnetpool(self, subnetpool, **_params):
+ """Fetches information of a certain subnetpool."""
+ return self.get(self.subnetpool_path % (subnetpool), params=_params)
+
+ 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, revision_number=None):
+ """Updates a subnetpool."""
+ return self._update_resource(self.subnetpool_path % (subnetpool),
+ body=body,
+ revision_number=revision_number)
+
+ def delete_subnetpool(self, subnetpool):
+ """Deletes the specified subnetpool."""
+ return self.delete(self.subnetpool_path % (subnetpool))
+
def list_routers(self, retrieve_all=True, **_params):
- """Fetches a list of all routers for a tenant."""
+ """Fetches a list of all routers for a project."""
# Pass filters in "params" argument to do_request
return self.list('routers', self.routers_path, retrieve_all,
**_params)
- @APIParamsCall
def show_router(self, router, **_params):
"""Fetches information of a certain router."""
return self.get(self.router_path % (router), params=_params)
- @APIParamsCall
def create_router(self, body=None):
"""Creates a new router."""
return self.post(self.routers_path, body=body)
- @APIParamsCall
- 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)
- @APIParamsCall
def delete_router(self, router):
"""Deletes the specified router."""
return self.delete(self.router_path % (router))
- @APIParamsCall
+ def list_address_scopes(self, retrieve_all=True, **_params):
+ """Fetches a list of all address scopes for a project."""
+ return self.list('address_scopes', self.address_scopes_path,
+ retrieve_all, **_params)
+
+ def show_address_scope(self, address_scope, **_params):
+ """Fetches information of a certain address scope."""
+ return self.get(self.address_scope_path % (address_scope),
+ params=_params)
+
+ def create_address_scope(self, body=None):
+ """Creates a new address scope."""
+ return self.post(self.address_scopes_path, body=body)
+
+ def update_address_scope(self, address_scope, body=None):
+ """Updates a address scope."""
+ return self.put(self.address_scope_path % (address_scope), body=body)
+
+ def delete_address_scope(self, address_scope):
+ """Deletes the specified address scope."""
+ return self.delete(self.address_scope_path % (address_scope))
+
def add_interface_router(self, router, body=None):
"""Adds an internal network interface to the specified router."""
return self.put((self.router_path % router) + "/add_router_interface",
body=body)
- @APIParamsCall
def remove_interface_router(self, router, body=None):
"""Removes an internal network interface from the specified router."""
return self.put((self.router_path % router) +
"/remove_router_interface", body=body)
- @APIParamsCall
+ 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),
body={'router': {'external_gateway_info': body}})
- @APIParamsCall
def remove_gateway_router(self, router):
"""Removes an external network gateway from the specified router."""
return self.put((self.router_path % router),
body={'router': {'external_gateway_info': {}}})
- @APIParamsCall
def list_floatingips(self, retrieve_all=True, **_params):
- """Fetches a list of all floatingips for a tenant."""
+ """Fetches a list of all floatingips for a project."""
# Pass filters in "params" argument to do_request
return self.list('floatingips', self.floatingips_path, retrieve_all,
**_params)
- @APIParamsCall
def show_floatingip(self, floatingip, **_params):
"""Fetches information of a certain floatingip."""
return self.get(self.floatingip_path % (floatingip), params=_params)
- @APIParamsCall
def create_floatingip(self, body=None):
"""Creates a new floatingip."""
return self.post(self.floatingips_path, body=body)
- @APIParamsCall
- 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)
- @APIParamsCall
def delete_floatingip(self, floatingip):
"""Deletes the specified floatingip."""
return self.delete(self.floatingip_path % (floatingip))
- @APIParamsCall
+ 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)
- @APIParamsCall
- 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)
- @APIParamsCall
def list_security_groups(self, retrieve_all=True, **_params):
- """Fetches a list of all security groups for a tenant."""
+ """Fetches a list of all security groups for a project."""
return self.list('security_groups', self.security_groups_path,
retrieve_all, **_params)
- @APIParamsCall
def show_security_group(self, security_group, **_params):
"""Fetches information of a certain security group."""
return self.get(self.security_group_path % (security_group),
params=_params)
- @APIParamsCall
def delete_security_group(self, security_group):
"""Deletes the specified security group."""
return self.delete(self.security_group_path % (security_group))
- @APIParamsCall
def create_security_group_rule(self, body=None):
"""Creates a new security group rule."""
return self.post(self.security_group_rules_path, body=body)
- @APIParamsCall
def delete_security_group_rule(self, security_group_rule):
"""Deletes the specified security group rule."""
return self.delete(self.security_group_rule_path %
(security_group_rule))
- @APIParamsCall
def list_security_group_rules(self, retrieve_all=True, **_params):
- """Fetches a list of all security group rules for a tenant."""
+ """Fetches a list of all security group rules for a project."""
return self.list('security_group_rules',
self.security_group_rules_path,
retrieve_all, **_params)
- @APIParamsCall
def show_security_group_rule(self, security_group_rule, **_params):
"""Fetches information of a certain security group rule."""
return self.get(self.security_group_rule_path % (security_group_rule),
params=_params)
- @APIParamsCall
+ 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,
+ retrieve_all, **_params)
+
+ def show_endpoint_group(self, endpointgroup, **_params):
+ """Fetches information for a specific VPN endpoint group."""
+ return self.get(self.endpoint_group_path % endpointgroup,
+ params=_params)
+
+ def create_endpoint_group(self, body=None):
+ """Creates a new VPN endpoint group."""
+ return self.post(self.endpoint_groups_path, body=body)
+
+ def update_endpoint_group(self, endpoint_group, body=None):
+ """Updates a VPN endpoint group."""
+ return self.put(self.endpoint_group_path % endpoint_group, body=body)
+
+ def delete_endpoint_group(self, endpoint_group):
+ """Deletes the specified VPN endpoint group."""
+ return self.delete(self.endpoint_group_path % endpoint_group)
+
def list_vpnservices(self, retrieve_all=True, **_params):
- """Fetches a list of all configured VPNServices for a tenant."""
+ """Fetches a list of all configured VPN services for a project."""
return self.list('vpnservices', self.vpnservices_path, retrieve_all,
**_params)
- @APIParamsCall
def show_vpnservice(self, vpnservice, **_params):
- """Fetches information of a specific VPNService."""
+ """Fetches information of a specific VPN service."""
return self.get(self.vpnservice_path % (vpnservice), params=_params)
- @APIParamsCall
def create_vpnservice(self, body=None):
- """Creates a new VPNService."""
+ """Creates a new VPN service."""
return self.post(self.vpnservices_path, body=body)
- @APIParamsCall
def update_vpnservice(self, vpnservice, body=None):
- """Updates a VPNService."""
+ """Updates a VPN service."""
return self.put(self.vpnservice_path % (vpnservice), body=body)
- @APIParamsCall
def delete_vpnservice(self, vpnservice):
- """Deletes the specified VPNService."""
+ """Deletes the specified VPN service."""
return self.delete(self.vpnservice_path % (vpnservice))
- @APIParamsCall
def list_ipsec_site_connections(self, retrieve_all=True, **_params):
- """Fetches all configured IPsecSiteConnections for a tenant."""
+ """Fetches all configured IPsecSiteConnections for a project."""
return self.list('ipsec_site_connections',
self.ipsec_site_connections_path,
retrieve_all,
**_params)
- @APIParamsCall
def show_ipsec_site_connection(self, ipsecsite_conn, **_params):
"""Fetches information of a specific IPsecSiteConnection."""
return self.get(
self.ipsec_site_connection_path % (ipsecsite_conn), params=_params
)
- @APIParamsCall
def create_ipsec_site_connection(self, body=None):
"""Creates a new IPsecSiteConnection."""
return self.post(self.ipsec_site_connections_path, body=body)
- @APIParamsCall
def update_ipsec_site_connection(self, ipsecsite_conn, body=None):
"""Updates an IPsecSiteConnection."""
return self.put(
self.ipsec_site_connection_path % (ipsecsite_conn), body=body
)
- @APIParamsCall
def delete_ipsec_site_connection(self, ipsecsite_conn):
"""Deletes the specified IPsecSiteConnection."""
return self.delete(self.ipsec_site_connection_path % (ipsecsite_conn))
- @APIParamsCall
def list_ikepolicies(self, retrieve_all=True, **_params):
- """Fetches a list of all configured IKEPolicies for a tenant."""
+ """Fetches a list of all configured IKEPolicies for a project."""
return self.list('ikepolicies', self.ikepolicies_path, retrieve_all,
**_params)
- @APIParamsCall
def show_ikepolicy(self, ikepolicy, **_params):
"""Fetches information of a specific IKEPolicy."""
return self.get(self.ikepolicy_path % (ikepolicy), params=_params)
- @APIParamsCall
def create_ikepolicy(self, body=None):
"""Creates a new IKEPolicy."""
return self.post(self.ikepolicies_path, body=body)
- @APIParamsCall
def update_ikepolicy(self, ikepolicy, body=None):
"""Updates an IKEPolicy."""
return self.put(self.ikepolicy_path % (ikepolicy), body=body)
- @APIParamsCall
def delete_ikepolicy(self, ikepolicy):
"""Deletes the specified IKEPolicy."""
return self.delete(self.ikepolicy_path % (ikepolicy))
- @APIParamsCall
def list_ipsecpolicies(self, retrieve_all=True, **_params):
- """Fetches a list of all configured IPsecPolicies for a tenant."""
+ """Fetches a list of all configured IPsecPolicies for a project."""
return self.list('ipsecpolicies',
self.ipsecpolicies_path,
retrieve_all,
**_params)
- @APIParamsCall
def show_ipsecpolicy(self, ipsecpolicy, **_params):
"""Fetches information of a specific IPsecPolicy."""
return self.get(self.ipsecpolicy_path % (ipsecpolicy), params=_params)
- @APIParamsCall
def create_ipsecpolicy(self, body=None):
"""Creates a new IPsecPolicy."""
return self.post(self.ipsecpolicies_path, body=body)
- @APIParamsCall
def update_ipsecpolicy(self, ipsecpolicy, body=None):
"""Updates an IPsecPolicy."""
return self.put(self.ipsecpolicy_path % (ipsecpolicy), body=body)
- @APIParamsCall
def delete_ipsecpolicy(self, ipsecpolicy):
"""Deletes the specified IPsecPolicy."""
return self.delete(self.ipsecpolicy_path % (ipsecpolicy))
- @APIParamsCall
+ def list_loadbalancers(self, retrieve_all=True, **_params):
+ """Fetches a list of all loadbalancers for a project."""
+ return self.list('loadbalancers', self.lbaas_loadbalancers_path,
+ retrieve_all, **_params)
+
+ def show_loadbalancer(self, lbaas_loadbalancer, **_params):
+ """Fetches information for a load balancer."""
+ return self.get(self.lbaas_loadbalancer_path % (lbaas_loadbalancer),
+ params=_params)
+
+ def create_loadbalancer(self, body=None):
+ """Creates a new load balancer."""
+ return self.post(self.lbaas_loadbalancers_path, body=body)
+
+ def update_loadbalancer(self, lbaas_loadbalancer, body=None):
+ """Updates a load balancer."""
+ return self.put(self.lbaas_loadbalancer_path % (lbaas_loadbalancer),
+ body=body)
+
+ def delete_loadbalancer(self, lbaas_loadbalancer):
+ """Deletes the specified load balancer."""
+ return self.delete(self.lbaas_loadbalancer_path %
+ (lbaas_loadbalancer))
+
+ def retrieve_loadbalancer_stats(self, loadbalancer, **_params):
+ """Retrieves stats for a certain load balancer."""
+ return self.get(self.lbaas_loadbalancer_path_stats % (loadbalancer),
+ params=_params)
+
+ def retrieve_loadbalancer_status(self, loadbalancer, **_params):
+ """Retrieves status for a certain load balancer."""
+ return self.get(self.lbaas_loadbalancer_path_status % (loadbalancer),
+ params=_params)
+
+ def list_listeners(self, retrieve_all=True, **_params):
+ """Fetches a list of all lbaas_listeners for a project."""
+ return self.list('listeners', self.lbaas_listeners_path,
+ retrieve_all, **_params)
+
+ def show_listener(self, lbaas_listener, **_params):
+ """Fetches information for a lbaas_listener."""
+ return self.get(self.lbaas_listener_path % (lbaas_listener),
+ params=_params)
+
+ def create_listener(self, body=None):
+ """Creates a new lbaas_listener."""
+ return self.post(self.lbaas_listeners_path, body=body)
+
+ def update_listener(self, lbaas_listener, body=None):
+ """Updates a lbaas_listener."""
+ return self.put(self.lbaas_listener_path % (lbaas_listener),
+ body=body)
+
+ def delete_listener(self, lbaas_listener):
+ """Deletes the specified lbaas_listener."""
+ return self.delete(self.lbaas_listener_path % (lbaas_listener))
+
+ def list_lbaas_l7policies(self, retrieve_all=True, **_params):
+ """Fetches a list of all L7 policies for a listener."""
+ return self.list('l7policies', self.lbaas_l7policies_path,
+ retrieve_all, **_params)
+
+ def show_lbaas_l7policy(self, l7policy, **_params):
+ """Fetches information of a certain listener's L7 policy."""
+ return self.get(self.lbaas_l7policy_path % l7policy,
+ params=_params)
+
+ def create_lbaas_l7policy(self, body=None):
+ """Creates L7 policy for a certain listener."""
+ return self.post(self.lbaas_l7policies_path, body=body)
+
+ def update_lbaas_l7policy(self, l7policy, body=None):
+ """Updates L7 policy."""
+ return self.put(self.lbaas_l7policy_path % l7policy,
+ body=body)
+
+ def delete_lbaas_l7policy(self, l7policy):
+ """Deletes the specified L7 policy."""
+ return self.delete(self.lbaas_l7policy_path % l7policy)
+
+ def list_lbaas_l7rules(self, l7policy, retrieve_all=True, **_params):
+ """Fetches a list of all rules for L7 policy."""
+ return self.list('rules', self.lbaas_l7rules_path % l7policy,
+ retrieve_all, **_params)
+
+ def show_lbaas_l7rule(self, l7rule, l7policy, **_params):
+ """Fetches information of a certain L7 policy's rule."""
+ return self.get(self.lbaas_l7rule_path % (l7policy, l7rule),
+ params=_params)
+
+ def create_lbaas_l7rule(self, l7policy, body=None):
+ """Creates rule for a certain L7 policy."""
+ return self.post(self.lbaas_l7rules_path % l7policy, body=body)
+
+ def update_lbaas_l7rule(self, l7rule, l7policy, body=None):
+ """Updates L7 rule."""
+ return self.put(self.lbaas_l7rule_path % (l7policy, l7rule),
+ body=body)
+
+ def delete_lbaas_l7rule(self, l7rule, l7policy):
+ """Deletes the specified L7 rule."""
+ return self.delete(self.lbaas_l7rule_path % (l7policy, l7rule))
+
+ def list_lbaas_pools(self, retrieve_all=True, **_params):
+ """Fetches a list of all lbaas_pools for a project."""
+ return self.list('pools', self.lbaas_pools_path,
+ retrieve_all, **_params)
+
+ def show_lbaas_pool(self, lbaas_pool, **_params):
+ """Fetches information for a lbaas_pool."""
+ return self.get(self.lbaas_pool_path % (lbaas_pool),
+ params=_params)
+
+ def create_lbaas_pool(self, body=None):
+ """Creates a new lbaas_pool."""
+ return self.post(self.lbaas_pools_path, body=body)
+
+ def update_lbaas_pool(self, lbaas_pool, body=None):
+ """Updates a lbaas_pool."""
+ return self.put(self.lbaas_pool_path % (lbaas_pool),
+ body=body)
+
+ def delete_lbaas_pool(self, lbaas_pool):
+ """Deletes the specified lbaas_pool."""
+ return self.delete(self.lbaas_pool_path % (lbaas_pool))
+
+ def list_lbaas_healthmonitors(self, retrieve_all=True, **_params):
+ """Fetches a list of all lbaas_healthmonitors for a project."""
+ return self.list('healthmonitors', self.lbaas_healthmonitors_path,
+ retrieve_all, **_params)
+
+ def show_lbaas_healthmonitor(self, lbaas_healthmonitor, **_params):
+ """Fetches information for a lbaas_healthmonitor."""
+ return self.get(self.lbaas_healthmonitor_path % (lbaas_healthmonitor),
+ params=_params)
+
+ def create_lbaas_healthmonitor(self, body=None):
+ """Creates a new lbaas_healthmonitor."""
+ return self.post(self.lbaas_healthmonitors_path, body=body)
+
+ def update_lbaas_healthmonitor(self, lbaas_healthmonitor, body=None):
+ """Updates a lbaas_healthmonitor."""
+ return self.put(self.lbaas_healthmonitor_path % (lbaas_healthmonitor),
+ body=body)
+
+ def delete_lbaas_healthmonitor(self, lbaas_healthmonitor):
+ """Deletes the specified lbaas_healthmonitor."""
+ return self.delete(self.lbaas_healthmonitor_path %
+ (lbaas_healthmonitor))
+
+ def list_lbaas_loadbalancers(self, retrieve_all=True, **_params):
+ """Fetches a list of all lbaas_loadbalancers for a project."""
+ return self.list('loadbalancers', self.lbaas_loadbalancers_path,
+ retrieve_all, **_params)
+
+ def list_lbaas_members(self, lbaas_pool, retrieve_all=True, **_params):
+ """Fetches a list of all lbaas_members for a project."""
+ return self.list('members', self.lbaas_members_path % lbaas_pool,
+ retrieve_all, **_params)
+
+ def show_lbaas_member(self, lbaas_member, lbaas_pool, **_params):
+ """Fetches information of a certain lbaas_member."""
+ return self.get(self.lbaas_member_path % (lbaas_pool, lbaas_member),
+ params=_params)
+
+ def create_lbaas_member(self, lbaas_pool, body=None):
+ """Creates a lbaas_member."""
+ return self.post(self.lbaas_members_path % lbaas_pool, body=body)
+
+ def update_lbaas_member(self, lbaas_member, lbaas_pool, body=None):
+ """Updates a lbaas_member."""
+ return self.put(self.lbaas_member_path % (lbaas_pool, lbaas_member),
+ body=body)
+
+ def delete_lbaas_member(self, lbaas_member, lbaas_pool):
+ """Deletes the specified lbaas_member."""
+ return self.delete(self.lbaas_member_path % (lbaas_pool, lbaas_member))
+
def list_vips(self, retrieve_all=True, **_params):
- """Fetches a list of all load balancer vips for a tenant."""
+ """Fetches a list of all load balancer vips for a project."""
# Pass filters in "params" argument to do_request
return self.list('vips', self.vips_path, retrieve_all,
**_params)
- @APIParamsCall
def show_vip(self, vip, **_params):
"""Fetches information of a certain load balancer vip."""
return self.get(self.vip_path % (vip), params=_params)
- @APIParamsCall
def create_vip(self, body=None):
"""Creates a new load balancer vip."""
return self.post(self.vips_path, body=body)
- @APIParamsCall
def update_vip(self, vip, body=None):
"""Updates a load balancer vip."""
return self.put(self.vip_path % (vip), body=body)
- @APIParamsCall
def delete_vip(self, vip):
"""Deletes the specified load balancer vip."""
return self.delete(self.vip_path % (vip))
- @APIParamsCall
def list_pools(self, retrieve_all=True, **_params):
- """Fetches a list of all load balancer pools for a tenant."""
+ """Fetches a list of all load balancer pools for a project."""
# Pass filters in "params" argument to do_request
return self.list('pools', self.pools_path, retrieve_all,
**_params)
- @APIParamsCall
def show_pool(self, pool, **_params):
"""Fetches information of a certain load balancer pool."""
return self.get(self.pool_path % (pool), params=_params)
- @APIParamsCall
def create_pool(self, body=None):
"""Creates a new load balancer pool."""
return self.post(self.pools_path, body=body)
- @APIParamsCall
def update_pool(self, pool, body=None):
"""Updates a load balancer pool."""
return self.put(self.pool_path % (pool), body=body)
- @APIParamsCall
def delete_pool(self, pool):
"""Deletes the specified load balancer pool."""
return self.delete(self.pool_path % (pool))
- @APIParamsCall
def retrieve_pool_stats(self, pool, **_params):
"""Retrieves stats for a certain load balancer pool."""
return self.get(self.pool_path_stats % (pool), params=_params)
- @APIParamsCall
def list_members(self, retrieve_all=True, **_params):
- """Fetches a list of all load balancer members for a tenant."""
+ """Fetches a list of all load balancer members for a project."""
# Pass filters in "params" argument to do_request
return self.list('members', self.members_path, retrieve_all,
**_params)
- @APIParamsCall
def show_member(self, member, **_params):
"""Fetches information of a certain load balancer member."""
return self.get(self.member_path % (member), params=_params)
- @APIParamsCall
def create_member(self, body=None):
"""Creates a new load balancer member."""
return self.post(self.members_path, body=body)
- @APIParamsCall
def update_member(self, member, body=None):
"""Updates a load balancer member."""
return self.put(self.member_path % (member), body=body)
- @APIParamsCall
def delete_member(self, member):
"""Deletes the specified load balancer member."""
return self.delete(self.member_path % (member))
- @APIParamsCall
def list_health_monitors(self, retrieve_all=True, **_params):
- """Fetches a list of all load balancer health monitors for a tenant."""
+ """Fetches a list of all load balancer health monitors for a project.
+
+ """
# Pass filters in "params" argument to do_request
return self.list('health_monitors', self.health_monitors_path,
retrieve_all, **_params)
- @APIParamsCall
def show_health_monitor(self, health_monitor, **_params):
"""Fetches information of a certain load balancer health monitor."""
return self.get(self.health_monitor_path % (health_monitor),
params=_params)
- @APIParamsCall
def create_health_monitor(self, body=None):
"""Creates a new load balancer health monitor."""
return self.post(self.health_monitors_path, body=body)
- @APIParamsCall
def update_health_monitor(self, health_monitor, body=None):
"""Updates a load balancer health monitor."""
return self.put(self.health_monitor_path % (health_monitor), body=body)
- @APIParamsCall
def delete_health_monitor(self, health_monitor):
"""Deletes the specified load balancer health monitor."""
return self.delete(self.health_monitor_path % (health_monitor))
- @APIParamsCall
def associate_health_monitor(self, pool, body):
"""Associate specified load balancer health monitor and pool."""
return self.post(self.associate_pool_health_monitors_path % (pool),
body=body)
- @APIParamsCall
def disassociate_health_monitor(self, pool, health_monitor):
"""Disassociate specified load balancer health monitor and pool."""
path = (self.disassociate_pool_health_monitors_path %
{'pool': pool, 'health_monitor': health_monitor})
return self.delete(path)
- @APIParamsCall
def create_qos_queue(self, body=None):
"""Creates a new queue."""
return self.post(self.qos_queues_path, body=body)
- @APIParamsCall
def list_qos_queues(self, **_params):
- """Fetches a list of all queues for a tenant."""
+ """Fetches a list of all queues for a project."""
return self.get(self.qos_queues_path, params=_params)
- @APIParamsCall
def show_qos_queue(self, queue, **_params):
"""Fetches information of a certain queue."""
return self.get(self.qos_queue_path % (queue),
params=_params)
- @APIParamsCall
def delete_qos_queue(self, queue):
"""Deletes the specified queue."""
return self.delete(self.qos_queue_path % (queue))
- @APIParamsCall
def list_agents(self, **_params):
"""Fetches agents."""
# Pass filters in "params" argument to do_request
return self.get(self.agents_path, params=_params)
- @APIParamsCall
def show_agent(self, agent, **_params):
"""Fetches information of a certain agent."""
return self.get(self.agent_path % (agent), params=_params)
- @APIParamsCall
def update_agent(self, agent, body=None):
"""Updates an agent."""
return self.put(self.agent_path % (agent), body=body)
- @APIParamsCall
def delete_agent(self, agent):
"""Deletes the specified agent."""
return self.delete(self.agent_path % (agent))
- @APIParamsCall
def list_network_gateways(self, **_params):
"""Retrieve network gateways."""
return self.get(self.network_gateways_path, params=_params)
- @APIParamsCall
def show_network_gateway(self, gateway_id, **_params):
"""Fetch a network gateway."""
return self.get(self.network_gateway_path % gateway_id, params=_params)
- @APIParamsCall
def create_network_gateway(self, body=None):
"""Create a new network gateway."""
return self.post(self.network_gateways_path, body=body)
- @APIParamsCall
def update_network_gateway(self, gateway_id, body=None):
"""Update a network gateway."""
return self.put(self.network_gateway_path % gateway_id, body=body)
- @APIParamsCall
def delete_network_gateway(self, gateway_id):
"""Delete the specified network gateway."""
return self.delete(self.network_gateway_path % gateway_id)
- @APIParamsCall
def connect_network_gateway(self, gateway_id, body=None):
"""Connect a network gateway to the specified network."""
base_uri = self.network_gateway_path % gateway_id
return self.put("%s/connect_network" % base_uri, body=body)
- @APIParamsCall
def disconnect_network_gateway(self, gateway_id, body=None):
"""Disconnect a network from the specified gateway."""
base_uri = self.network_gateway_path % gateway_id
return self.put("%s/disconnect_network" % base_uri, body=body)
- @APIParamsCall
def list_gateway_devices(self, **_params):
"""Retrieve gateway devices."""
return self.get(self.gateway_devices_path, params=_params)
- @APIParamsCall
def show_gateway_device(self, gateway_device_id, **_params):
"""Fetch a gateway device."""
return self.get(self.gateway_device_path % gateway_device_id,
params=_params)
- @APIParamsCall
def create_gateway_device(self, body=None):
"""Create a new gateway device."""
return self.post(self.gateway_devices_path, body=body)
- @APIParamsCall
def update_gateway_device(self, gateway_device_id, body=None):
"""Updates a new gateway device."""
return self.put(self.gateway_device_path % gateway_device_id,
body=body)
- @APIParamsCall
def delete_gateway_device(self, gateway_device_id):
"""Delete the specified gateway device."""
return self.delete(self.gateway_device_path % gateway_device_id)
- @APIParamsCall
def list_dhcp_agent_hosting_networks(self, network, **_params):
"""Fetches a list of dhcp agents hosting a network."""
return self.get((self.network_path + self.DHCP_AGENTS) % network,
params=_params)
- @APIParamsCall
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)
- @APIParamsCall
def add_network_to_dhcp_agent(self, dhcp_agent, body=None):
"""Adds a network to dhcp agent."""
return self.post((self.agent_path + self.DHCP_NETS) % dhcp_agent,
body=body)
- @APIParamsCall
def remove_network_from_dhcp_agent(self, dhcp_agent, network_id):
"""Remove a network from dhcp agent."""
return self.delete((self.agent_path + self.DHCP_NETS + "/%s") % (
dhcp_agent, network_id))
- @APIParamsCall
def list_l3_agent_hosting_routers(self, router, **_params):
"""Fetches a list of L3 agents hosting a router."""
return self.get((self.router_path + self.L3_AGENTS) % router,
params=_params)
- @APIParamsCall
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)
- @APIParamsCall
def add_router_to_l3_agent(self, l3_agent, body):
"""Adds a router to L3 agent."""
return self.post((self.agent_path + self.L3_ROUTERS) % l3_agent,
body=body)
- @APIParamsCall
+ def list_dragents_hosting_bgp_speaker(self, bgp_speaker, **_params):
+ """Fetches a list of Dynamic Routing agents hosting a BGP speaker."""
+ return self.get((self.bgp_speaker_path + self.BGP_DRAGENTS)
+ % bgp_speaker, params=_params)
+
+ def add_bgp_speaker_to_dragent(self, bgp_dragent, body):
+ """Adds a BGP speaker to Dynamic Routing agent."""
+ return self.post((self.agent_path + self.BGP_DRINSTANCES)
+ % bgp_dragent, body=body)
+
+ def remove_bgp_speaker_from_dragent(self, bgp_dragent, bgpspeaker_id):
+ """Removes a BGP speaker from Dynamic Routing agent."""
+ return self.delete((self.agent_path + self.BGP_DRINSTANCES + "/%s")
+ % (bgp_dragent, bgpspeaker_id))
+
+ def list_bgp_speaker_on_dragent(self, bgp_dragent, **_params):
+ """Fetches a list of BGP speakers hosted by Dynamic Routing agent."""
+ return self.get((self.agent_path + self.BGP_DRINSTANCES)
+ % bgp_dragent, params=_params)
+
def list_firewall_rules(self, retrieve_all=True, **_params):
- """Fetches a list of all firewall rules for a tenant."""
+ """Fetches a list of all firewall rules for a project."""
# Pass filters in "params" argument to do_request
return self.list('firewall_rules', self.firewall_rules_path,
retrieve_all, **_params)
- @APIParamsCall
def show_firewall_rule(self, firewall_rule, **_params):
"""Fetches information of a certain firewall rule."""
return self.get(self.firewall_rule_path % (firewall_rule),
params=_params)
- @APIParamsCall
def create_firewall_rule(self, body=None):
"""Creates a new firewall rule."""
return self.post(self.firewall_rules_path, body=body)
- @APIParamsCall
def update_firewall_rule(self, firewall_rule, body=None):
"""Updates a firewall rule."""
return self.put(self.firewall_rule_path % (firewall_rule), body=body)
- @APIParamsCall
def delete_firewall_rule(self, firewall_rule):
"""Deletes the specified firewall rule."""
return self.delete(self.firewall_rule_path % (firewall_rule))
- @APIParamsCall
def list_firewall_policies(self, retrieve_all=True, **_params):
- """Fetches a list of all firewall policies for a tenant."""
+ """Fetches a list of all firewall policies for a project."""
# Pass filters in "params" argument to do_request
return self.list('firewall_policies', self.firewall_policies_path,
retrieve_all, **_params)
- @APIParamsCall
def show_firewall_policy(self, firewall_policy, **_params):
"""Fetches information of a certain firewall policy."""
return self.get(self.firewall_policy_path % (firewall_policy),
params=_params)
- @APIParamsCall
def create_firewall_policy(self, body=None):
"""Creates a new firewall policy."""
return self.post(self.firewall_policies_path, body=body)
- @APIParamsCall
def update_firewall_policy(self, firewall_policy, body=None):
"""Updates a firewall policy."""
return self.put(self.firewall_policy_path % (firewall_policy),
body=body)
- @APIParamsCall
def delete_firewall_policy(self, firewall_policy):
"""Deletes the specified firewall policy."""
return self.delete(self.firewall_policy_path % (firewall_policy))
- @APIParamsCall
def firewall_policy_insert_rule(self, firewall_policy, body=None):
"""Inserts specified rule into firewall policy."""
return self.put(self.firewall_policy_insert_path % (firewall_policy),
body=body)
- @APIParamsCall
def firewall_policy_remove_rule(self, firewall_policy, body=None):
"""Removes specified rule from firewall policy."""
return self.put(self.firewall_policy_remove_path % (firewall_policy),
body=body)
- @APIParamsCall
def list_firewalls(self, retrieve_all=True, **_params):
- """Fetches a list of all firewals for a tenant."""
+ """Fetches a list of all firewalls for a project."""
# Pass filters in "params" argument to do_request
return self.list('firewalls', self.firewalls_path, retrieve_all,
**_params)
- @APIParamsCall
def show_firewall(self, firewall, **_params):
"""Fetches information of a certain firewall."""
return self.get(self.firewall_path % (firewall), params=_params)
- @APIParamsCall
def create_firewall(self, body=None):
"""Creates a new firewall."""
return self.post(self.firewalls_path, body=body)
- @APIParamsCall
def update_firewall(self, firewall, body=None):
"""Updates a firewall."""
return self.put(self.firewall_path % (firewall), body=body)
- @APIParamsCall
def delete_firewall(self, firewall):
"""Deletes the specified firewall."""
return self.delete(self.firewall_path % (firewall))
- @APIParamsCall
+ 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") % (
l3_agent, router_id))
- @APIParamsCall
def get_lbaas_agent_hosting_pool(self, pool, **_params):
"""Fetches a loadbalancer agent hosting a pool."""
return self.get((self.pool_path + self.LOADBALANCER_AGENT) % pool,
params=_params)
- @APIParamsCall
def list_pools_on_lbaas_agent(self, lbaas_agent, **_params):
"""Fetches a list of pools hosted by the loadbalancer agent."""
return self.get((self.agent_path + self.LOADBALANCER_POOLS) %
lbaas_agent, params=_params)
- @APIParamsCall
+ def get_lbaas_agent_hosting_loadbalancer(self, loadbalancer, **_params):
+ """Fetches a loadbalancer agent hosting a loadbalancer."""
+ return self.get((self.lbaas_loadbalancer_path +
+ self.LOADBALANCER_HOSTING_AGENT) % loadbalancer,
+ params=_params)
+
+ def list_loadbalancers_on_lbaas_agent(self, lbaas_agent, **_params):
+ """Fetches a list of loadbalancers hosted by the loadbalancer agent."""
+ return self.get((self.agent_path + self.AGENT_LOADBALANCERS) %
+ lbaas_agent, params=_params)
+
def list_service_providers(self, retrieve_all=True, **_params):
"""Fetches service providers."""
# Pass filters in "params" argument to do_request
return self.list('service_providers', self.service_providers_path,
retrieve_all, **_params)
- def list_credentials(self, **_params):
- """Fetch a list of all credentials for a tenant."""
- return self.get(self.credentials_path, params=_params)
-
- @APIParamsCall
- def show_credential(self, credential, **_params):
- """Fetch a credential."""
- return self.get(self.credential_path % (credential), params=_params)
-
- @APIParamsCall
- def create_credential(self, body=None):
- """Create a new credential."""
- return self.post(self.credentials_path, body=body)
-
- @APIParamsCall
- def update_credential(self, credential, body=None):
- """Update a credential."""
- return self.put(self.credential_path % (credential), body=body)
-
- @APIParamsCall
- def delete_credential(self, credential):
- """Delete the specified credential."""
- return self.delete(self.credential_path % (credential))
-
- def list_network_profile_bindings(self, **params):
- """Fetch a list of all tenants associated for a network profile."""
- return self.get(self.network_profile_bindings_path, params=params)
-
- @APIParamsCall
- def list_network_profiles(self, **params):
- """Fetch a list of all network profiles for a tenant."""
- return self.get(self.network_profiles_path, params=params)
-
- @APIParamsCall
- def show_network_profile(self, profile, **params):
- """Fetch a network profile."""
- return self.get(self.network_profile_path % (profile), params=params)
-
- @APIParamsCall
- def create_network_profile(self, body=None):
- """Create a network profile."""
- return self.post(self.network_profiles_path, body=body)
-
- @APIParamsCall
- def update_network_profile(self, profile, body=None):
- """Update a network profile."""
- return self.put(self.network_profile_path % (profile), body=body)
-
- @APIParamsCall
- def delete_network_profile(self, profile):
- """Delete the network profile."""
- return self.delete(self.network_profile_path % profile)
-
- @APIParamsCall
- def list_policy_profile_bindings(self, **params):
- """Fetch a list of all tenants associated for a policy profile."""
- return self.get(self.policy_profile_bindings_path, params=params)
-
- @APIParamsCall
- def list_policy_profiles(self, **params):
- """Fetch a list of all network profiles for a tenant."""
- return self.get(self.policy_profiles_path, params=params)
-
- @APIParamsCall
- def show_policy_profile(self, profile, **params):
- """Fetch a network profile."""
- return self.get(self.policy_profile_path % (profile), params=params)
-
- @APIParamsCall
- def update_policy_profile(self, profile, body=None):
- """Update a policy profile."""
- return self.put(self.policy_profile_path % (profile), body=body)
-
- @APIParamsCall
def create_metering_label(self, body=None):
"""Creates a metering label."""
return self.post(self.metering_labels_path, body=body)
- @APIParamsCall
def delete_metering_label(self, label):
"""Deletes the specified metering label."""
return self.delete(self.metering_label_path % (label))
- @APIParamsCall
def list_metering_labels(self, retrieve_all=True, **_params):
- """Fetches a list of all metering labels for a tenant."""
+ """Fetches a list of all metering labels for a project."""
return self.list('metering_labels', self.metering_labels_path,
retrieve_all, **_params)
- @APIParamsCall
def show_metering_label(self, metering_label, **_params):
"""Fetches information of a certain metering label."""
return self.get(self.metering_label_path %
(metering_label), params=_params)
- @APIParamsCall
def create_metering_label_rule(self, body=None):
"""Creates a metering label rule."""
return self.post(self.metering_label_rules_path, body=body)
- @APIParamsCall
def delete_metering_label_rule(self, rule):
"""Deletes the specified metering label rule."""
return self.delete(self.metering_label_rule_path % (rule))
- @APIParamsCall
def list_metering_label_rules(self, retrieve_all=True, **_params):
"""Fetches a list of all metering label rules for a label."""
return self.list('metering_label_rules',
self.metering_label_rules_path, retrieve_all,
**_params)
- @APIParamsCall
def show_metering_label_rule(self, metering_label_rule, **_params):
"""Fetches information of a certain metering label rule."""
return self.get(self.metering_label_rule_path %
(metering_label_rule), params=_params)
- @APIParamsCall
- def list_net_partitions(self, **params):
- """Fetch a list of all network partitions for a tenant."""
- return self.get(self.net_partitions_path, params=params)
+ def create_rbac_policy(self, body=None):
+ """Create a new RBAC policy."""
+ return self.post(self.rbac_policies_path, body=body)
- @APIParamsCall
- def show_net_partition(self, netpartition, **params):
- """Fetch a network partition."""
- return self.get(self.net_partition_path % (netpartition),
- params=params)
+ def update_rbac_policy(self, rbac_policy_id, body=None):
+ """Update a RBAC policy."""
+ return self.put(self.rbac_policy_path % rbac_policy_id, body=body)
- @APIParamsCall
- def create_net_partition(self, body=None):
- """Create a network partition."""
- return self.post(self.net_partitions_path, body=body)
+ def list_rbac_policies(self, retrieve_all=True, **_params):
+ """Fetch a list of all RBAC policies for a project."""
+ return self.list('rbac_policies', self.rbac_policies_path,
+ retrieve_all, **_params)
- @APIParamsCall
- def delete_net_partition(self, netpartition):
- """Delete the network partition."""
- return self.delete(self.net_partition_path % netpartition)
+ def show_rbac_policy(self, rbac_policy_id, **_params):
+ """Fetch information of a certain RBAC policy."""
+ return self.get(self.rbac_policy_path % rbac_policy_id,
+ params=_params)
- def __init__(self, **kwargs):
- """Initialize a new client for the Neutron v2.0 API."""
- super(Client, self).__init__()
- self.httpclient = client.HTTPClient(**kwargs)
- self.version = '2.0'
- self.format = 'json'
- self.action_prefix = "/v%s" % (self.version)
- self.retries = 0
- self.retry_interval = 1
+ def delete_rbac_policy(self, rbac_policy_id):
+ """Delete the specified RBAC policy."""
+ return self.delete(self.rbac_policy_path % rbac_policy_id)
- def _handle_fault_response(self, status_code, response_body):
- # Create exception with HTTP status code and message
- _logger.debug(_("Error message: %s"), response_body)
- # Add deserialized error message to exception arguments
- try:
- des_error_body = self.deserialize(response_body, status_code)
- except Exception:
- # If unable to deserialized body it is probably not a
- # Neutron error
- des_error_body = {'message': response_body}
- # Raise the appropriate exception
- exception_handler_v20(status_code, des_error_body)
+ def list_qos_policies(self, retrieve_all=True, **_params):
+ """Fetches a list of all qos policies for a project."""
+ # Pass filters in "params" argument to do_request
+ return self.list('policies', self.qos_policies_path,
+ retrieve_all, **_params)
- def _check_uri_length(self, action):
- uri_len = len(self.httpclient.endpoint_url) + len(action)
- if uri_len > self.MAX_URI_LEN:
- raise exceptions.RequestURITooLong(
- excess=uri_len - self.MAX_URI_LEN)
+ def show_qos_policy(self, qos_policy, **_params):
+ """Fetches information of a certain qos policy."""
+ return self.get(self.qos_policy_path % qos_policy,
+ params=_params)
- def do_request(self, method, action, body=None, headers=None, params=None):
- # Add format and tenant_id
- action += ".%s" % self.format
- action = self.action_prefix + action
- if type(params) is dict and params:
- params = utils.safe_encode_dict(params)
- action += '?' + urllib.urlencode(params, doseq=1)
- # Ensure client always has correct uri - do not guesstimate anything
- self.httpclient.authenticate_and_fetch_endpoint_url()
- self._check_uri_length(action)
+ def create_qos_policy(self, body=None):
+ """Creates a new qos policy."""
+ return self.post(self.qos_policies_path, body=body)
- if body:
- body = self.serialize(body)
- self.httpclient.content_type = self.content_type()
- resp, replybody = self.httpclient.do_request(action, method, body=body)
- status_code = self.get_status_code(resp)
- if status_code in (httplib.OK,
- httplib.CREATED,
- httplib.ACCEPTED,
- httplib.NO_CONTENT):
- return self.deserialize(replybody, status_code)
- else:
- self._handle_fault_response(status_code, replybody)
+ def update_qos_policy(self, qos_policy, body=None, revision_number=None):
+ """Updates a qos policy."""
+ return self._update_resource(self.qos_policy_path % qos_policy,
+ body=body,
+ revision_number=revision_number)
- def get_auth_info(self):
- return self.httpclient.get_auth_info()
+ def delete_qos_policy(self, qos_policy):
+ """Deletes the specified qos policy."""
+ return self.delete(self.qos_policy_path % qos_policy)
+
+ def list_qos_rule_types(self, retrieve_all=True, **_params):
+ """List available qos rule types."""
+ return self.list('rule_types', self.qos_rule_types_path,
+ retrieve_all, **_params)
+
+ def list_bandwidth_limit_rules(self, policy_id,
+ retrieve_all=True, **_params):
+ """Fetches a list of all bandwidth limit rules for the given policy."""
+ return self.list('bandwidth_limit_rules',
+ self.qos_bandwidth_limit_rules_path % policy_id,
+ retrieve_all, **_params)
- def get_status_code(self, response):
- """Returns the integer status code from the response.
+ 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), params=_params)
+
+ def create_bandwidth_limit_rule(self, policy, body=None):
+ """Creates a new bandwidth limit rule."""
+ return self.post(self.qos_bandwidth_limit_rules_path % policy,
+ body=body)
+
+ def update_bandwidth_limit_rule(self, rule, policy, body=None):
+ """Updates a bandwidth limit rule."""
+ return self.put(self.qos_bandwidth_limit_rule_path %
+ (policy, rule), body=body)
+
+ def delete_bandwidth_limit_rule(self, rule, policy):
+ """Deletes a bandwidth limit rule."""
+ return self.delete(self.qos_bandwidth_limit_rule_path %
+ (policy, rule))
+
+ def list_dscp_marking_rules(self, policy_id,
+ retrieve_all=True, **_params):
+ """Fetches a list of all DSCP marking rules for the given policy."""
+ return self.list('dscp_marking_rules',
+ self.qos_dscp_marking_rules_path % policy_id,
+ retrieve_all, **_params)
+
+ 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), params=_params)
+
+ def create_dscp_marking_rule(self, policy, body=None):
+ """Creates a new DSCP marking rule."""
+ return self.post(self.qos_dscp_marking_rules_path % policy,
+ body=body)
+
+ def update_dscp_marking_rule(self, rule, policy, body=None):
+ """Updates a DSCP marking rule."""
+ return self.put(self.qos_dscp_marking_rule_path %
+ (policy, rule), body=body)
+
+ def delete_dscp_marking_rule(self, rule, policy):
+ """Deletes a DSCP marking rule."""
+ 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.
- Either a Webob.Response (used in testing) or httplib.Response
- is returned.
"""
- if hasattr(response, 'status_int'):
- return response.status_int
- else:
- return response.status
+ 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 serialize(self, data):
- """Serializes a dictionary into either xml or json.
+ 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
- A dictionary with a single key can be passed and
- it can contain any structure.
"""
- if data is None:
- return None
- elif type(data) is dict:
- return serializer.Serializer(
- self.get_attr_metadata()).serialize(data, self.content_type())
- else:
- raise Exception(_("Unable to serialize object of type = '%s'") %
- type(data))
+ 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 deserialize(self, data, status_code):
- """Deserializes an xml or json string into a dictionary."""
- if status_code == 204:
- return data
- return serializer.Serializer(self.get_attr_metadata()).deserialize(
- data, self.content_type())['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 content_type(self, _format=None):
- """Returns the mime-type for either 'xml' or 'json'.
+ 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))
- Defaults to the currently set format.
- """
- _format = _format or self.format
- return "application/%s" % (_format)
+ 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 retry_request(self, method, action, body=None,
- headers=None, params=None):
- """Call do_request with the default retry configuration.
+ 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
- Only idempotent requests should retry failed connection attempts.
- :raises: ConnectionFailed if the maximum # of retries is exceeded
"""
- max_attempts = self.retries + 1
- for i in range(max_attempts):
- try:
- return self.do_request(method, action, body=body,
- headers=headers, params=params)
- except exceptions.ConnectionFailed:
- # Exception has already been logged by do_request()
- if i < self.retries:
- _logger.debug(_('Retrying connection to Neutron service'))
- time.sleep(self.retry_interval)
+ 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)
- raise exceptions.ConnectionFailed(reason=_("Maximum attempts reached"))
+ 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(self, action, body=None, headers=None, params=None):
- return self.retry_request("DELETE", action, body=body,
- headers=headers, params=params)
+ 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 get(self, action, body=None, headers=None, params=None):
- return self.retry_request("GET", action, body=body,
- headers=headers, params=params)
+ def create_flavor(self, body=None):
+ """Creates a new Neutron service flavor."""
+ return self.post(self.flavors_path, body=body)
- def post(self, action, body=None, headers=None, params=None):
- # Do not retry POST requests to avoid the orphan objects problem.
- return self.do_request("POST", action, body=body,
- headers=headers, params=params)
+ def delete_flavor(self, flavor):
+ """Deletes the specified Neutron service flavor."""
+ return self.delete(self.flavor_path % (flavor))
- def put(self, action, body=None, headers=None, params=None):
- return self.retry_request("PUT", action, body=body,
- headers=headers, params=params)
+ def list_flavors(self, retrieve_all=True, **_params):
+ """Fetches a list of all Neutron service flavors for a project."""
+ return self.list('flavors', self.flavors_path, retrieve_all,
+ **_params)
- def list(self, collection, path, retrieve_all=True, **params):
- if retrieve_all:
- res = []
- for r in self._pagination(collection, path, **params):
- res.extend(r[collection])
- return {collection: res}
- else:
- return self._pagination(collection, path, **params)
+ def show_flavor(self, flavor, **_params):
+ """Fetches information for a certain Neutron service flavor."""
+ return self.get(self.flavor_path % (flavor), params=_params)
- def _pagination(self, collection, path, **params):
- if params.get('page_reverse', False):
- linkrel = 'previous'
- else:
- linkrel = 'next'
- next = True
- while next:
- res = self.get(path, params=params)
- yield res
- next = False
- try:
- for link in res['%s_links' % collection]:
- if link['rel'] == linkrel:
- query_str = urlparse.urlparse(link['href']).query
- params = urlparse.parse_qs(query_str)
- next = True
- break
- except KeyError:
- break
+ def update_flavor(self, flavor, body):
+ """Update a Neutron service flavor."""
+ return self.put(self.flavor_path % (flavor), body=body)
+
+ def associate_flavor(self, flavor, body):
+ """Associate a Neutron service flavor with a profile."""
+ return self.post(self.flavor_profile_bindings_path %
+ (flavor), body=body)
+
+ def disassociate_flavor(self, flavor, flavor_profile):
+ """Disassociate a Neutron service flavor with a profile."""
+ return self.delete(self.flavor_profile_binding_path %
+ (flavor, flavor_profile))
+
+ def create_service_profile(self, body=None):
+ """Creates a new Neutron service flavor profile."""
+ return self.post(self.service_profiles_path, body=body)
+
+ def delete_service_profile(self, flavor_profile):
+ """Deletes the specified Neutron service flavor profile."""
+ return self.delete(self.service_profile_path % (flavor_profile))
+
+ def list_service_profiles(self, retrieve_all=True, **_params):
+ """Fetches a list of all Neutron service flavor profiles."""
+ return self.list('service_profiles', self.service_profiles_path,
+ retrieve_all, **_params)
+
+ def show_service_profile(self, flavor_profile, **_params):
+ """Fetches information for a certain Neutron service flavor profile."""
+ return self.get(self.service_profile_path % (flavor_profile),
+ params=_params)
+
+ def update_service_profile(self, service_profile, body):
+ """Update a Neutron service profile."""
+ return self.put(self.service_profile_path % (service_profile),
+ body=body)
+
+ def list_availability_zones(self, retrieve_all=True, **_params):
+ """Fetches a list of all availability zones."""
+ return self.list('availability_zones', self.availability_zones_path,
+ retrieve_all, **_params)
+
+ @debtcollector.renames.renamed_kwarg(
+ 'tenant_id', 'project_id', replace=True)
+ def get_auto_allocated_topology(self, project_id, **_params):
+ """Fetch information about a project's auto-allocated topology."""
+ return self.get(
+ 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):
+ """Validate requirements for getting an auto-allocated topology."""
+ return self.get_auto_allocated_topology(project_id, fields=['dry-run'])
+
+ def list_bgp_speakers(self, retrieve_all=True, **_params):
+ """Fetches a list of all BGP speakers for a project."""
+ return self.list('bgp_speakers', self.bgp_speakers_path, retrieve_all,
+ **_params)
+
+ def show_bgp_speaker(self, bgp_speaker_id, **_params):
+ """Fetches information of a certain BGP speaker."""
+ return self.get(self.bgp_speaker_path % (bgp_speaker_id),
+ params=_params)
+
+ def create_bgp_speaker(self, body=None):
+ """Creates a new BGP speaker."""
+ return self.post(self.bgp_speakers_path, body=body)
+
+ def update_bgp_speaker(self, bgp_speaker_id, body=None):
+ """Update a BGP speaker."""
+ return self.put(self.bgp_speaker_path % bgp_speaker_id, body=body)
+
+ def delete_bgp_speaker(self, speaker_id):
+ """Deletes the specified BGP speaker."""
+ return self.delete(self.bgp_speaker_path % (speaker_id))
+
+ def add_peer_to_bgp_speaker(self, speaker_id, body=None):
+ """Adds a peer to BGP speaker."""
+ return self.put((self.bgp_speaker_path % speaker_id) +
+ "/add_bgp_peer", body=body)
+
+ def remove_peer_from_bgp_speaker(self, speaker_id, body=None):
+ """Removes a peer from BGP speaker."""
+ return self.put((self.bgp_speaker_path % speaker_id) +
+ "/remove_bgp_peer", body=body)
+
+ def add_network_to_bgp_speaker(self, speaker_id, body=None):
+ """Adds a network to BGP speaker."""
+ return self.put((self.bgp_speaker_path % speaker_id) +
+ "/add_gateway_network", body=body)
+
+ def remove_network_from_bgp_speaker(self, speaker_id, body=None):
+ """Removes a network from BGP speaker."""
+ return self.put((self.bgp_speaker_path % speaker_id) +
+ "/remove_gateway_network", body=body)
+
+ def list_route_advertised_from_bgp_speaker(self, speaker_id, **_params):
+ """Fetches a list of all routes advertised by BGP speaker."""
+ return self.get((self.bgp_speaker_path % speaker_id) +
+ "/get_advertised_routes", params=_params)
+
+ def list_bgp_peers(self, **_params):
+ """Fetches a list of all BGP peers."""
+ return self.get(self.bgp_peers_path, params=_params)
+
+ def show_bgp_peer(self, peer_id, **_params):
+ """Fetches information of a certain BGP peer."""
+ return self.get(self.bgp_peer_path % peer_id,
+ params=_params)
+
+ def create_bgp_peer(self, body=None):
+ """Create a new BGP peer."""
+ return self.post(self.bgp_peers_path, body=body)
+
+ def update_bgp_peer(self, bgp_peer_id, body=None):
+ """Update a BGP peer."""
+ return self.put(self.bgp_peer_path % bgp_peer_id, body=body)
+
+ def delete_bgp_peer(self, peer_id):
+ """Deletes the specified BGP peer."""
+ return self.delete(self.bgp_peer_path % peer_id)
+
+ def list_network_ip_availabilities(self, retrieve_all=True, **_params):
+ """Fetches IP availability information for all networks"""
+ return self.list('network_ip_availabilities',
+ self.network_ip_availabilities_path,
+ retrieve_all, **_params)
+
+ def show_network_ip_availability(self, network, **_params):
+ """Fetches IP availability information for a specified network"""
+ return self.get(self.network_ip_availability_path % (network),
+ params=_params)
+
+ def add_tag(self, resource_type, resource_id, tag, **_params):
+ """Add a tag on the resource."""
+ return self.put(self.tag_path % (resource_type, resource_id, tag))
+
+ def replace_tag(self, resource_type, resource_id, body, **_params):
+ """Replace tags on the resource."""
+ return self.put(self.tags_path % (resource_type, resource_id), body)
+
+ def remove_tag(self, resource_type, resource_id, tag, **_params):
+ """Remove a tag on the resource."""
+ return self.delete(self.tag_path % (resource_type, resource_id, tag))
+
+ def remove_tag_all(self, resource_type, resource_id, **_params):
+ """Remove all tags on the resource."""
+ return self.delete(self.tags_path % (resource_type, resource_id))
+
+ 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, revision_number=None):
+ """Update a trunk port."""
+ return self._update_resource(self.trunk_path % trunk, body=body,
+ revision_number=revision_number)
+
+ def delete_trunk(self, trunk):
+ """Delete a trunk port."""
+ return self.delete(self.trunk_path % (trunk))
+
+ def list_trunks(self, retrieve_all=True, **_params):
+ """Fetch a list of all trunk ports."""
+ return self.list('trunks', self.trunks_path, retrieve_all,
+ **_params)
+
+ def show_trunk(self, trunk, **_params):
+ """Fetch information for a certain trunk port."""
+ return self.get(self.trunk_path % (trunk), params=_params)
+
+ def trunk_add_subports(self, trunk, body=None):
+ """Add specified subports to the trunk."""
+ return self.put(self.subports_add_path % (trunk), body=body)
+
+ def trunk_remove_subports(self, trunk, body=None):
+ """Removes specified subports from the trunk."""
+ return self.put(self.subports_remove_path % (trunk), body=body)
+
+ 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)
+
+ def _parent_fx(obj, parent_id, **_params):
+ return self.show_ext(path % parent_id, obj, **_params)
+ fn = _fx if not parent_resource else _parent_fx
+ setattr(self, "show_%s" % resource_singular, fn)
+
+ def extend_list(self, resource_plural, path, parent_resource):
+ def _fx(retrieve_all=True, **_params):
+ return self.list_ext(resource_plural, path,
+ retrieve_all, **_params)
+
+ def _parent_fx(parent_id, retrieve_all=True, **_params):
+ return self.list_ext(resource_plural, path % parent_id,
+ retrieve_all, **_params)
+ fn = _fx if not parent_resource else _parent_fx
+ setattr(self, "list_%s" % resource_plural, fn)
+
+ def extend_create(self, resource_singular, path, parent_resource):
+ def _fx(body=None):
+ return self.create_ext(path, body)
+
+ def _parent_fx(parent_id, body=None):
+ return self.create_ext(path % parent_id, body)
+ fn = _fx if not parent_resource else _parent_fx
+ setattr(self, "create_%s" % resource_singular, fn)
+
+ def extend_delete(self, resource_singular, path, parent_resource):
+ def _fx(obj):
+ return self.delete_ext(path, obj)
+
+ def _parent_fx(obj, parent_id):
+ return self.delete_ext(path % parent_id, obj)
+ fn = _fx if not parent_resource else _parent_fx
+ setattr(self, "delete_%s" % resource_singular, fn)
+
+ def extend_update(self, resource_singular, path, parent_resource):
+ def _fx(obj, body=None):
+ return self.update_ext(path, obj, body)
+
+ def _parent_fx(obj, parent_id, body=None):
+ return self.update_ext(path % parent_id, obj, body)
+ fn = _fx if not parent_resource else _parent_fx
+ setattr(self, "update_%s" % resource_singular, fn)
+
+ def _extend_client_with_module(self, module, version):
+ classes = inspect.getmembers(module, inspect.isclass)
+ for cls_name, cls in classes:
+ if hasattr(cls, 'versions'):
+ if version not in cls.versions:
+ continue
+ parent_resource = getattr(cls, 'parent_resource', None)
+ if issubclass(cls, client_extension.ClientExtensionList):
+ self.extend_list(cls.resource_plural, cls.object_path,
+ parent_resource)
+ elif issubclass(cls, client_extension.ClientExtensionCreate):
+ self.extend_create(cls.resource, cls.object_path,
+ parent_resource)
+ elif issubclass(cls, client_extension.ClientExtensionUpdate):
+ self.extend_update(cls.resource, cls.resource_path,
+ parent_resource)
+ elif issubclass(cls, client_extension.ClientExtensionDelete):
+ self.extend_delete(cls.resource, cls.resource_path,
+ parent_resource)
+ elif issubclass(cls, client_extension.ClientExtensionShow):
+ self.extend_show(cls.resource, cls.resource_path,
+ parent_resource)
+ elif issubclass(cls, client_extension.NeutronClientExtension):
+ setattr(self, "%s_path" % cls.resource_plural,
+ cls.object_path)
+ setattr(self, "%s_path" % cls.resource, cls.resource_path)
+ self.EXTED_PLURALS.update({cls.resource_plural: cls.resource})
+
+ def _register_extensions(self, version):
+ for name, module in itertools.chain(
+ client_extension._discover_via_entry_points()):
+ self._extend_client_with_module(module, version)
diff --git a/neutronclient/version.py b/neutronclient/version.py
index 9315671c3..d597b5c91 100644
--- a/neutronclient/version.py
+++ b/neutronclient/version.py
@@ -13,10 +13,9 @@
# License for the specific language governing permissions and limitations
# under the License.
#
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-# @author: Carl Baldwin, Hewlett-Packard
import pbr.version
-__version__ = pbr.version.VersionInfo('python-neutronclient').version_string()
+version_info = pbr.version.VersionInfo('python-neutronclient')
+__version__ = version_info.version_string()
diff --git a/openstack-common.conf b/openstack-common.conf
deleted file mode 100644
index 902a82e40..000000000
--- a/openstack-common.conf
+++ /dev/null
@@ -1,7 +0,0 @@
-[DEFAULT]
-
-# The list of modules to copy from openstack-common
-modules=gettextutils,jsonutils,strutils,timeutils
-
-# The base module to hold the copy of openstack.common
-base=neutronclient
diff --git a/releasenotes/notes/.placeholder b/releasenotes/notes/.placeholder
new file mode 100644
index 000000000..e69de29bb
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-get-me-a-network-5ab2d60bf6f257b1.yaml b/releasenotes/notes/add-get-me-a-network-5ab2d60bf6f257b1.yaml
new file mode 100644
index 000000000..51dd37f41
--- /dev/null
+++ b/releasenotes/notes/add-get-me-a-network-5ab2d60bf6f257b1.yaml
@@ -0,0 +1,9 @@
+---
+features:
+ - |
+ CLI support for the "get-me-a-network" feature, which simplifies
+ the process for launching an instance with basic network
+ connectivity.
+
+ * The ``auto-allocated-topology-show`` command provides the
+ network of the automatically allocated topology for a tenant.
diff --git a/releasenotes/notes/add-l7-content-policies-capability-0f17cd06f044c83c.yaml b/releasenotes/notes/add-l7-content-policies-capability-0f17cd06f044c83c.yaml
new file mode 100644
index 000000000..e413df7ec
--- /dev/null
+++ b/releasenotes/notes/add-l7-content-policies-capability-0f17cd06f044c83c.yaml
@@ -0,0 +1,8 @@
+---
+features:
+ - |
+ CLI support for Layer 7 content policies and rules.
+
+ * L7 policies can be defined for listeners along
+ with the ability to set L7 policy order.
+ * Multiple rules can be created for an L7 policy.
diff --git a/releasenotes/notes/add-lb-status-tree-723f23c09617de3b.yaml b/releasenotes/notes/add-lb-status-tree-723f23c09617de3b.yaml
new file mode 100644
index 000000000..8f48d2863
--- /dev/null
+++ b/releasenotes/notes/add-lb-status-tree-723f23c09617de3b.yaml
@@ -0,0 +1,7 @@
+---
+features:
+ - |
+ CLI support for load balancer status tree.
+
+ * The ``lbaas-loadbalancer-status`` command provides the status
+ tree of a specific load balancer.
\ No newline at end of file
diff --git a/releasenotes/notes/add-neutron-purge-a89e3d1179dce4b1.yaml b/releasenotes/notes/add-neutron-purge-a89e3d1179dce4b1.yaml
new file mode 100644
index 000000000..a689b89ed
--- /dev/null
+++ b/releasenotes/notes/add-neutron-purge-a89e3d1179dce4b1.yaml
@@ -0,0 +1,16 @@
+---
+features:
+ - |
+ New command 'neutron purge ' will delete all
+ supported resources owned by the given tenant, provided
+ that the user has sufficient authorization and the
+ resources in question are not shared, in use, or
+ otherwise undeletable.
+
+ Supported resources are:
+ * Networks
+ * Subnets
+ * Routers
+ * Ports
+ * Floating IPs
+ * Security Groups
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-no-shared-option-to-qos-policy-update-56ac41fb3af7e309.yaml b/releasenotes/notes/add-no-shared-option-to-qos-policy-update-56ac41fb3af7e309.yaml
new file mode 100644
index 000000000..0c34f5eeb
--- /dev/null
+++ b/releasenotes/notes/add-no-shared-option-to-qos-policy-update-56ac41fb3af7e309.yaml
@@ -0,0 +1,6 @@
+---
+fixes:
+ - |
+ CLI support to set QoS policy as not shared if it was shared before.
+ The ``qos-policy-update`` command include a ``--no-shared`` option.
+ Closes `bug 1590942 `_.
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-osc-trunk-commands-7e77283a369729c5.yaml b/releasenotes/notes/add-osc-trunk-commands-7e77283a369729c5.yaml
new file mode 100644
index 000000000..684ae3102
--- /dev/null
+++ b/releasenotes/notes/add-osc-trunk-commands-7e77283a369729c5.yaml
@@ -0,0 +1,8 @@
+---
+features:
+ - |
+ 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 `_]
diff --git a/releasenotes/notes/add-quota-default-show-c2ab35b791dcdcbc.yaml b/releasenotes/notes/add-quota-default-show-c2ab35b791dcdcbc.yaml
new file mode 100644
index 000000000..3aececa35
--- /dev/null
+++ b/releasenotes/notes/add-quota-default-show-c2ab35b791dcdcbc.yaml
@@ -0,0 +1,7 @@
+---
+features:
+ - |
+ CLI support to display the default quota reserved for a tenant.
+
+ * The ``quota-default-show`` command outputs the default quota
+ of resources for a given tenant.
diff --git a/releasenotes/notes/add-rbac-qos-type-support-c42e31fadd7b.yaml b/releasenotes/notes/add-rbac-qos-type-support-c42e31fadd7b.yaml
new file mode 100644
index 000000000..9dff77c75
--- /dev/null
+++ b/releasenotes/notes/add-rbac-qos-type-support-c42e31fadd7b.yaml
@@ -0,0 +1,8 @@
+---
+features:
+ - |
+ CLI support for QoS policy RBAC.
+
+ * The ``rbac-create`` command include a --type qos-policy
+ option.
+ * The ``rbac-list`` command output includes a new 'type' column.
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-shared-pools-support-6f79b565afad3e47.yaml b/releasenotes/notes/add-shared-pools-support-6f79b565afad3e47.yaml
new file mode 100644
index 000000000..1be2e0ed1
--- /dev/null
+++ b/releasenotes/notes/add-shared-pools-support-6f79b565afad3e47.yaml
@@ -0,0 +1,8 @@
+---
+features:
+ - |
+ CLI support for Neutron-LBaaS v2 shared pools added.
+
+ * Pools can be created independently from listeners.
+ * Listeners can share the same default_pool.
+ * Makes Layer 7 switching support much more useful.
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/add-tag-support-bad62d60ecc7075c.yaml b/releasenotes/notes/add-tag-support-bad62d60ecc7075c.yaml
new file mode 100644
index 000000000..cc5b6eec2
--- /dev/null
+++ b/releasenotes/notes/add-tag-support-bad62d60ecc7075c.yaml
@@ -0,0 +1,16 @@
+---
+features:
+ - |
+ CLI support for tag.
+
+ * The ``tag-add`` command sets a tag on the network resource. It also
+ includes ``--resource-type``, ``--resource`` and ``--tag`` options.
+ * The ``tag-replace`` command replaces tags on the network resource. It
+ also includes ``--resource-type``, ``--resource`` and ``--tag``
+ options. More than one ``--tag`` options can be set.
+ * The ``tag-remove`` command removes tags on the network resource. It also
+ includes ``--resource-type``, ``--resource``, ``--tag`` and ``--all``
+ options. The ``--all`` option allow to remove all tags on the network
+ resource.
+ * The ``net-list`` command includes ``--tags``, ``--tags-any``,
+ ``--not-tags`` and ``--not-tags-any`` options.
\ No newline at end of file
diff --git a/releasenotes/notes/availability-zone-support-8e66f55e46b7ef9a.yaml b/releasenotes/notes/availability-zone-support-8e66f55e46b7ef9a.yaml
new file mode 100644
index 000000000..5551084f1
--- /dev/null
+++ b/releasenotes/notes/availability-zone-support-8e66f55e46b7ef9a.yaml
@@ -0,0 +1,10 @@
+---
+features:
+ - |
+ CLI support for availability zones.
+
+ * The ``availability-zone-list`` command provides a list of
+ availability zones.
+ * The ``net-create`` and ``router-create`` commands include a
+ ``--availability-zone-hint`` option.
+ * The ``agent-list`` command output includes availability zones.
diff --git a/releasenotes/notes/bgp-dynamic-routing-b97a1c81d3007049.yaml b/releasenotes/notes/bgp-dynamic-routing-b97a1c81d3007049.yaml
new file mode 100644
index 000000000..2fa2e8e6e
--- /dev/null
+++ b/releasenotes/notes/bgp-dynamic-routing-b97a1c81d3007049.yaml
@@ -0,0 +1,5 @@
+---
+features:
+ - |
+ CLI support for the BGP dynamic routing functionality will help
+ advertising neutron fixed-ips and dvr host routes via BGP.
\ 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/bulk-delete-support-94a353db08efec8d.yaml b/releasenotes/notes/bulk-delete-support-94a353db08efec8d.yaml
new file mode 100644
index 000000000..4d2575d9d
--- /dev/null
+++ b/releasenotes/notes/bulk-delete-support-94a353db08efec8d.yaml
@@ -0,0 +1,9 @@
+---
+features:
+ - |
+ CLI support for bulk delete.
+
+ * By using this feature, multiple resource
+ can be deleted using a single command.
+ * Example: ``neutron router-delete router_a router_b``
+ deletes both router_a and router_b.
diff --git a/releasenotes/notes/default-subnetpool-support-c0d34870e9d3e814.yaml b/releasenotes/notes/default-subnetpool-support-c0d34870e9d3e814.yaml
new file mode 100644
index 000000000..2a571d6d8
--- /dev/null
+++ b/releasenotes/notes/default-subnetpool-support-c0d34870e9d3e814.yaml
@@ -0,0 +1,9 @@
+---
+features:
+ - |
+ CLI support for default subnetpools.
+
+ * The ``subnetpool-list`` and ``subnetpool-show`` command output includes
+ the ``is_default`` field.
+ * The ``subnetpool-create`` and ``subnetpool-update`` commands include a
+ ``--is-default`` option.
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/direct-physical-vnic-port-create-736d8b2600faf22b.yaml b/releasenotes/notes/direct-physical-vnic-port-create-736d8b2600faf22b.yaml
new file mode 100644
index 000000000..911377496
--- /dev/null
+++ b/releasenotes/notes/direct-physical-vnic-port-create-736d8b2600faf22b.yaml
@@ -0,0 +1,6 @@
+---
+features:
+ - Added new 'direct-physical' vnic-type option for port-create CLI command.
+ Passing this particular value allows for a port to be create with the
+ vnic-type used for assigning SR-IOV physical functions to
+ instances.
diff --git a/releasenotes/notes/docs-improvements-17e31babe38e2962.yaml b/releasenotes/notes/docs-improvements-17e31babe38e2962.yaml
new file mode 100644
index 000000000..f00dc742d
--- /dev/null
+++ b/releasenotes/notes/docs-improvements-17e31babe38e2962.yaml
@@ -0,0 +1,5 @@
+---
+other:
+ - |
+ Addition of CLI user documentation including output filters, extra
+ options, and operation using os-client-config.
diff --git a/releasenotes/notes/drop-nuage-commands-df10aab6ccd77ed2.yaml b/releasenotes/notes/drop-nuage-commands-df10aab6ccd77ed2.yaml
new file mode 100644
index 000000000..b76f40f05
--- /dev/null
+++ b/releasenotes/notes/drop-nuage-commands-df10aab6ccd77ed2.yaml
@@ -0,0 +1,3 @@
+---
+upgrade:
+ - Remove Nuage-specific commands.
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/drop-xml-support-41babecb1784d996.yaml b/releasenotes/notes/drop-xml-support-41babecb1784d996.yaml
new file mode 100644
index 000000000..23c95e688
--- /dev/null
+++ b/releasenotes/notes/drop-xml-support-41babecb1784d996.yaml
@@ -0,0 +1,5 @@
+---
+upgrade:
+ - XML request format support has been removed.
+deprecations:
+ - request-format option is deprecated.
diff --git a/releasenotes/notes/dscp_qos-4a26d3c0363624b0.yaml b/releasenotes/notes/dscp_qos-4a26d3c0363624b0.yaml
new file mode 100644
index 000000000..37770cd51
--- /dev/null
+++ b/releasenotes/notes/dscp_qos-4a26d3c0363624b0.yaml
@@ -0,0 +1,6 @@
+---
+prelude: >
+ Adding new QoS DSCP marking rule commands.
+features:
+ - New create, update, list, show, and delete commands are added for QoS
+ DSCP marking rule functionality.
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-exception-typeerror-4.1.0-b37d738146575ed5.yaml b/releasenotes/notes/fix-exception-typeerror-4.1.0-b37d738146575ed5.yaml
new file mode 100644
index 000000000..a21eebbeb
--- /dev/null
+++ b/releasenotes/notes/fix-exception-typeerror-4.1.0-b37d738146575ed5.yaml
@@ -0,0 +1,5 @@
+---
+critical:
+ - Fix a critical bug that when lazy translation is enabled
+ NeutronClientException raises a TypeError
+ (`bug 1552760 `_).
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/fix-token-endpoint-auth-support-26bf7ee12e4ec833.yaml b/releasenotes/notes/fix-token-endpoint-auth-support-26bf7ee12e4ec833.yaml
new file mode 100644
index 000000000..9f8b29040
--- /dev/null
+++ b/releasenotes/notes/fix-token-endpoint-auth-support-26bf7ee12e4ec833.yaml
@@ -0,0 +1,6 @@
+---
+fixes:
+ - Fix `bug 1450414 `_
+ that authentication with via ``--os-token`` and ``--os-url`` options
+ (or corresponding environment variables) does not work
+ after keystone v3 API support.
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/keystonev3-7f9ede9c21b30841.yaml b/releasenotes/notes/keystonev3-7f9ede9c21b30841.yaml
new file mode 100644
index 000000000..507259663
--- /dev/null
+++ b/releasenotes/notes/keystonev3-7f9ede9c21b30841.yaml
@@ -0,0 +1,7 @@
+---
+deprecations:
+ - |
+ Keystone v3 support for CLI
+
+ * Using 'tenant_id' and 'tenant_name' arguments in API bindings is
+ deprecated. Use 'project_id' and 'project_name' arguments instead.
diff --git a/releasenotes/notes/log-request-id-64bef955b8292c18.yaml b/releasenotes/notes/log-request-id-64bef955b8292c18.yaml
new file mode 100644
index 000000000..62c73fa2e
--- /dev/null
+++ b/releasenotes/notes/log-request-id-64bef955b8292c18.yaml
@@ -0,0 +1,3 @@
+---
+features:
+ - Added support to log 'x-openstack-request-id' for each api call.
\ No newline at end of file
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/network-ip-availability-ac9a462f42fe9db4.yaml b/releasenotes/notes/network-ip-availability-ac9a462f42fe9db4.yaml
new file mode 100644
index 000000000..7a120fd7e
--- /dev/null
+++ b/releasenotes/notes/network-ip-availability-ac9a462f42fe9db4.yaml
@@ -0,0 +1,9 @@
+---
+features:
+ - |
+ CLI support for network IP availability
+
+ * The ``net-ip-availability-list`` command provides a list of IP
+ usage statistics for all networks.
+ * The ``net-ip-availability-show`` command provides IP usage stats
+ for a specific network.
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