Retire Packaging Deb project repos
This commit is part of a series to retire the Packaging Deb project. Step 2 is to remove all content from the project repos, replacing it with a README notification where to find ongoing work, and how to recover the repo if needed at some future point (as in https://docs.openstack.org/infra/manual/drivers.html#retiring-a-project). Change-Id: I76a161b00d94c91e065703ec3c2687329c36f453
This commit is contained in:
parent
b9101294a9
commit
67ee2140b0
|
@ -1,7 +0,0 @@
|
|||
[run]
|
||||
branch = True
|
||||
source = neutronclient
|
||||
omit = neutronclient/tests/*
|
||||
|
||||
[report]
|
||||
ignore_errors = True
|
|
@ -1,22 +0,0 @@
|
|||
*.pyc
|
||||
*.DS_Store
|
||||
*.egg
|
||||
*.sw?
|
||||
AUTHORS
|
||||
ChangeLog
|
||||
build/*
|
||||
build-stamp
|
||||
cover/*
|
||||
doc/build/
|
||||
doc/source/api/
|
||||
releasenotes/build/
|
||||
python_neutronclient.egg-info/*
|
||||
neutron/vcsversion.py
|
||||
neutronclient/versioninfo
|
||||
run_tests.err.log
|
||||
run_tests.log
|
||||
.autogenerated
|
||||
.coverage
|
||||
.testrepository/
|
||||
.tox/
|
||||
.venv/
|
|
@ -1,4 +0,0 @@
|
|||
[gerrit]
|
||||
host=review.openstack.org
|
||||
port=29418
|
||||
project=openstack/python-neutronclient.git
|
42
.pylintrc
42
.pylintrc
|
@ -1,42 +0,0 @@
|
|||
# The format of this file isn't really documented; just use --generate-rcfile
|
||||
[MASTER]
|
||||
# Add <file or directory> to the black list. It should be a base name, not a
|
||||
# path. You may set this option multiple times.
|
||||
ignore=test
|
||||
|
||||
[Messages Control]
|
||||
# NOTE(justinsb): We might want to have a 2nd strict pylintrc in future
|
||||
# C0111: Don't require docstrings on every method
|
||||
# W0511: TODOs in code comments are fine.
|
||||
# W0142: *args and **kwargs are fine.
|
||||
# W0622: Redefining id is fine.
|
||||
disable=C0111,W0511,W0142,W0622
|
||||
|
||||
[Basic]
|
||||
# Variable names can be 1 to 31 characters long, with lowercase and underscores
|
||||
variable-rgx=[a-z_][a-z0-9_]{0,30}$
|
||||
|
||||
# Argument names can be 2 to 31 characters long, with lowercase and underscores
|
||||
argument-rgx=[a-z_][a-z0-9_]{1,30}$
|
||||
|
||||
# Method names should be at least 3 characters long
|
||||
# and be lowecased with underscores
|
||||
method-rgx=([a-z_][a-z0-9_]{2,50}|setUp|tearDown)$
|
||||
|
||||
# Module names matching quantum-* are ok (files in bin/)
|
||||
module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+)|(quantum-[a-z0-9_-]+))$
|
||||
|
||||
# Don't require docstrings on tests.
|
||||
no-docstring-rgx=((__.*__)|([tT]est.*)|setUp|tearDown)$
|
||||
|
||||
[Design]
|
||||
max-public-methods=100
|
||||
min-public-methods=0
|
||||
max-args=6
|
||||
|
||||
[Variables]
|
||||
|
||||
# List of additional names supposed to be defined in builtins. Remember that
|
||||
# you should avoid to define new builtins when possible.
|
||||
# _ is used by our localization
|
||||
additional-builtins=_
|
|
@ -1,4 +0,0 @@
|
|||
[DEFAULT]
|
||||
test_command=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 ${PYTHON:-python} -m subunit.run discover -t ./ ${OS_TEST_PATH:-./neutronclient/tests/unit} $LISTOPT $IDOPTION
|
||||
test_id_option=--load-list $IDFILE
|
||||
test_list_option=--list
|
|
@ -1,16 +0,0 @@
|
|||
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
|
26
HACKING.rst
26
HACKING.rst
|
@ -1,26 +0,0 @@
|
|||
Neutron Style Commandments
|
||||
================================
|
||||
|
||||
- Step 1: Read the OpenStack Style Commandments
|
||||
http://docs.openstack.org/developer/hacking/
|
||||
- Step 2: Read on
|
||||
|
||||
|
||||
Running Tests
|
||||
-------------
|
||||
The testing system is based on a combination of tox and testr. The canonical
|
||||
approach to running tests is to simply run the command `tox`. This will
|
||||
create virtual environments, populate them with depenedencies and run all of
|
||||
the tests that OpenStack CI systems run. Behind the scenes, tox is running
|
||||
`testr run --parallel`, but is set up such that you can supply any additional
|
||||
testr arguments that are needed to tox. For example, you can run:
|
||||
`tox -- --analyze-isolation` to cause tox to tell testr to add
|
||||
--analyze-isolation to its argument list.
|
||||
|
||||
It is also possible to run the tests inside of a virtual environment
|
||||
you have created, or it is possible that you have all of the dependencies
|
||||
installed locally already. In this case, you can interact with the testr
|
||||
command directly. Running `testr run` will run the entire test suite. `testr
|
||||
run --parallel` will run it in parallel (this is the default incantation tox
|
||||
uses.) More information about testr can be found at:
|
||||
http://wiki.openstack.org/testr
|
176
LICENSE
176
LICENSE
|
@ -1,176 +0,0 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
include tox.ini
|
||||
include LICENSE README.rst HACKING.rst
|
||||
include AUTHORS
|
||||
include ChangeLog
|
||||
include tools/*
|
||||
recursive-include tests *
|
|
@ -0,0 +1,14 @@
|
|||
This project is no longer maintained.
|
||||
|
||||
The contents of this repository are still available in the Git
|
||||
source code management system. To see the contents of this
|
||||
repository before it reached its end of life, please check out the
|
||||
previous commit with "git checkout HEAD^1".
|
||||
|
||||
For ongoing work on maintaining OpenStack packages in the Debian
|
||||
distribution, please see the Debian OpenStack packaging team at
|
||||
https://wiki.debian.org/OpenStack/.
|
||||
|
||||
For any further questions, please email
|
||||
openstack-dev@lists.openstack.org or join #openstack-dev on
|
||||
Freenode.
|
31
README.rst
31
README.rst
|
@ -1,31 +0,0 @@
|
|||
Python bindings to the Neutron API
|
||||
==================================
|
||||
|
||||
.. image:: https://img.shields.io/pypi/v/python-neutronclient.svg
|
||||
:target: https://pypi.python.org/pypi/python-neutronclient/
|
||||
:alt: Latest Version
|
||||
|
||||
.. image:: https://img.shields.io/pypi/dm/python-neutronclient.svg
|
||||
:target: https://pypi.python.org/pypi/python-neutronclient/
|
||||
:alt: Downloads
|
||||
|
||||
This is a client library for Neutron built on the Neutron API. It
|
||||
provides a Python API (the ``neutronclient`` module) and a command-line tool
|
||||
(``neutron``).
|
||||
|
||||
* 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.python.org/pypi/python-neutronclient
|
||||
.. _Online Documentation: http://docs.openstack.org/developer/python-neutronclient
|
||||
.. _Launchpad project: https://launchpad.net/python-neutronclient
|
||||
.. _Blueprints: https://blueprints.launchpad.net/python-neutronclient
|
||||
.. _Bugs: https://bugs.launchpad.net/python-neutronclient
|
||||
.. _Source: https://git.openstack.org/cgit/openstack/python-neutronclient
|
||||
.. _Developer's Guide: http://docs.openstack.org/infra/manual/developers.html
|
|
@ -1,55 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
project = 'python-neutronclient'
|
||||
|
||||
# -- General configuration ---------------------------------------------
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = ['sphinx.ext.autodoc',
|
||||
'oslosphinx',
|
||||
'reno.sphinxext',
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
copyright = u'OpenStack Foundation'
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
add_module_names = True
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# -- Options for HTML output ---------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. Major themes that come with
|
||||
# Sphinx are currently 'default' and 'sphinxdoc'.
|
||||
# html_theme = 'nature'
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = '%sdoc' % project
|
||||
|
||||
|
||||
# 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'),
|
||||
]
|
|
@ -1,263 +0,0 @@
|
|||
..
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
not use this file except in compliance with the License. You may obtain
|
||||
a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
|
||||
Convention for heading levels in Neutron devref:
|
||||
======= Heading 0 (reserved for the title in a document)
|
||||
------- Heading 1
|
||||
~~~~~~~ Heading 2
|
||||
+++++++ Heading 3
|
||||
''''''' Heading 4
|
||||
(Avoid deeper levels because they do not render well.)
|
||||
|
||||
CLI Option Guideline
|
||||
====================
|
||||
|
||||
This document describes the conventions of neutron CLI options.
|
||||
|
||||
General conventions
|
||||
-------------------
|
||||
|
||||
#. Option names should be delimited by a hyphen instead of a underscore.
|
||||
This is the common guidelines across all OpenStack CLIs.
|
||||
|
||||
* Good: ``--ip-version``
|
||||
* Not Good: ``--ip_version``
|
||||
|
||||
#. Use at least one required option for ``*-create`` command. If all options
|
||||
are optional, we typically use ``name`` field as a required option.
|
||||
|
||||
#. When you need to specify an ID of a resource, it is better to provide
|
||||
another way to specify the resource like ``name`` or other reasonable field.
|
||||
|
||||
#. If an attribute name in API is ``foo_id``, the corresponding option
|
||||
should be ``--foo`` instead of ``--foo-id``.
|
||||
|
||||
* It is because we usually support ID and ``name`` to specify a resource.
|
||||
|
||||
#. Do not use ``nargs='?'`` without a special reason.
|
||||
|
||||
* The behavior of ``nargs='?'`` option for python argparse is
|
||||
bit tricky and may lead to unexpected option parsing different
|
||||
from the help message. The detail is described in the
|
||||
:ref:`Background section <background-nargs>` below.
|
||||
|
||||
#. (option) Avoid using positional options as much as possible.
|
||||
|
||||
* Positional arguments should be limited to attributes which will
|
||||
be required in the long future.
|
||||
|
||||
#. We honor existing options and should keep compatibilities when adding or
|
||||
changing options.
|
||||
|
||||
Options for boolean value
|
||||
-------------------------
|
||||
|
||||
Use the form of ``--option-name {True|False}``.
|
||||
|
||||
* For a new option, it is recommended.
|
||||
* It is suggested to use ``common.utils.add_boolean_argument`` in an
|
||||
implementation. It allows ``true``/``false`` in addition to ``True``/``False``.
|
||||
* For existing options, migration to the recommended form is not necessarily
|
||||
required. All backward-compatibility should be kept without reasonable
|
||||
reasons.
|
||||
|
||||
Options for dict value
|
||||
----------------------
|
||||
|
||||
Some API attributes take a dictionary.
|
||||
|
||||
``--foo key1=val1,key2=val2`` is usually used.
|
||||
|
||||
This means ``{"key1": "val1", "key2": "val2"}`` is passed in the API layer.
|
||||
|
||||
Examples:
|
||||
|
||||
* ``--host-route destination=CIDR,nexthop=IP_ADDR`` for a subnet
|
||||
* ``--fixed-ip subnet_id=SUBNET,ip_address=IP_ADDR`` for a port.
|
||||
|
||||
Options for list value
|
||||
----------------------
|
||||
|
||||
Some attributes take a list.
|
||||
|
||||
In this case, we usually use:
|
||||
|
||||
* Define an option per element (Use a singular form as an option name)
|
||||
* Allow to specify the option multiple times
|
||||
|
||||
For Example, **port-create** has ``--security-group`` option.
|
||||
``--security-group SG1 --security-group SG2`` generates
|
||||
``{"security_groups: ["SG1", "SG2"]}`` in the API layer.
|
||||
|
||||
This convention applies to a case of a list of dict.
|
||||
``--allocation-pool`` and ``--host-route`` for a subnet are examples.
|
||||
|
||||
Compatibility with extra arguments
|
||||
----------------------------------
|
||||
|
||||
*extra arguments* supports various types of option specifications.
|
||||
At least the following patterns needs to be considered when defining
|
||||
a new option. For more detail, see :ref:`cli_extra_arguments`.
|
||||
|
||||
* Normal options with value
|
||||
* Boolean options : ``--foo True``, ``--bar=False``
|
||||
* List options : ``--bars list=true val1 val2``, ``--bars val1 val2``
|
||||
* Dict options : ``--foo type=dict key1=va1,key2=val2``
|
||||
* List of Dict options : ``--bars list=true type=dict key1=val1,key2=val2 key3=val3,key4=val4``
|
||||
* ``action=clear``
|
||||
|
||||
For normal options with value, there are four patterns to specify an option
|
||||
as extra arguments.
|
||||
|
||||
* ``--admin-state-up True`` (a space between option name and value)
|
||||
* ``--admin-state-up=True`` (= between option name and value)
|
||||
* ``--admin_state_up True`` (underscore is used as delimiter)
|
||||
* ``--admin_state_up=True`` (underscore is used as delimiter)
|
||||
|
||||
.. _background:
|
||||
|
||||
Background
|
||||
----------
|
||||
|
||||
There are a lot of opinions on which form of options are better or not.
|
||||
This section tries to capture the reason of the current choice.
|
||||
|
||||
Use at least one required option
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
As a convention, **neutron** CLI requires one required argument.
|
||||
|
||||
If all options are optional in the API level and we have ``name`` field,
|
||||
we usually use ``name`` as a required parameter.
|
||||
Requiring at least one argument has the following benefits:
|
||||
|
||||
* If we run ``neutron *-create`` without a required argument, we will have a
|
||||
brief help message without detail option help. It is convenient.
|
||||
* We can avoid miss operation by just hitting ``neutron *-create``.
|
||||
Requiring at least one parameter is a good balance.
|
||||
|
||||
Even though we can change this convention to allow to create a resource
|
||||
without ``name`` field, it will bring confusions to existing users.
|
||||
|
||||
There may be opinion that it is inconsistent with API level requirement
|
||||
or Horizon behavior, but even if neutron CLI requires ``name`` field
|
||||
there is no bad impact on regular users. Considering possible confusion
|
||||
if we change it, it looks better to keep it as-is.
|
||||
|
||||
Options for Boolean value
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* ``--enable-foo``/``--disable-foo`` or similar patterns (including
|
||||
``--admin-state-down``) is not suggested because we need two exclusive
|
||||
options for one attribute in REST API. It is meaningless.
|
||||
|
||||
* It is not recommended to have an option only to specify non-default value.
|
||||
For example, we have ``--shared`` or ``--admin-state-down`` options for
|
||||
net-create. This form only works for ``*-create`` and does not work for
|
||||
``*-update``. It leads to having different options for ``*-create`` and
|
||||
``*-update``.
|
||||
|
||||
* A flag option like ``--enable-dhcp`` (without value) also has a problem when
|
||||
considering the compatibility with *extra argument*. We can specify
|
||||
``-enable-dhcp True/False`` or ``--enable-dhcp=True/False`` in the *extra
|
||||
argument* mechanism. If we introduce ``--enable-dhcp`` (without value),
|
||||
the form of ``-enable-dhcp True/False`` cannot be used now.
|
||||
This is another reason we don't use a flag style option for a boolean parameter.
|
||||
|
||||
.. _background-nargs:
|
||||
|
||||
Avoid using nargs in positional or optional arguments
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The behavior of ``nargs='?'`` option for python argparse is bit tricky.
|
||||
When we use ``nargs='?'`` and if the order of command-line options is
|
||||
changed then the command-line parser may fail to parse the arguments
|
||||
correctly. Two examples of such failures are provided below.
|
||||
|
||||
Example 1:
|
||||
This example shows how the actual behavior can differ from the provided
|
||||
help message. In the below block, help message at ``[5]`` says ``--bb CC``
|
||||
is a valid format but the argument parsing for the same format fails at ``[7]``.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
In [1]: import argparse
|
||||
In [2]: parser = argparse.ArgumentParser()
|
||||
In [3]: parser.add_argument('--bb', nargs='?')
|
||||
In [4]: parser.add_argument('cc')
|
||||
|
||||
In [5]: parser.print_help()
|
||||
usage: ipython [-h] [--bb [BB]] cc
|
||||
|
||||
positional arguments:
|
||||
cc
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--bb [BB]
|
||||
|
||||
In [6]: parser.parse_args('--bb 1 X'.split())
|
||||
Out[6]: Namespace(bb='1', cc='X')
|
||||
|
||||
In [7]: parser.parse_args('--bb X'.split())
|
||||
usage: ipython [-h] [--bb [BB]] cc
|
||||
ipython: error: too few arguments
|
||||
An exception has occurred, use %tb to see the full traceback.
|
||||
|
||||
SystemExit: 2
|
||||
|
||||
|
||||
Example 2:
|
||||
This example shows how fragile ``nargs='?'`` can be when user specifies
|
||||
options in different order from the help message.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
In [1]: import argparse
|
||||
In [2]: parser = argparse.ArgumentParser()
|
||||
In [3]: parser.add_argument('--a', help='option a')
|
||||
In [4]: parser.add_argument('--b', help='option b')
|
||||
In [5]: parser.add_argument('x', help='positional arg X')
|
||||
In [6]: parser.add_argument('y', nargs='?', help='positional arg Y')
|
||||
In [7]: parser.print_help()
|
||||
usage: ipython [-h] [--a A] [--b B] x [y]
|
||||
|
||||
positional arguments:
|
||||
x positional arg X
|
||||
y positional arg Y
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--a A option a
|
||||
--b B option b
|
||||
|
||||
In [8]: parser.parse_args('--a 1 --b 2 X Y'.split())
|
||||
Out[8]: Namespace(a='1', b='2', x='X', y='Y')
|
||||
|
||||
In [9]: parser.parse_args('X Y --a 1 --b 2'.split())
|
||||
Out[9]: Namespace(a='1', b='2', x='X', y='Y')
|
||||
|
||||
In [10]: parser.parse_args('X --a 1 --b 2 Y'.split())
|
||||
usage: ipython [-h] [--a A] [--b B] x [y]
|
||||
ipython: error: unrecognized arguments: Y
|
||||
An exception has occurred, use %tb to see the full traceback.
|
||||
|
||||
SystemExit: 2
|
||||
|
||||
To exit: use 'exit', 'quit', or Ctrl-D.
|
||||
To exit: use 'exit', 'quit', or Ctrl-D.
|
||||
|
||||
Note: Most CLI users don't care about the order of the command-line
|
||||
options. Hence, such fragile behavior should be avoided.
|
||||
|
|
@ -1,97 +0,0 @@
|
|||
..
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
not use this file except in compliance with the License. You may obtain
|
||||
a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
|
||||
Convention for heading levels in Neutron devref:
|
||||
======= Heading 0 (reserved for the title in a document)
|
||||
------- Heading 1
|
||||
~~~~~~~ Heading 2
|
||||
+++++++ Heading 3
|
||||
''''''' Heading 4
|
||||
(Avoid deeper levels because they do not render well.)
|
||||
|
||||
Client command extension support
|
||||
=================================
|
||||
|
||||
The client command extension adds support for extending the neutron client while
|
||||
considering ease of creation.
|
||||
Extensions strongly conform to preexisting neutron commands (/neutron/v2_0/).
|
||||
|
||||
A sample extension can be seen at:
|
||||
neutronclient/neutron/v2_0/contrib/_fox_sockets.py
|
||||
|
||||
Minimum requirements from an extension
|
||||
--------------------------------------
|
||||
|
||||
* NeutronClientExtension subclasses must have a shell_command class variable
|
||||
if the command is to be available to the CLI (shell.py)
|
||||
|
||||
Example: neutronclient.neutron.v2_0.contrib._fox_sockets.FoxInSocketsList
|
||||
|
||||
Minimum requirements to use canonical neutron CRUD commands framework
|
||||
----------------------------------------------------------------------
|
||||
|
||||
Neutron commands are cliff commands, commands in extension can use their
|
||||
own way to finish their tasks. But if they want to make use of the canonical
|
||||
neutron CRUD commands framework, the extension should:
|
||||
|
||||
* have a class that subclasses NeutronClientExtension to provide the
|
||||
requisite resource name, version support, and resource collection and
|
||||
object paths for a resource the commands will process.
|
||||
|
||||
Example: neutronclient.neutron.v2_0.contrib._fox_sockets.FoxInSocket
|
||||
|
||||
* have a class that subclasses from the ClientExtensionList to provide
|
||||
resource object list function. This is because most commands
|
||||
need the list function to get object ID via
|
||||
neutronclient.neutron.v2_0.__init__.find_resource_by_id.
|
||||
|
||||
Example: neutronclient.neutron.v2_0.contrib._fox_sockets.FoxInSocketsList
|
||||
|
||||
* if needed, subclass ClientExtensionUpdate to implement update of the resource
|
||||
object.
|
||||
|
||||
Example: neutronclient.neutron.v2_0.contrib._fox_sockets.FoxInSocketsUpdate
|
||||
|
||||
* if needed, subclass ClientExtensionDelete to implement deletion of the resource
|
||||
object.
|
||||
|
||||
Example: neutronclient.neutron.v2_0.contrib._fox_sockets.FoxInSocketsDelete
|
||||
|
||||
* if needed, subclass ClientExtensionShow to get the detail of the resource
|
||||
object.
|
||||
|
||||
Example: neutronclient.neutron.v2_0.contrib._fox_sockets.FoxInSocketsShow
|
||||
|
||||
Precedence of command loading
|
||||
------------------------------
|
||||
|
||||
* hard coded commands are loaded first
|
||||
* external commands (installed in the environment) are loaded then
|
||||
|
||||
Commands that have the same name will be overwritten by commands that are
|
||||
loaded later. To change the execution of a command for your particular
|
||||
extension you only need to override the execute method.
|
||||
|
||||
Currently this extension support is limited to top-level resources.
|
||||
Parent/child relationships may be added if desired.
|
||||
|
||||
neutronclient.extension entry_point
|
||||
-----------------------------------
|
||||
|
||||
To activate the commands in a specific extension module, add an entry in
|
||||
setup.cfg under neutronclient.extension. For example::
|
||||
|
||||
[entry_points]
|
||||
neutronclient.extension =
|
||||
fox_sockets = neutronclient.neutron.v2_0.contrib._fox_sockets
|
|
@ -1,228 +0,0 @@
|
|||
..
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
not use this file except in compliance with the License. You may obtain
|
||||
a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
|
||||
Convention for heading levels in Neutron devref:
|
||||
======= Heading 0 (reserved for the title in a document)
|
||||
------- Heading 1
|
||||
~~~~~~~ Heading 2
|
||||
+++++++ Heading 3
|
||||
''''''' Heading 4
|
||||
(Avoid deeper levels because they do not render well.)
|
||||
|
||||
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) <https://github.com/openstack/python-openstackclient>`_
|
||||
and the `OpenStack Python SDK <https://github.com/openstack/python-openstacksdk>`_.
|
||||
This transition is being guided by the
|
||||
`Deprecate individual CLIs in favour of OSC <https://review.openstack.org/#/c/243348/>`_
|
||||
OpenStack spec. See the `Neutron RFE <https://bugs.launchpad.net/neutron/+bug/1521291>`_,
|
||||
`OSC neutron support etherpad <https://etherpad.openstack.org/p/osc-neutron-support>`_ 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 <https://github.com/openstack/python-keystoneclient>`_
|
||||
``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 <http://docs.openstack.org/developer/python-openstackclient/plugins.html>`_
|
||||
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.openstack.org/#/c/138745/
|
||||
|
||||
2. **Done:** OSC switches its networking support for the
|
||||
`network <http://docs.openstack.org/developer/python-openstackclient/command-objects/network.html>`_
|
||||
command object to use the OpenStack Python SDK instead of the neutron
|
||||
client's Python library. See the following patch set:
|
||||
https://review.openstack.org/#/c/253348/
|
||||
|
||||
3. **Done:** OSC removes its python-neutronclient dependency.
|
||||
See the following patch set: https://review.openstack.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.
|
||||
|
||||
5. **Done:** OSC switches its networking support for the
|
||||
`ip floating <http://docs.openstack.org/developer/python-openstackclient/command-objects/ip-floating.html>`_,
|
||||
`ip floating pool <http://docs.openstack.org/developer/python-openstackclient/command-objects/ip-floating-pool.html>`_,
|
||||
`ip fixed <http://docs.openstack.org/developer/python-openstackclient/command-objects/ip-fixed.html>`_,
|
||||
`security group <http://docs.openstack.org/developer/python-openstackclient/command-objects/security-group.html>`_, and
|
||||
`security group rule <http://docs.openstack.org/developer/python-openstackclient/command-objects/security-group-rule.html>`_
|
||||
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 <https://bugs.launchpad.net/python-openstackclient/+bug/1519502>`_
|
||||
|
||||
* **Done** `Port CRUD <https://bugs.launchpad.net/python-openstackclient/+bug/1519909>`_
|
||||
|
||||
* **Done** `Security Group CRUD <https://bugs.launchpad.net/python-openstackclient/+bug/1519511>`_
|
||||
|
||||
* **Done** `Security Group Rule CRUD <https://bugs.launchpad.net/python-openstackclient/+bug/1519512>`_
|
||||
|
||||
6. **In Progress:** OSC continues enhancing its networking support.
|
||||
At this point and when applicable, enhancements to the ``neutron``
|
||||
CLI must also be made to the ``openstack`` CLI and the OpenStack Python SDK.
|
||||
Enhancements to the networking support in the OpenStack Python SDK will be
|
||||
handled via bugs. Users of the neutron client's command extensions should
|
||||
start their transition to the OSC plugin system.
|
||||
See the developer guide section below for more information on this step.
|
||||
|
||||
7. **Not Started:** Deprecate the ``neutron`` CLI once the criteria below have
|
||||
been meet. Running the CLI after it has been deprecated will issue a warning
|
||||
messages such as the following:
|
||||
``DeprecationWarning: The neutron CLI is deprecated in favor of python-openstackclient.``
|
||||
In addition, only security fixes will be made to the CLI after it has been
|
||||
deprecated.
|
||||
|
||||
* 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 <http://docs.openstack.org/developer/neutron/stadium/sub_projects.html>`_
|
||||
projects, Neutron documentation and `DevStack <http://docs.openstack.org/developer/devstack/>`_
|
||||
use ``openstack`` CLI instead of ``neutron`` CLI.
|
||||
|
||||
* Most users of the neutron client's command extensions have transitioned
|
||||
to the OSC plugin system and use the ``openstack`` CLI instead of the
|
||||
``neutron`` CLI.
|
||||
|
||||
8. **Not Started:** Remove the ``neutron`` CLI after two deprecation cycles.
|
||||
|
||||
Developer Guide
|
||||
---------------
|
||||
The ``neutron`` CLI version 4.x, without extensions, supports over 200
|
||||
commands while the ``openstack`` CLI version 2.6.0 supports over 50
|
||||
networking commands. Of the 50 commands, some do not have all of the options
|
||||
or arguments of their ``neutron`` CLI equivalent. With this large functional
|
||||
gap, a couple critical questions for developers during this transition are "Which
|
||||
CLI do I change?" and "Where does my CLI belong?" The answer depends on the
|
||||
state of a command and the state of the overall transition. Details are
|
||||
outlined in the tables below. Early stages of the transition will require dual
|
||||
maintenance. Eventually, dual maintenance will be reduced to critical bug fixes
|
||||
only with feature requests only being made to the ``openstack`` CLI.
|
||||
|
||||
**Which CLI do I change?**
|
||||
|
||||
+----------------------+------------------------+-------------------------------------------------+
|
||||
| ``neutron`` Command | ``openstack`` Command | CLI to Change |
|
||||
+======================+========================+=================================================+
|
||||
| Exists | Doesn't Exist | ``neutron`` |
|
||||
+----------------------+------------------------+-------------------------------------------------+
|
||||
| Exists | In Progress | ``neutron`` and ``openstack`` |
|
||||
| | | (update related blueprint or bug) |
|
||||
+----------------------+------------------------+-------------------------------------------------+
|
||||
| Exists | Exists | ``openstack`` |
|
||||
| | | (assumes command parity resulting in |
|
||||
| | | ``neutron`` being deprecated) |
|
||||
+----------------------+------------------------+-------------------------------------------------+
|
||||
| Doesn't Exist | Doesn't Exist | ``openstack`` |
|
||||
+----------------------+------------------------+-------------------------------------------------+
|
||||
|
||||
**Where does my CLI belong?**
|
||||
|
||||
+---------------------------+-------------------+-------------------------------------------------+
|
||||
| Networking Commands | OSC Plugin | OpenStack Project for ``openstack`` Commands |
|
||||
+===========================+===================+=================================================+
|
||||
| Core | No | python-openstackclient |
|
||||
+---------------------------+-------------------+-------------------------------------------------+
|
||||
| Dynamic Routing | Yes | python-neutronclient |
|
||||
| | | (``neutronclient/osc/v2/dynamic_routing``) |
|
||||
+---------------------------+-------------------+-------------------------------------------------+
|
||||
| FWaaS v1 | N/A | None (deprecated) |
|
||||
+---------------------------+-------------------+-------------------------------------------------+
|
||||
| FWaaS v2 | Yes | python-neutronclient |
|
||||
| | | (``neutronclient/osc/v2/fwaas``) |
|
||||
+---------------------------+-------------------+-------------------------------------------------+
|
||||
| LBaaS v1 | N/A | None (deprecated) |
|
||||
+---------------------------+-------------------+-------------------------------------------------+
|
||||
| LBaaS v2 | Yes | python-neutronclient |
|
||||
| | | (``neutronclient/osc/v2/lbaas``) |
|
||||
+---------------------------+-------------------+-------------------------------------------------+
|
||||
| Other | Yes | Applicable project owning networking resource |
|
||||
+---------------------------+-------------------+-------------------------------------------------+
|
||||
| VPNaaS | Yes | python-neutronclient |
|
||||
| | | (``neutronclient/osc/v2/vpnaas``) |
|
||||
+---------------------------+-------------------+-------------------------------------------------+
|
||||
|
||||
|
||||
**Important:** The actual name of the command object and/or action in OSC may differ
|
||||
from those used by neutron in order to follow the OSC command structure and to avoid
|
||||
name conflicts. Developers should get new command objects and actions approved by
|
||||
the OSC team before proceeding with the implementation.
|
||||
|
||||
The "Core" group includes network resources that provide ``neutron`` project features
|
||||
(i.e. not advanced service or other features). Examples in the "Core" group include:
|
||||
network, subnet, port, etc.
|
||||
|
||||
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 <https://github.com/openstack/python-openstackclient/blob/master/requirements.txt>`_
|
||||
file. ``openstack`` networking commands outside python-openstackclient
|
||||
are encouraged but not required to use the OpenStack Python SDK.
|
||||
|
||||
When adding an ``openstack`` networking command to python-openstackclient,
|
||||
you can optionally propose an
|
||||
`OSC command spec <https://github.com/openstack/python-openstackclient/blob/master/doc/source/specs/commands.rst>`_
|
||||
which documents the new command interface before proceeding with the implementation.
|
||||
|
||||
Users of the neutron client's command extensions must adopt the
|
||||
`OSC plugin <https://github.com/openstack/python-openstackclient/blob/master/doc/source/plugins.rst>`_
|
||||
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 <https://etherpad.openstack.org/p/osc-neutron-support>`_
|
||||
to determine if an ``openstack`` command is in progress.
|
||||
* See `OSC command list <https://github.com/openstack/python-openstackclient/tree/master/doc/source/command-objects>`_
|
||||
to determine if an ``openstack`` command exists.
|
||||
* See `OSC command spec list <https://github.com/openstack/python-openstackclient/tree/master/doc/source/specs/command-objects>`_
|
||||
to determine if an ``openstack`` command spec exists.
|
||||
* See `OSC plugin command list <http://docs.openstack.org/developer/python-openstackclient/plugin-commands.html>`_
|
||||
to determine if an ``openstack`` plugin command exists.
|
||||
* See `OSC command structure <https://github.com/openstack/python-openstackclient/blob/master/doc/source/commands.rst>`_
|
||||
to determine the current ``openstack`` command objects, plugin objects and actions.
|
||||
* See `OSC human interface guide <https://github.com/openstack/python-openstackclient/blob/master/doc/source/humaninterfaceguide.rst>`_
|
||||
for guidance on creating new OSC command interfaces.
|
||||
* See `OSC plugin <https://github.com/openstack/python-openstackclient/blob/master/doc/source/plugins.rst>`_
|
||||
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
|
|
@ -1,58 +0,0 @@
|
|||
..
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
not use this file except in compliance with the License. You may obtain
|
||||
a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
Convention for heading levels in Neutron devref:
|
||||
======= Heading 0 (reserved for the title in a document)
|
||||
------- Heading 1
|
||||
~~~~~~~ Heading 2
|
||||
+++++++ Heading 3
|
||||
''''''' Heading 4
|
||||
(Avoid deeper levels because they do not render well.)
|
||||
|
||||
Python bindings to the OpenStack Networking API
|
||||
===============================================
|
||||
|
||||
This is a client for OpenStack Networking API. There is a :doc:`Python API
|
||||
<usage/library>` (the neutronclient module), and a :doc:`command-line script
|
||||
<usage/cli>` (installed as **neutron**). Each implements the entire OpenStack
|
||||
Networking API.
|
||||
|
||||
Using neutronclient
|
||||
-------------------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
usage/cli
|
||||
usage/library
|
||||
usage/osc_cli_plugins
|
||||
|
||||
Developer Guide
|
||||
---------------
|
||||
|
||||
In the Developer Guide, you will find information on Neutron’s client
|
||||
lower level programming details or APIs as well as the transition to
|
||||
OpenStack client.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
devref/client_command_extensions
|
||||
devref/cli_option_guideline
|
||||
devref/transition_to_osc
|
||||
|
||||
History
|
||||
-------
|
||||
|
||||
Release notes is available at
|
||||
http://docs.openstack.org/releasenotes/python-neutronclient/.
|
|
@ -1,390 +0,0 @@
|
|||
..
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
not use this file except in compliance with the License. You may obtain
|
||||
a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
|
||||
Convention for heading levels in Neutron devref:
|
||||
======= Heading 0 (reserved for the title in a document)
|
||||
------- Heading 1
|
||||
~~~~~~~ Heading 2
|
||||
+++++++ Heading 3
|
||||
''''''' Heading 4
|
||||
(Avoid deeper levels because they do not render well.)
|
||||
|
||||
Command-line Interface
|
||||
======================
|
||||
|
||||
The **neutron** shell utility interacts with OpenStack Networking API from the
|
||||
command-line. It supports the entire features of OpenStack Networking API.
|
||||
|
||||
Basic Usage
|
||||
-----------
|
||||
|
||||
In order to use the CLI, you must provide your OpenStack username, password,
|
||||
tenant, and auth endpoint. Use the corresponding configuration options
|
||||
(``--os-username``, ``--os-password``, ``--os-tenant-name``, and
|
||||
``--os-auth-url``), but it is easier to set them in environment variables.
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
export OS_USERNAME=user
|
||||
export OS_PASSWORD=pass
|
||||
export OS_TENANT_NAME=tenant
|
||||
export OS_AUTH_URL=http://auth.example.com:5000/v2.0
|
||||
|
||||
Once you've configured your authentication parameters, you can run **neutron**
|
||||
commands. All commands take the form of:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
neutron <command> [arguments...]
|
||||
|
||||
Run **neutron help** to get a full list of all possible commands, and run
|
||||
**neutron help <command>** to get detailed help for that command.
|
||||
|
||||
Using with os-client-config
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
`os-client-config <http://docs.openstack.org/developer/os-client-config/>`_
|
||||
provides more convenient way to manage a collection of client configurations
|
||||
and you can easily switch multiple OpenStack-based configurations.
|
||||
|
||||
To use os-client-config, you first need to prepare
|
||||
``~/.config/openstack/clouds.yaml`` like the following.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
clouds:
|
||||
devstack:
|
||||
auth:
|
||||
auth_url: http://auth.example.com:5000
|
||||
password: your-secret
|
||||
project_domain_id: default
|
||||
project_name: demo
|
||||
user_domain_id: default
|
||||
username: demo
|
||||
identity_api_version: '3'
|
||||
region_name: RegionOne
|
||||
devstack-admin:
|
||||
auth:
|
||||
auth_url: http://auth.example.com:35357
|
||||
password: another-secret
|
||||
project_domain_id: default
|
||||
project_name: admin
|
||||
user_domain_id: default
|
||||
username: admin
|
||||
identity_api_version: '3'
|
||||
region_name: RegionOne
|
||||
|
||||
Then, you need to specify a configuration name defined in the above clouds.yaml.
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
export OS_CLOUD=devstack
|
||||
|
||||
For more detail information, see the
|
||||
`os-client-config <http://docs.openstack.org/developer/os-client-config/>`_
|
||||
documentation.
|
||||
|
||||
Using with keystone token
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The command-line tool will attempt to re-authenticate using your provided
|
||||
credentials for every request. You can override this behavior by manually
|
||||
supplying an auth token using ``--os-url`` and ``--os-auth-token``. You can
|
||||
alternatively set these environment variables.
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
export OS_URL=http://neutron.example.org:9696/
|
||||
export OS_TOKEN=3bcc3d3a03f44e3d8377f9247b0ad155
|
||||
|
||||
Using noauth mode
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
If neutron server does not require authentication, besides these two arguments
|
||||
or environment variables (We can use any value as token.), we need manually
|
||||
supply ``--os-auth-strategy`` or set the environment variable.
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
export OS_AUTH_STRATEGY=noauth
|
||||
|
||||
Display options
|
||||
---------------
|
||||
|
||||
Filtering
|
||||
~~~~~~~~~
|
||||
|
||||
Neutron API supports filtering in the listing operation.
|
||||
**neutron** CLI supports this feature too.
|
||||
|
||||
To specify a filter in ``*-list`` command, you need to pass a pair of an
|
||||
attribute name and an expected value with the format of ``--<attribute> <value>``.
|
||||
The example below retrieves ports owned by compute instances.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ neutron port-list --device_owner network:dhcp
|
||||
+--------------------------------------+------+-------------------+-------------------------------------------------------------------------------------------------------------+
|
||||
| id | name | mac_address | fixed_ips |
|
||||
+--------------------------------------+------+-------------------+-------------------------------------------------------------------------------------------------------------+
|
||||
| 8953d683-29ad-4be3-b73f-060727c7849b | | fa:16:3e:4b:9e:0a | {"subnet_id": "6b832dfe-f271-443c-abad-629961414a73", "ip_address": "10.0.0.2"} |
|
||||
| | | | {"subnet_id": "cdcc616b-0cff-482f-96f5-06fc63d21247", "ip_address": "fd12:877c:1d66:0:f816:3eff:fe4b:9e0a"} |
|
||||
+--------------------------------------+------+-------------------+-------------------------------------------------------------------------------------------------------------+
|
||||
|
||||
You can also specify multiple filters.
|
||||
The example below retrieves security group rules applied to IPv4 traffic
|
||||
which belongs to a security group bfa493f9-2b03-46d2-8399-b9b038a53bc1.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ neutron security-group-rule-list --security-group-id bfa493f9-2b03-46d2-8399-b9b038a53bc1 --ethertype IPv4
|
||||
+--------------------------------------+----------------+-----------+-----------+---------------+-----------------+
|
||||
| id | security_group | direction | ethertype | protocol/port | remote |
|
||||
+--------------------------------------+----------------+-----------+-----------+---------------+-----------------+
|
||||
| 65489805-0400-4bce-9bd9-16a81952263c | default | egress | IPv4 | any | any |
|
||||
| 9429f336-4947-4643-bbd9-24528cc65648 | default | ingress | IPv4 | any | default (group) |
|
||||
+--------------------------------------+----------------+-----------+-----------+---------------+-----------------+
|
||||
|
||||
.. note::
|
||||
|
||||
Looking up UUID from name is not supported when specifying a filter.
|
||||
You need to use UUID to specify a specific resource.
|
||||
|
||||
.. note::
|
||||
|
||||
Filtering for dictionary or list attributes is not supported.
|
||||
|
||||
Changing displayed columns
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If you want displayed columns in a list operation, ``-c`` option can be used.
|
||||
``-c`` can be specified multiple times and the column order will be same as
|
||||
the order of ``-c`` options.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ neutron port-list -c id -c device_owner -c fixed_ips
|
||||
+--------------------------------------+--------------------------+-------------------------------------------------------------------------------------------------------------+
|
||||
| id | device_owner | fixed_ips |
|
||||
+--------------------------------------+--------------------------+-------------------------------------------------------------------------------------------------------------+
|
||||
| 41ca1b9b-4bbd-4aa8-bcaa-31d3d5704205 | network:router_interface | {"subnet_id": "6b832dfe-f271-443c-abad-629961414a73", "ip_address": "10.0.0.1"} |
|
||||
| 8953d683-29ad-4be3-b73f-060727c7849b | network:dhcp | {"subnet_id": "6b832dfe-f271-443c-abad-629961414a73", "ip_address": "10.0.0.2"} |
|
||||
| | | {"subnet_id": "cdcc616b-0cff-482f-96f5-06fc63d21247", "ip_address": "fd12:877c:1d66:0:f816:3eff:fe4b:9e0a"} |
|
||||
| a9da29f8-4504-4526-a5ce-cd3624fbd173 | neutron:LOADBALANCER | {"subnet_id": "6b832dfe-f271-443c-abad-629961414a73", "ip_address": "10.0.0.3"} |
|
||||
| | | {"subnet_id": "cdcc616b-0cff-482f-96f5-06fc63d21247", "ip_address": "fd12:877c:1d66:0:f816:3eff:feb1:ab71"} |
|
||||
| d6a1ff96-0a99-416f-a4d6-65d9614cf64e | compute:nova | {"subnet_id": "6b832dfe-f271-443c-abad-629961414a73", "ip_address": "10.0.0.4"} |
|
||||
| | | {"subnet_id": "cdcc616b-0cff-482f-96f5-06fc63d21247", "ip_address": "fd12:877c:1d66:0:f816:3eff:fe2c:348e"} |
|
||||
| f4789225-26d0-409f-8047-82d2c7a87a95 | network:router_interface | {"subnet_id": "cdcc616b-0cff-482f-96f5-06fc63d21247", "ip_address": "fd12:877c:1d66::1"} |
|
||||
+--------------------------------------+--------------------------+-------------------------------------------------------------------------------------------------------------+
|
||||
|
||||
.. _cli_extra_arguments:
|
||||
|
||||
Extra arguments for create/update operation
|
||||
-------------------------------------------
|
||||
|
||||
**neutron** CLI has a mechanism called the *extra arguments* for ``*-create``
|
||||
and ``*-update`` commands. It allows users to specify a set of *unknown
|
||||
options* which are not defined as options and not shown in the help text.
|
||||
**Unknown options MUST be placed at the end of the command line.**
|
||||
*unknown options* will be directly passed to the API layer. By this mechanism,
|
||||
you can pass an attribute which is not defined in the upstream **neutron**
|
||||
CLI. For example, when you are developing a new feature which add a new
|
||||
attribute to an existing resource, it is useful because we can test your
|
||||
feature without changing the existing neutron CLI.
|
||||
|
||||
For example, if you run the following command::
|
||||
|
||||
neutron resource-update <ID> --key1 value1 --key2 value2
|
||||
|
||||
where ``resource`` is some resource name and ``--key1`` and ``--key2`` are
|
||||
unknown options, then the following JSON will be sent to the neutron API::
|
||||
|
||||
PUT /v2.0/resources/<ID>
|
||||
|
||||
{
|
||||
"resource": {
|
||||
"key2": "value2",
|
||||
"key1": "value1"
|
||||
}
|
||||
}
|
||||
|
||||
Key interpretation
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This means an option name (``--key1`` in this case) must be one of valid
|
||||
resources of a corresponding resource. An option name ``--foo_bar`` is
|
||||
recognized as an attribute name ``foo_bar``. ``--foo-bar`` is also interpreted
|
||||
as an attribute name ``foo_bar``.
|
||||
|
||||
Value interpretation
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
By default, if the number of values is 1, the option value is interpreted as a
|
||||
string and is passed to the API layer as specified in a command-line.
|
||||
|
||||
If the number of values is greater than 1, the option value is interpreted as a
|
||||
list and the result in the API layer will be same as when specifying a list as
|
||||
described below.
|
||||
|
||||
neutron resource-update <ID> --key1 val1 val2 val3 --key2 val4
|
||||
|
||||
In the above example, a value of ``key1`` is interpreted as
|
||||
``["val1", "val2", "val3"]`` and a value of ``key2`` is interpreted
|
||||
as ``val4``.
|
||||
|
||||
The extra argument mechanism supports more complex value like a list or a dict.
|
||||
|
||||
Specify a list value
|
||||
++++++++++++++++++++
|
||||
|
||||
A command-line::
|
||||
|
||||
neutron resource-update <ID> --key list=true val1 val2 val3
|
||||
|
||||
will send the following in the API layer::
|
||||
|
||||
{
|
||||
"key": [
|
||||
"val1",
|
||||
"val2",
|
||||
"val3"
|
||||
]
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
||||
If you want to specify a list value, it is recommended to specify
|
||||
``list=true``. When ``list=true`` is specified, specified values are
|
||||
interpreted as a list even regardless of the number of values.
|
||||
|
||||
If ``list=true`` is not specified, specified values are interpreted
|
||||
depends on the number of values how. If the number of values is more than 2,
|
||||
the specified values are interpreted as a list. If 1, the value
|
||||
is interpreted as a string.
|
||||
|
||||
Specify a dict value
|
||||
++++++++++++++++++++
|
||||
|
||||
A command-line::
|
||||
|
||||
neutron resource-update <ID> --key type=dict key1=val1,key2=val2,key3=val3
|
||||
|
||||
will send the following in the API layer::
|
||||
|
||||
{
|
||||
"key": {
|
||||
"key1": "val1",
|
||||
"key2": "val2",
|
||||
"key3": "val3"
|
||||
}
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
||||
``type=bool True/False`` and ``type=int 10`` are also supported.
|
||||
|
||||
Specify a list of dicts
|
||||
+++++++++++++++++++++++
|
||||
|
||||
A command-line::
|
||||
|
||||
neutron resource-update <ID> --key type=dict list=true key1=val1 key2=val2 key3=val3
|
||||
|
||||
will send the following in the API layer::
|
||||
|
||||
{
|
||||
"key": [
|
||||
{"key1": "val1"},
|
||||
{"key2": "val2"},
|
||||
{"key3": "val3"}
|
||||
]
|
||||
}
|
||||
|
||||
Passing None as a value
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
There is a case where we would like to pass ``None`` (``null`` in JSON)
|
||||
in the API layer. To do this::
|
||||
|
||||
neutron resource-update <ID> --key action=clear
|
||||
|
||||
The following body will be in the API layer::
|
||||
|
||||
{"key": null}
|
||||
|
||||
.. note::
|
||||
|
||||
If ``action=clear`` is specified, ``list=true`` or ``type=dict`` is ignored.
|
||||
It means when ``action=clear`` is specified ``None`` is always sent.
|
||||
|
||||
Debugging
|
||||
---------
|
||||
|
||||
Display API-level communication
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
``-v`` (or ``--verbose``, ``--debug``) option displays a detail interaction
|
||||
with your neutron server. It is useful to debug what happens in the API level.
|
||||
|
||||
Here is an sample output of ``net-show`` command.
|
||||
|
||||
The first line show what parameters are recognized by neutronclient.
|
||||
It is sometimes useful to check if command-line parameters you specify are recognized properly.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ neutron -v net-show mynetwork
|
||||
DEBUG: neutronclient.neutron.v2_0.network.ShowNetwork get_data(Namespace(columns=[], fields=[], formatter='table', id=u'mynetwork', max_width=0, noindent=False, prefix='', request_format='json', show_details=False, variables=[]))
|
||||
|
||||
Next, neutronclient sends an authentication request to keystone to get a token
|
||||
which is used in further operations.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
DEBUG: keystoneauth.session REQ: curl -g -i -X GET http://172.16.18.47:5000 -H "Accept: application/json" -H "User-Agent: keystoneauth1"
|
||||
DEBUG: keystoneauth.session RESP: [300] Content-Length: 593 Vary: X-Auth-Token Keep-Alive: timeout=5, max=100 Server: Apache/2.4.7 (Ubuntu) Connection: Keep-Alive Date: Fri, 27 Nov 2015 20:10:54 GMT Content-Type: application/json
|
||||
RESP BODY: {"versions": {"values": [{"status": "stable", "updated": "2015-03-30T00:00:00Z", "media-types": [{"base": "application/json", "type": "application/vnd.openstack.identity-v3+json"}], "id": "v3.4", "links": [{"href": "http://172.16.18.47:5000/v3/", "rel": "self"}]}, {"status": "stable", "updated": "2014-04-17T00:00:00Z", "media-types": [{"base": "application/json", "type": "application/vnd.openstack.identity-v2.0+json"}], "id": "v2.0", "links": [{"href": "http://172.16.18.47:5000/v2.0/", "rel": "self"}, {"href": "http://docs.openstack.org/", "type": "text/html", "rel": "describedby"}]}]}}
|
||||
|
||||
DEBUG: keystoneauth.identity.v3.base Making authentication request to http://172.16.18.47:5000/v3/auth/tokens
|
||||
|
||||
Neutronclient looks up a network ID corresponding to a given network name.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
DEBUG: keystoneauth.session REQ: curl -g -i -X GET http://172.16.18.47:9696/v2.0/networks.json?fields=id&name=mynetwork -H "User-Agent: python-neutronclient" -H "Accept: application/json" -H "X-Auth-Token: {SHA1}39300e7398d53a02afd183f13cb6afaef95ec4e5"
|
||||
DEBUG: keystoneauth.session RESP: [200] Date: Fri, 27 Nov 2015 20:10:55 GMT Connection: keep-alive Content-Type: application/json; charset=UTF-8 Content-Length: 62 X-Openstack-Request-Id: req-ccebf6e4-4f52-4874-a1ab-5499abcba378
|
||||
RESP BODY: {"networks": [{"id": "3698d3c7-d581-443e-bf86-53c4e3a738f7"}]}
|
||||
|
||||
Finally, neutronclient retrieves a detail of a given network using the resolved ID.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
DEBUG: keystoneauth.session REQ: curl -g -i -X GET http://172.16.18.47:9696/v2.0/networks/3698d3c7-d581-443e-bf86-53c4e3a738f7.json -H "User-Agent: python-neutronclient" -H "Accept: application/json" -H "X-Auth-Token: {SHA1}39300e7398d53a02afd183f13cb6afaef95ec4e5"
|
||||
DEBUG: keystoneauth.session RESP: [200] Date: Fri, 27 Nov 2015 20:10:55 GMT Connection: keep-alive Content-Type: application/json; charset=UTF-8 Content-Length: 272 X-Openstack-Request-Id: req-261add00-d6d3-4ea7-becc-105b60ac7369
|
||||
RESP BODY: {"network": {"status": "ACTIVE", "subnets": [], "name": "mynetwork", "admin_state_up": true, "tenant_id": "8f0ebf767043483a987736c8c684178d", "mtu": 0, "router:external": false, "shared": false, "port_security_enabled": true, "id": "3698d3c7-d581-443e-bf86-53c4e3a738f7"}}
|
||||
|
||||
+-----------------------+--------------------------------------+
|
||||
| Field | Value |
|
||||
+-----------------------+--------------------------------------+
|
||||
| admin_state_up | True |
|
||||
| id | 3698d3c7-d581-443e-bf86-53c4e3a738f7 |
|
||||
| mtu | 0 |
|
||||
| name | mynetwork |
|
||||
| port_security_enabled | True |
|
||||
| router:external | False |
|
||||
| shared | False |
|
||||
| status | ACTIVE |
|
||||
| subnets | |
|
||||
| tenant_id | 8f0ebf767043483a987736c8c684178d |
|
||||
+-----------------------+--------------------------------------+
|
|
@ -1,71 +0,0 @@
|
|||
..
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
not use this file except in compliance with the License. You may obtain
|
||||
a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
|
||||
Convention for heading levels in Neutron devref:
|
||||
======= Heading 0 (reserved for the title in a document)
|
||||
------- Heading 1
|
||||
~~~~~~~ Heading 2
|
||||
+++++++ Heading 3
|
||||
''''''' Heading 4
|
||||
(Avoid deeper levels because they do not render well.)
|
||||
|
||||
neutronclient Python API
|
||||
========================
|
||||
|
||||
Basic Usage
|
||||
-----------
|
||||
|
||||
First create a client instance.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> from neutronclient.v2_0 import client
|
||||
>>> username='adminUser'
|
||||
>>> password='secretword'
|
||||
>>> project_name='openstackDemo'
|
||||
>>> auth_url='http://192.168.206.130:5000/v2.0'
|
||||
>>> neutron = client.Client(username=username,
|
||||
... password=password,
|
||||
... project_name=project_name,
|
||||
... auth_url=auth_url)
|
||||
|
||||
Now you can call various methods on the client instance.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> network = {'name': 'mynetwork', 'admin_state_up': True}
|
||||
>>> neutron.create_network({'network':network})
|
||||
>>> networks = neutron.list_networks(name='mynetwork')
|
||||
>>> print networks
|
||||
>>> network_id = networks['networks'][0]['id']
|
||||
>>> neutron.delete_network(network_id)
|
||||
|
||||
Alternatively, you can create a client instance using an auth token
|
||||
and a service endpoint URL directly.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> from neutronclient.v2_0 import client
|
||||
>>> neutron = client.Client(endpoint_url='http://192.168.206.130:9696/',
|
||||
... token='d3f9226f27774f338019aa2611112ef6')
|
||||
|
||||
You can get ``X-Openstack-Request-Id`` as ``request_ids`` from the result.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> network = {'name': 'mynetwork', 'admin_state_up': True}
|
||||
>>> neutron.create_network({'network':network})
|
||||
>>> networks = neutron.list_networks(name='mynetwork')
|
||||
>>> print networks.request_ids
|
||||
['req-978a0160-7ab0-44f0-8a93-08e9a4e785fa']
|
|
@ -1,171 +0,0 @@
|
|||
=============
|
||||
network trunk
|
||||
=============
|
||||
|
||||
A **network trunk** is a container to group logical ports from different
|
||||
networks and provide a single trunked vNIC for servers. It consists of
|
||||
one parent port which is a regular VIF and multiple subports which allow
|
||||
the server to connect to more networks.
|
||||
|
||||
Network v2
|
||||
|
||||
network subport list
|
||||
--------------------
|
||||
|
||||
List all subports for a given network trunk
|
||||
|
||||
.. program:: network subport list
|
||||
.. code:: bash
|
||||
|
||||
os network subport list
|
||||
--trunk <trunk>
|
||||
|
||||
.. option:: --trunk <trunk>
|
||||
|
||||
List subports belonging to this trunk (name or ID) (required)
|
||||
|
||||
network trunk create
|
||||
--------------------
|
||||
|
||||
Create a network trunk for a given project
|
||||
|
||||
.. program:: network trunk create
|
||||
.. code:: bash
|
||||
|
||||
os network trunk create
|
||||
--parent-port <parent-port>
|
||||
[--subport <port=,segmentation-type=,segmentation-id=>]
|
||||
[--enable | --disable]
|
||||
[--project <project> [--project-domain <project-domain>]]
|
||||
<name>
|
||||
|
||||
.. option:: --parent-port <parent-port>
|
||||
|
||||
Parent port belonging to this trunk (name or ID) (required)
|
||||
|
||||
.. option:: --subport <port=,segmentation-type=,segmentation-id=>
|
||||
|
||||
Subport to add. Subport is of form 'port=<name or ID>,segmentation-type=,segmentation-ID='
|
||||
(--subport) option can be repeated
|
||||
|
||||
.. option:: --enable
|
||||
|
||||
Enable trunk (default)
|
||||
|
||||
.. option:: --disable
|
||||
|
||||
Disable trunk
|
||||
|
||||
.. option:: --project <project>
|
||||
|
||||
Owner's project (name or ID)
|
||||
|
||||
.. option:: --project-domain <project-domain>
|
||||
|
||||
Domain the project belongs to (name or ID).
|
||||
This can be used in case collisions between project names exist.
|
||||
|
||||
network trunk delete
|
||||
--------------------
|
||||
|
||||
Delete a given network trunk
|
||||
|
||||
.. program:: network trunk delete
|
||||
.. code:: bash
|
||||
|
||||
os network trunk delete
|
||||
<trunk> [<trunk> ...]
|
||||
|
||||
.. _network_trunk_delete-trunk:
|
||||
.. describe:: <trunk>
|
||||
|
||||
Trunk(s) to delete (name or ID)
|
||||
|
||||
network trunk list
|
||||
------------------
|
||||
|
||||
List all network trunks
|
||||
|
||||
.. program:: network trunk list
|
||||
.. code:: bash
|
||||
|
||||
os network trunk list
|
||||
[--long]
|
||||
|
||||
.. option:: --long
|
||||
|
||||
List additional fields in output
|
||||
|
||||
network trunk set
|
||||
-----------------
|
||||
|
||||
Set network trunk properties
|
||||
|
||||
.. program:: network trunk set
|
||||
.. code:: bash
|
||||
|
||||
os network trunk set
|
||||
[--name <name>]
|
||||
[--subport <port=,segmentation-type=,segmentation-id=>]
|
||||
[--enable | --disable]
|
||||
<trunk>
|
||||
|
||||
.. option:: --name <name>
|
||||
|
||||
Set trunk name
|
||||
|
||||
.. option:: --subport <port=,segmentation-type=,segmentation-id=>
|
||||
|
||||
Subport to add. Subport is of form 'port=<name or ID>,segmentation-type=,segmentation-ID='
|
||||
(--subport) option can be repeated
|
||||
|
||||
.. option:: --enable
|
||||
|
||||
Enable trunk
|
||||
|
||||
.. option:: --disable
|
||||
|
||||
Disable trunk
|
||||
|
||||
.. _network_trunk_set-trunk:
|
||||
.. describe:: <trunk>
|
||||
|
||||
Trunk to modify (name or ID)
|
||||
|
||||
network trunk show
|
||||
------------------
|
||||
|
||||
Show information of a given network trunk
|
||||
|
||||
.. program:: network trunk show
|
||||
.. code:: bash
|
||||
|
||||
os network trunk show
|
||||
<trunk>
|
||||
|
||||
.. _network_trunk_show-trunk:
|
||||
.. describe:: <trunk>
|
||||
|
||||
Trunk to display (name or ID)
|
||||
|
||||
network trunk unset
|
||||
-------------------
|
||||
|
||||
Unset subports from a given network trunk
|
||||
|
||||
.. program:: network trunk unset
|
||||
.. code:: bash
|
||||
|
||||
os network trunk unset
|
||||
--subport <subport>
|
||||
<trunk>
|
||||
|
||||
.. option:: --subport <subport>
|
||||
|
||||
Subport to delete (name or ID of the port) (required)
|
||||
(--subport) option can be repeated
|
||||
|
||||
.. _network_trunk_unset-trunk:
|
||||
.. describe:: <trunk>
|
||||
|
||||
Unset subports from this trunk (name or ID)
|
|
@ -1,33 +0,0 @@
|
|||
..
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
not use this file except in compliance with the License. You may obtain
|
||||
a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
|
||||
Convention for heading levels in Neutron devref:
|
||||
======= Heading 0 (reserved for the title in a document)
|
||||
------- Heading 1
|
||||
~~~~~~~ Heading 2
|
||||
+++++++ Heading 3
|
||||
''''''' Heading 4
|
||||
(Avoid deeper levels because they do not render well.)
|
||||
|
||||
Using Network CLI extensions to OpenStack Client
|
||||
================================================
|
||||
|
||||
List of released CLI commands available in openstack client. These commands
|
||||
can be referenced by doing ``openstack help network``.
|
||||
|
||||
.. toctree::
|
||||
:glob:
|
||||
:maxdepth: 2
|
||||
|
||||
osc/v2/*
|
165
neutron_test.sh
165
neutron_test.sh
|
@ -1,165 +0,0 @@
|
|||
#!/bin/bash
|
||||
set -x
|
||||
function die() {
|
||||
local exitcode=$?
|
||||
set +o xtrace
|
||||
echo $@
|
||||
cleanup
|
||||
exit $exitcode
|
||||
}
|
||||
|
||||
net_name=mynet1
|
||||
subnet_name=mysubnet1
|
||||
port_name=myport1
|
||||
function cleanup() {
|
||||
echo Removing test port, subnet and net...
|
||||
neutron port-delete $port_name
|
||||
neutron subnet-delete $subnet_name
|
||||
neutron net-delete $net_name
|
||||
}
|
||||
|
||||
noauth_tenant_id=me
|
||||
if [ "$1" == "noauth" ]; then
|
||||
NOAUTH="--tenant_id $noauth_tenant_id"
|
||||
else
|
||||
NOAUTH=
|
||||
fi
|
||||
|
||||
echo "NOTE: User should be admin in order to perform all operations."
|
||||
sleep 3
|
||||
|
||||
# test the CRUD of network
|
||||
network=$net_name
|
||||
neutron net-create $NOAUTH $network || die "fail to create network $network"
|
||||
temp=`neutron net-list -- --name $network --fields id | wc -l`
|
||||
echo $temp
|
||||
if [ $temp -ne 5 ]; then
|
||||
die "networks with name $network is not unique or found"
|
||||
fi
|
||||
network_id=`neutron net-list -- --name $network --fields id | tail -n 2 | head -n 1 | cut -d' ' -f 2`
|
||||
echo "ID of network with name $network is $network_id"
|
||||
|
||||
neutron net-show $network || die "fail to show network $network"
|
||||
neutron net-show $network_id || die "fail to show network $network_id"
|
||||
|
||||
neutron net-update $network --admin_state_up False || die "fail to update network $network"
|
||||
neutron net-update $network_id --admin_state_up True || die "fail to update network $network_id"
|
||||
|
||||
neutron net-list -c id -- --id fakeid || die "fail to list networks with column selection on empty list"
|
||||
|
||||
# test the CRUD of subnet
|
||||
subnet=$subnet_name
|
||||
cidr=10.0.1.0/24
|
||||
neutron subnet-create $NOAUTH $network $cidr --name $subnet || die "fail to create subnet $subnet"
|
||||
tempsubnet=`neutron subnet-list -- --name $subnet --fields id | wc -l`
|
||||
echo $tempsubnet
|
||||
if [ $tempsubnet -ne 5 ]; then
|
||||
die "subnets with name $subnet is not unique or found"
|
||||
fi
|
||||
subnet_id=`neutron subnet-list -- --name $subnet --fields id | tail -n 2 | head -n 1 | cut -d' ' -f 2`
|
||||
echo "ID of subnet with name $subnet is $subnet_id"
|
||||
neutron subnet-show $subnet || die "fail to show subnet $subnet"
|
||||
neutron subnet-show $subnet_id || die "fail to show subnet $subnet_id"
|
||||
|
||||
neutron subnet-update $subnet --dns_nameservers list=true 1.1.1.11 1.1.1.12 || die "fail to update subnet $subnet"
|
||||
neutron subnet-update $subnet_id --dns_nameservers list=true 2.2.2.21 2.2.2.22 || die "fail to update subnet $subnet_id"
|
||||
|
||||
# test the crud of ports
|
||||
port=$port_name
|
||||
neutron port-create $NOAUTH $network --name $port || die "fail to create port $port"
|
||||
tempport=`neutron port-list -- --name $port --fields id | wc -l`
|
||||
echo $tempport
|
||||
if [ $tempport -ne 5 ]; then
|
||||
die "ports with name $port is not unique or found"
|
||||
fi
|
||||
port_id=`neutron port-list -- --name $port --fields id | tail -n 2 | head -n 1 | cut -d' ' -f 2`
|
||||
echo "ID of port with name $port is $port_id"
|
||||
neutron port-show $port || die "fail to show port $port"
|
||||
neutron port-show $port_id || die "fail to show port $port_id"
|
||||
neutron port-update $port --device_id deviceid1 || die "fail to update port $port"
|
||||
neutron port-update $port_id --device_id deviceid2 || die "fail to update port $port_id"
|
||||
neutron port-update $port_id --allowed-address-pair ip_address=1.1.1.11,mac_address=10:00:00:00:00:00 --allowed-address-pair ip_address=1.1.1.12,mac_address=10:00:00:00:00:01 || die "fail to update port $port_id --allowed-address-pair"
|
||||
neutron port-show $port || die "fail to show port $port"
|
||||
neutron port-show $port_id || die "fail to show port $port_id"
|
||||
neutron port-update $port_id --no-allowed-address-pairs || die "fail to update port $port_id --no-allowed-address-pairs"
|
||||
neutron port-show $port || die "fail to show port $port"
|
||||
neutron port-show $port_id || die "fail to show port $port_id"
|
||||
neutron port-delete $port_id
|
||||
|
||||
# test the create port with allowed-address-pairs
|
||||
port=$port_name
|
||||
neutron port-create $NOAUTH $network --name $port -- --allowed-address-pairs type=dict list=true ip_address=1.1.1.11,mac_address=10:00:00:00:00:00 ip_address=1.1.1.12,mac_address=10:00:00:00:00:01 || die "fail to create port $port"
|
||||
tempport=`neutron port-list -- --name $port --fields id | wc -l`
|
||||
echo $tempport
|
||||
if [ $tempport -ne 5 ]; then
|
||||
die "ports with name $port is not unique or found"
|
||||
fi
|
||||
port_id=`neutron port-list -- --name $port --fields id | tail -n 2 | head -n 1 | cut -d' ' -f 2`
|
||||
echo "ID of port with name $port is $port_id"
|
||||
neutron port-show $port || die "fail to show port $port"
|
||||
neutron port-show $port_id || die "fail to show port $port_id"
|
||||
neutron port-update $port_id --no-allowed-address-pairs || die "fail to update port $port_id --no-allowed-address-pairs"
|
||||
neutron port-show $port_id
|
||||
|
||||
# test quota commands RUD
|
||||
DEFAULT_NETWORKS=10
|
||||
DEFAULT_PORTS=50
|
||||
tenant_id=tenant_a
|
||||
tenant_id_b=tenant_b
|
||||
neutron quota-update --tenant_id $tenant_id --network 30 || die "fail to update quota for tenant $tenant_id"
|
||||
neutron quota-update --tenant_id $tenant_id_b --network 20 || die "fail to update quota for tenant $tenant_id"
|
||||
networks=`neutron quota-list -c network -c tenant_id | grep $tenant_id | awk '{print $2}'`
|
||||
if [ $networks -ne 30 ]; then
|
||||
die "networks quota should be 30"
|
||||
fi
|
||||
networks=`neutron quota-list -c network -c tenant_id | grep $tenant_id_b | awk '{print $2}'`
|
||||
if [ $networks -ne 20 ]; then
|
||||
die "networks quota should be 20"
|
||||
fi
|
||||
networks=`neutron quota-show --tenant_id $tenant_id | grep network | awk -F'|' '{print $3}'`
|
||||
if [ $networks -ne 30 ]; then
|
||||
die "networks quota should be 30"
|
||||
fi
|
||||
neutron quota-delete --tenant_id $tenant_id || die "fail to delete quota for tenant $tenant_id"
|
||||
networks=`neutron quota-show --tenant_id $tenant_id | grep network | awk -F'|' '{print $3}'`
|
||||
if [ $networks -ne $DEFAULT_NETWORKS ]; then
|
||||
die "networks quota should be $DEFAULT_NETWORKS"
|
||||
fi
|
||||
# update self
|
||||
if [ "t$NOAUTH" = "t" ]; then
|
||||
# with auth
|
||||
neutron quota-update --port 99 || die "fail to update quota for self"
|
||||
ports=`neutron quota-show | grep port | awk -F'|' '{print $3}'`
|
||||
if [ $ports -ne 99 ]; then
|
||||
die "ports quota should be 99"
|
||||
fi
|
||||
|
||||
ports=`neutron quota-list -c port | grep 99 | awk '{print $2}'`
|
||||
if [ $ports -ne 99 ]; then
|
||||
die "ports quota should be 99"
|
||||
fi
|
||||
neutron quota-delete || die "fail to delete quota for tenant self"
|
||||
ports=`neutron quota-show | grep port | awk -F'|' '{print $3}'`
|
||||
if [ $ports -ne $DEFAULT_PORTS ]; then
|
||||
die "ports quota should be $DEFAULT_PORTS"
|
||||
fi
|
||||
else
|
||||
# without auth
|
||||
neutron quota-update --port 100
|
||||
if [ $? -eq 0 ]; then
|
||||
die "without valid context on server, quota update command should fail."
|
||||
fi
|
||||
neutron quota-show
|
||||
if [ $? -eq 0 ]; then
|
||||
die "without valid context on server, quota show command should fail."
|
||||
fi
|
||||
neutron quota-delete
|
||||
if [ $? -eq 0 ]; then
|
||||
die "without valid context on server, quota delete command should fail."
|
||||
fi
|
||||
neutron quota-list || die "fail to update quota for self"
|
||||
fi
|
||||
|
||||
cleanup
|
||||
echo "Success! :)"
|
||||
|
|
@ -1,41 +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 oslo_i18n
|
||||
|
||||
|
||||
DOMAIN = 'neutronclient'
|
||||
|
||||
_translators = oslo_i18n.TranslatorFactory(domain=DOMAIN)
|
||||
|
||||
# The primary translation function using the well-known name "_"
|
||||
_ = _translators.primary
|
||||
|
||||
# The contextual translation function using the name "_C"
|
||||
_C = _translators.contextual_form
|
||||
|
||||
# The plural translation function using the name "_P"
|
||||
_P = _translators.plural_form
|
||||
|
||||
# Translators for log levels.
|
||||
#
|
||||
# The abbreviated names are meant to reflect the usual use of a short
|
||||
# name like '_'. The "L" is for "log" and the other letter comes from
|
||||
# the level.
|
||||
_LI = _translators.log_info
|
||||
_LW = _translators.log_warning
|
||||
_LE = _translators.log_error
|
||||
_LC = _translators.log_critical
|
||||
|
||||
|
||||
def get_available_languages():
|
||||
return oslo_i18n.get_available_languages(DOMAIN)
|
|
@ -1,400 +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.
|
||||
#
|
||||
|
||||
try:
|
||||
import json
|
||||
except ImportError:
|
||||
import simplejson as json
|
||||
import logging
|
||||
import os
|
||||
|
||||
import debtcollector.renames
|
||||
from keystoneauth1 import access
|
||||
from keystoneauth1 import adapter
|
||||
import requests
|
||||
|
||||
from neutronclient._i18n import _
|
||||
from neutronclient.common import exceptions
|
||||
from neutronclient.common import utils
|
||||
|
||||
_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
|
||||
|
||||
logging.getLogger("requests").setLevel(_requests_log_level)
|
||||
MAX_URI_LEN = 8192
|
||||
USER_AGENT = 'python-neutronclient'
|
||||
|
||||
|
||||
class HTTPClient(object):
|
||||
"""Handles the REST calls and responses, include authn."""
|
||||
|
||||
CONTENT_TYPE = 'application/json'
|
||||
|
||||
@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):
|
||||
|
||||
self.username = username
|
||||
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.endpoint_url = endpoint_url
|
||||
self.auth_strategy = auth_strategy
|
||||
self.log_credentials = log_credentials
|
||||
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'] = USER_AGENT
|
||||
|
||||
if 'body' in kwargs:
|
||||
kargs['body'] = kwargs['body']
|
||||
|
||||
if self.log_credentials:
|
||||
log_kargs = kargs
|
||||
else:
|
||||
log_kargs = self._strip_credentials(kargs)
|
||||
|
||||
utils.http_log_req(_logger, args, log_kargs)
|
||||
try:
|
||||
resp, body = self.request(*args, **kargs)
|
||||
except requests.exceptions.SSLError as e:
|
||||
raise exceptions.SslCertificateValidationError(reason=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)
|
||||
utils.http_log_resp(_logger, resp, body)
|
||||
if resp.status_code == 401:
|
||||
raise exceptions.Unauthorized(message=body)
|
||||
return resp, body
|
||||
|
||||
def _strip_credentials(self, kwargs):
|
||||
if kwargs.get('body') and self.password:
|
||||
log_kwargs = kwargs.copy()
|
||||
log_kwargs['body'] = kwargs['body'].replace(self.password,
|
||||
'REDACTED')
|
||||
return log_kwargs
|
||||
else:
|
||||
return kwargs
|
||||
|
||||
def authenticate_and_fetch_endpoint_url(self):
|
||||
if not self.auth_token:
|
||||
self.authenticate()
|
||||
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)
|
||||
|
||||
headers['User-Agent'] = USER_AGENT
|
||||
|
||||
resp = requests.request(
|
||||
method,
|
||||
url,
|
||||
data=body,
|
||||
headers=headers,
|
||||
verify=self.verify_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', {})
|
||||
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']['X-Auth-Token'] = self.auth_token
|
||||
resp, body = self._cs_request(
|
||||
self.endpoint_url + url, method, **kwargs)
|
||||
return resp, body
|
||||
|
||||
def _extract_service_catalog(self, body):
|
||||
"""Set the client's service catalog from the response data."""
|
||||
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(
|
||||
region_name=self.region_name,
|
||||
service_type=self.service_type,
|
||||
interface=self.endpoint_type)
|
||||
|
||||
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': creds,
|
||||
'tenantName': self.project_name, }, }
|
||||
|
||||
if self.auth_url is None:
|
||||
raise exceptions.NoAuthURLProvided()
|
||||
|
||||
token_url = self.auth_url + "/tokens"
|
||||
resp, resp_body = self._cs_request(token_url, "POST",
|
||||
body=json.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)
|
||||
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()
|
||||
|
||||
url = self.auth_url + '/tokens/%s/endpoints' % self.auth_token
|
||||
try:
|
||||
resp, body = self._cs_request(url, "GET")
|
||||
except exceptions.Unauthorized:
|
||||
# rollback to authenticate() to handle case when neutron client
|
||||
# is initialized just before the token is expired
|
||||
self.authenticate()
|
||||
return self.endpoint_url
|
||||
|
||||
body = json.loads(body)
|
||||
for endpoint in body.get('endpoints', []):
|
||||
if (endpoint['type'] == 'network' and
|
||||
endpoint.get('region') == self.region_name):
|
||||
if self.endpoint_type not in endpoint:
|
||||
raise exceptions.EndpointTypeNotFound(
|
||||
type_=self.endpoint_type)
|
||||
return endpoint[self.endpoint_type]
|
||||
|
||||
raise exceptions.EndpointNotFound()
|
||||
|
||||
def get_auth_info(self):
|
||||
return {'auth_token': self.auth_token,
|
||||
'auth_tenant_id': self.auth_tenant_id,
|
||||
'auth_user_id': self.auth_user_id,
|
||||
'endpoint_url': self.endpoint_url}
|
||||
|
||||
|
||||
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.setdefault('headers', {})
|
||||
headers.setdefault('Accept', content_type)
|
||||
|
||||
try:
|
||||
kwargs.setdefault('data', kwargs.pop('body'))
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
if kwargs.get('data'):
|
||||
headers.setdefault('Content-Type', content_type)
|
||||
|
||||
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()
|
||||
|
||||
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:
|
||||
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
|
||||
|
||||
|
||||
# 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,
|
||||
service_type='network',
|
||||
session=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,
|
||||
**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,
|
||||
log_credentials=log_credentials,
|
||||
auth_strategy=auth_strategy)
|
|
@ -1,125 +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.
|
||||
#
|
||||
|
||||
"""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."""
|
||||
|
||||
def __init__(self, factory):
|
||||
self.factory = factory
|
||||
self._handle = None
|
||||
|
||||
def __get__(self, instance, owner):
|
||||
# Tell the ClientManager to login to keystone
|
||||
if self._handle is None:
|
||||
self._handle = self.factory(instance)
|
||||
return self._handle
|
||||
|
||||
|
||||
class ClientManager(object):
|
||||
"""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,
|
||||
project_name=None,
|
||||
project_id=None,
|
||||
username=None,
|
||||
user_id=None,
|
||||
password=None,
|
||||
region_name=None,
|
||||
api_version=None,
|
||||
auth_strategy=None,
|
||||
insecure=False,
|
||||
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._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
|
||||
self._service_catalog = None
|
||||
self._auth_strategy = auth_strategy
|
||||
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.construct_http_client(
|
||||
username=self._username,
|
||||
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
|
||||
self._token = httpclient.auth_token
|
||||
self._url = httpclient.endpoint_url
|
|
@ -1,32 +0,0 @@
|
|||
# Copyright (c) 2012 OpenStack Foundation.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
TYPE_BOOL = "bool"
|
||||
TYPE_INT = "int"
|
||||
TYPE_LONG = "long"
|
||||
TYPE_FLOAT = "float"
|
||||
TYPE_LIST = "list"
|
||||
TYPE_DICT = "dict"
|
||||
|
||||
PLURALS = {'networks': 'network',
|
||||
'ports': 'port',
|
||||
'subnets': 'subnet',
|
||||
'subnetpools': 'subnetpool',
|
||||
'dns_nameservers': 'dns_nameserver',
|
||||
'host_routes': 'host_route',
|
||||
'allocation_pools': 'allocation_pool',
|
||||
'fixed_ips': 'fixed_ip',
|
||||
'extensions': 'extension'}
|
|
@ -1,246 +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.
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
|
||||
class NeutronException(Exception):
|
||||
"""Base Neutron Exception.
|
||||
|
||||
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, message=None, **kwargs):
|
||||
if message:
|
||||
self.message = message
|
||||
try:
|
||||
self._error_string = self.message % kwargs
|
||||
except Exception:
|
||||
# at least get the core message out if something happened
|
||||
self._error_string = self.message
|
||||
|
||||
def __str__(self):
|
||||
return self._error_string
|
||||
|
||||
|
||||
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 <Neutron Exception Name> + 'Client'
|
||||
# e.g., NetworkNotFound -> NetworkNotFoundClient
|
||||
|
||||
class NetworkNotFoundClient(NotFound):
|
||||
pass
|
||||
|
||||
|
||||
class PortNotFoundClient(NotFound):
|
||||
pass
|
||||
|
||||
|
||||
class StateInvalidClient(BadRequest):
|
||||
pass
|
||||
|
||||
|
||||
class NetworkInUseClient(Conflict):
|
||||
pass
|
||||
|
||||
|
||||
class PortInUseClient(Conflict):
|
||||
pass
|
||||
|
||||
|
||||
class IpAddressInUseClient(Conflict):
|
||||
pass
|
||||
|
||||
|
||||
class InvalidIpForNetworkClient(BadRequest):
|
||||
pass
|
||||
|
||||
|
||||
class InvalidIpForSubnetClient(BadRequest):
|
||||
pass
|
||||
|
||||
|
||||
class OverQuotaClient(Conflict):
|
||||
pass
|
||||
|
||||
|
||||
class IpAddressGenerationFailureClient(Conflict):
|
||||
pass
|
||||
|
||||
|
||||
class MacAddressInUseClient(Conflict):
|
||||
pass
|
||||
|
||||
|
||||
class HostNotCompatibleWithFixedIpsClient(Conflict):
|
||||
pass
|
||||
|
||||
|
||||
class ExternalIpAddressExhaustedClient(BadRequest):
|
||||
pass
|
||||
|
||||
|
||||
# Exceptions from client library
|
||||
|
||||
class NoAuthURLProvided(Unauthorized):
|
||||
message = _("auth_url was not provided to the Neutron client")
|
||||
|
||||
|
||||
class EndpointNotFound(NeutronClientException):
|
||||
message = _("Could not find Service or Region in Service Catalog.")
|
||||
|
||||
|
||||
class EndpointTypeNotFound(NeutronClientException):
|
||||
message = _("Could not find endpoint type %(type_)s in Service Catalog.")
|
||||
|
||||
|
||||
class AmbiguousEndpoints(NeutronClientException):
|
||||
message = _("Found more than one matching endpoint in Service Catalog: "
|
||||
"%(matching_endpoints)")
|
||||
|
||||
|
||||
class RequestURITooLong(NeutronClientException):
|
||||
"""Raised when a request fails with HTTP error 414."""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.excess = kwargs.get('excess', 0)
|
||||
super(RequestURITooLong, self).__init__(**kwargs)
|
||||
|
||||
|
||||
class ConnectionFailed(NeutronClientException):
|
||||
message = _("Connection to neutron failed: %(reason)s")
|
||||
|
||||
|
||||
class SslCertificateValidationError(NeutronClientException):
|
||||
message = _("SSL certificate validation has failed: %(reason)s")
|
||||
|
||||
|
||||
class MalformedResponseBody(NeutronClientException):
|
||||
message = _("Malformed response body: %(reason)s")
|
||||
|
||||
|
||||
class InvalidContentType(NeutronClientException):
|
||||
message = _("Invalid content type %(content_type)s.")
|
||||
|
||||
|
||||
# Command line exceptions
|
||||
|
||||
class NeutronCLIError(NeutronException):
|
||||
"""Exception raised when command line parsing fails."""
|
||||
pass
|
||||
|
||||
|
||||
class CommandError(NeutronCLIError):
|
||||
pass
|
||||
|
||||
|
||||
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(NeutronCLIError):
|
||||
message = _("Multiple %(resource)s matches found for name '%(name)s',"
|
||||
" use an ID to be more specific.")
|
|
@ -1,86 +0,0 @@
|
|||
# Copyright 2015 Rackspace Hosting Inc.
|
||||
# All Rights Reserved
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
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)
|
|
@ -1,128 +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.
|
||||
|
||||
import logging
|
||||
|
||||
from oslo_serialization import jsonutils
|
||||
import six
|
||||
|
||||
from neutronclient._i18n import _
|
||||
from neutronclient.common import exceptions as exception
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
if six.PY3:
|
||||
long = int
|
||||
|
||||
|
||||
class ActionDispatcher(object):
|
||||
"""Maps method name to local methods through action name."""
|
||||
|
||||
def dispatch(self, *args, **kwargs):
|
||||
"""Find and call local method."""
|
||||
action = kwargs.pop('action', 'default')
|
||||
action_method = getattr(self, str(action), self.default)
|
||||
return action_method(*args, **kwargs)
|
||||
|
||||
def default(self, data):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class DictSerializer(ActionDispatcher):
|
||||
"""Default request body serialization."""
|
||||
|
||||
def serialize(self, data, action='default'):
|
||||
return self.dispatch(data, action=action)
|
||||
|
||||
def default(self, data):
|
||||
return ""
|
||||
|
||||
|
||||
class JSONDictSerializer(DictSerializer):
|
||||
"""Default JSON request body serialization."""
|
||||
|
||||
def default(self, data):
|
||||
def sanitizer(obj):
|
||||
return six.text_type(obj)
|
||||
return jsonutils.dumps(data, default=sanitizer)
|
||||
|
||||
|
||||
class TextDeserializer(ActionDispatcher):
|
||||
"""Default request body deserialization."""
|
||||
|
||||
def deserialize(self, datastring, action='default'):
|
||||
return self.dispatch(datastring, action=action)
|
||||
|
||||
def default(self, datastring):
|
||||
return {}
|
||||
|
||||
|
||||
class JSONDeserializer(TextDeserializer):
|
||||
|
||||
def _from_json(self, datastring):
|
||||
try:
|
||||
return jsonutils.loads(datastring)
|
||||
except ValueError:
|
||||
msg = _("Cannot understand JSON")
|
||||
raise exception.MalformedResponseBody(reason=msg)
|
||||
|
||||
def default(self, datastring):
|
||||
return {'body': self._from_json(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):
|
||||
"""Create a serializer based on the given WSGI environment.
|
||||
|
||||
'metadata' is an optional dict mapping MIME types to information
|
||||
needed to serialize a dictionary to that type.
|
||||
|
||||
"""
|
||||
self.metadata = metadata or {}
|
||||
|
||||
def _get_serialize_handler(self, content_type):
|
||||
handlers = {
|
||||
'application/json': JSONDictSerializer(),
|
||||
}
|
||||
|
||||
try:
|
||||
return handlers[content_type]
|
||||
except Exception:
|
||||
raise exception.InvalidContentType(content_type=content_type)
|
||||
|
||||
def serialize(self, data):
|
||||
"""Serialize a dictionary into the specified content type."""
|
||||
return self._get_serialize_handler("application/json").serialize(data)
|
||||
|
||||
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("application/json").deserialize(
|
||||
datastring)
|
||||
|
||||
def get_deserialize_handler(self, content_type):
|
||||
handlers = {
|
||||
'application/json': JSONDeserializer(),
|
||||
}
|
||||
|
||||
try:
|
||||
return handlers[content_type]
|
||||
except Exception:
|
||||
raise exception.InvalidContentType(content_type=content_type)
|
|
@ -1,236 +0,0 @@
|
|||
# Copyright 2011, VMware, 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.
|
||||
#
|
||||
# Borrowed from nova code base, more utilities will be added/borrowed as and
|
||||
# when needed.
|
||||
|
||||
"""Utilities and helper functions."""
|
||||
|
||||
import argparse
|
||||
import functools
|
||||
import hashlib
|
||||
import logging
|
||||
import netaddr
|
||||
import os
|
||||
|
||||
from oslo_utils import encodeutils
|
||||
from oslo_utils import importutils
|
||||
import six
|
||||
|
||||
from neutronclient._i18n import _
|
||||
from neutronclient.common import exceptions
|
||||
|
||||
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.
|
||||
"""
|
||||
for v in vars:
|
||||
value = os.environ.get(v)
|
||||
if value:
|
||||
return value
|
||||
return kwargs.get('default', '')
|
||||
|
||||
|
||||
def convert_to_uppercase(string):
|
||||
return string.upper()
|
||||
|
||||
|
||||
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.
|
||||
|
||||
:param api_name: the name of the API, e.g. 'compute', 'image', etc
|
||||
:param version: the requested API version
|
||||
:param version_map: a dict of client classes keyed by version
|
||||
:rtype: a client class for the requested API version
|
||||
"""
|
||||
try:
|
||||
client_path = version_map[str(version)]
|
||||
except (KeyError, ValueError):
|
||||
msg = _("Invalid %(api_name)s client version '%(version)s'. must be "
|
||||
"one of: %(map_keys)s")
|
||||
msg = msg % {'api_name': api_name, 'version': version,
|
||||
'map_keys': ', '.join(version_map.keys())}
|
||||
raise exceptions.UnsupportedVersion(msg)
|
||||
|
||||
return importutils.import_class(client_path)
|
||||
|
||||
|
||||
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)
|
||||
:param fields: tuple of strings with the desired field names
|
||||
:param mixed_case_fields: tuple of field names to preserve case
|
||||
:param formatters: dictionary mapping field names to callables
|
||||
to format the values
|
||||
"""
|
||||
if formatters is None:
|
||||
formatters = {}
|
||||
|
||||
row = []
|
||||
|
||||
for field in fields:
|
||||
if field in formatters:
|
||||
row.append(formatters[field](item))
|
||||
else:
|
||||
if field in mixed_case_fields:
|
||||
field_name = field.replace(' ', '_')
|
||||
else:
|
||||
field_name = field.lower().replace(' ', '_')
|
||||
if not hasattr(item, field_name) and isinstance(item, dict):
|
||||
data = item[field_name]
|
||||
else:
|
||||
data = getattr(item, field_name, '')
|
||||
if data is None:
|
||||
data = ''
|
||||
row.append(data)
|
||||
return tuple(row)
|
||||
|
||||
|
||||
def str2bool(strbool):
|
||||
if strbool is None:
|
||||
return None
|
||||
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:
|
||||
for kv in strdict.split(','):
|
||||
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):
|
||||
if not _logger.isEnabledFor(logging.DEBUG):
|
||||
return
|
||||
|
||||
string_parts = ['curl -i']
|
||||
for element in args:
|
||||
if element in ('GET', 'POST', 'DELETE', 'PUT'):
|
||||
string_parts.append(' -X %s' % element)
|
||||
else:
|
||||
string_parts.append(' %s' % element)
|
||||
|
||||
for (key, value) in six.iteritems(kwargs['headers']):
|
||||
if key in SENSITIVE_HEADERS:
|
||||
v = value.encode('utf-8')
|
||||
h = hashlib.sha1(v)
|
||||
d = h.hexdigest()
|
||||
value = "{SHA1}%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']))
|
||||
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: %(code)s %(headers)s %(body)s",
|
||||
{'code': resp.status_code,
|
||||
'headers': resp.headers,
|
||||
'body': body})
|
||||
|
||||
|
||||
def _safe_encode_without_obj(data):
|
||||
if isinstance(data, six.string_types):
|
||||
return encodeutils.safe_encode(data)
|
||||
return data
|
||||
|
||||
|
||||
def safe_encode_list(data):
|
||||
return list(map(_safe_encode_without_obj, data))
|
||||
|
||||
|
||||
def safe_encode_dict(data):
|
||||
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(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)
|
||||
|
||||
|
||||
def is_valid_cidr(cidr):
|
||||
try:
|
||||
netaddr.IPNetwork(cidr)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
|
@ -1,69 +0,0 @@
|
|||
# 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}))
|
|
@ -1,27 +0,0 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
# TODO(amotoki): Remove this file at the beginning of Nxx cycle.
|
||||
|
||||
from debtcollector import moves
|
||||
|
||||
from neutronclient import _i18n
|
||||
|
||||
message = ("moved to neutronclient._i18n; please migrate to local "
|
||||
"oslo_i18n usage, as defined at "
|
||||
"http://docs.openstack.org/developer/oslo.i18n/usage.html")
|
||||
|
||||
_ = moves.moved_function(_i18n._, '_', __name__, message=message)
|
||||
_LC = moves.moved_function(_i18n._LC, '_LC', __name__, message=message)
|
||||
_LE = moves.moved_function(_i18n._LE, '_LE', __name__, message=message)
|
||||
_LW = moves.moved_function(_i18n._LW, '_LW', __name__, message=message)
|
||||
_LI = moves.moved_function(_i18n._LI, '_LI', __name__, message=message)
|
|
@ -1,65 +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.
|
||||
#
|
||||
|
||||
from neutronclient.common import utils
|
||||
|
||||
|
||||
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."""
|
||||
neutron_client = utils.get_client_class(
|
||||
API_NAME,
|
||||
instance._api_version[API_NAME],
|
||||
API_VERSIONS,
|
||||
)
|
||||
instance.initialize()
|
||||
url = instance._url
|
||||
url = url.rstrip("/")
|
||||
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(
|
||||
API_NAME,
|
||||
api_version,
|
||||
API_VERSIONS,
|
||||
)
|
||||
return neutron_client(*args, **kwargs)
|
|
@ -1,782 +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.
|
||||
#
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import abc
|
||||
import argparse
|
||||
import functools
|
||||
import logging
|
||||
|
||||
from cliff import command
|
||||
from cliff import lister
|
||||
from cliff import show
|
||||
from oslo_serialization import jsonutils
|
||||
import six
|
||||
|
||||
from neutronclient._i18n import _
|
||||
from neutronclient.common import exceptions
|
||||
from neutronclient.common import utils
|
||||
|
||||
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 information.'),
|
||||
action='store_true',
|
||||
default=False, )
|
||||
parser.add_argument(
|
||||
'--show_details',
|
||||
action='store_true',
|
||||
help=argparse.SUPPRESS)
|
||||
parser.add_argument(
|
||||
'--fields',
|
||||
help=argparse.SUPPRESS,
|
||||
action='append',
|
||||
default=[])
|
||||
parser.add_argument(
|
||||
'-F', '--field',
|
||||
dest='fields', metavar='FIELD',
|
||||
help=_('Specify the field(s) to be returned by server. You can '
|
||||
'repeat this option.'),
|
||||
action='append',
|
||||
default=[])
|
||||
|
||||
|
||||
def add_pagination_argument(parser):
|
||||
parser.add_argument(
|
||||
'-P', '--page-size',
|
||||
dest='page_size', metavar='SIZE', type=int,
|
||||
help=_("Specify retrieve unit of each request, then split one request "
|
||||
"to several requests."),
|
||||
default=None)
|
||||
|
||||
|
||||
def add_sorting_argument(parser):
|
||||
parser.add_argument(
|
||||
'--sort-key',
|
||||
dest='sort_key', metavar='FIELD',
|
||||
action='append',
|
||||
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=_("Sorts the list in the specified direction. You can repeat "
|
||||
"this option."),
|
||||
action='append',
|
||||
default=[],
|
||||
choices=['asc', 'desc'])
|
||||
|
||||
|
||||
def is_number(s):
|
||||
try:
|
||||
float(s) # for int, long and float
|
||||
except ValueError:
|
||||
try:
|
||||
complex(s) # for complex
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def _process_previous_argument(current_arg, _value_number, current_type_str,
|
||||
_list_flag, _values_specs, _clear_flag,
|
||||
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))
|
||||
if _value_number > 1 or _list_flag or current_type_str == 'list':
|
||||
current_arg.update({'nargs': '+'})
|
||||
elif _value_number == 0:
|
||||
if _clear_flag:
|
||||
# if we have action=clear, we use argument's default
|
||||
# value None for argument
|
||||
_values_specs.pop()
|
||||
else:
|
||||
# We assume non value argument as bool one
|
||||
current_arg.update({'action': 'store_true'})
|
||||
|
||||
|
||||
def parse_args_to_dict(values_specs):
|
||||
"""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,
|
||||
list_nets -- --tag x y --key1 value1, where '-- --tag x y --key1 value1'
|
||||
is extra options to our list_nets. This feature can support V2.0 API's
|
||||
fields selection and filters. For example, to list networks which has name
|
||||
'test4', we can have list_nets -- --name=test4.
|
||||
|
||||
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
|
||||
values_specs_copy = values_specs[:]
|
||||
if values_specs_copy and values_specs_copy[0] == '--':
|
||||
del values_specs_copy[0]
|
||||
# converted ArgumentParser arguments for each of the options
|
||||
_options = {}
|
||||
# the argument part for current option in _options
|
||||
current_arg = None
|
||||
# the string after remove meta info in values_specs
|
||||
# for example, '--tag x y --key1 value1'
|
||||
_values_specs = []
|
||||
# record the count of values for an option
|
||||
# for example: for '--tag x y', it is 2, while for '--key1 value1', it is 1
|
||||
_value_number = 0
|
||||
# list=true
|
||||
_list_flag = False
|
||||
# action=clear
|
||||
_clear_flag = False
|
||||
# the current item in values_specs
|
||||
current_item = None
|
||||
# the str after 'type='
|
||||
current_type_str = None
|
||||
for _item in values_specs_copy:
|
||||
if _item.startswith('--'):
|
||||
# Deal with previous argument if any
|
||||
_process_previous_argument(
|
||||
current_arg, _value_number, current_type_str,
|
||||
_list_flag, _values_specs, _clear_flag, values_specs)
|
||||
|
||||
# Init variables for current argument
|
||||
current_item = _item
|
||||
_list_flag = False
|
||||
_clear_flag = False
|
||||
current_type_str = None
|
||||
if "=" in _item:
|
||||
_value_number = 1
|
||||
_item = _item.split('=')[0]
|
||||
else:
|
||||
_value_number = 0
|
||||
if _item in _options:
|
||||
raise exceptions.CommandError(
|
||||
_("Duplicated options %s") % ' '.join(values_specs))
|
||||
else:
|
||||
_options.update({_item: {}})
|
||||
current_arg = _options[_item]
|
||||
_item = current_item
|
||||
elif _item.startswith('type='):
|
||||
if current_arg is None:
|
||||
raise exceptions.CommandError(
|
||||
_("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
|
||||
elif _item == 'list=true':
|
||||
_list_flag = True
|
||||
continue
|
||||
elif _item == 'action=clear':
|
||||
_clear_flag = True
|
||||
continue
|
||||
|
||||
if not _item.startswith('--'):
|
||||
# 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)):
|
||||
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
|
||||
_process_previous_argument(
|
||||
current_arg, _value_number, current_type_str,
|
||||
_list_flag, _values_specs, _clear_flag, values_specs)
|
||||
|
||||
# Populate the parser with arguments
|
||||
_parser = argparse.ArgumentParser(add_help=False)
|
||||
for opt, optspec in six.iteritems(_options):
|
||||
_parser.add_argument(opt, **optspec)
|
||||
_args = _parser.parse_args(_values_specs)
|
||||
|
||||
result_dict = {}
|
||||
for opt in six.iterkeys(_options):
|
||||
_opt = opt.split('--', 2)[1]
|
||||
_opt = _opt.replace('-', '_')
|
||||
_value = getattr(_args, _opt)
|
||||
result_dict.update({_opt: _value})
|
||||
return result_dict
|
||||
|
||||
|
||||
def _merge_args(qCmd, parsed_args, _extra_values, value_specs):
|
||||
"""Merge arguments from _extra_values into parsed_args.
|
||||
|
||||
If an argument value are provided in both and it is a list,
|
||||
the values in _extra_values will be merged into parsed_args.
|
||||
|
||||
@param parsed_args: the parsed args from known options
|
||||
@param _extra_values: the other parsed arguments in unknown parts
|
||||
@param values_specs: the unparsed unknown parts
|
||||
"""
|
||||
temp_values = _extra_values.copy()
|
||||
for key, value in six.iteritems(temp_values):
|
||||
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
|
||||
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.
|
||||
|
||||
:param obj: the object updated into dict
|
||||
:param dict: the result dictionary
|
||||
:param attributes: a list of attributes belonging to obj
|
||||
"""
|
||||
for attribute in attributes:
|
||||
if hasattr(obj, attribute) and getattr(obj, attribute) is not None:
|
||||
dict[attribute] = getattr(obj, attribute)
|
||||
|
||||
|
||||
# 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)
|
||||
|
||||
|
||||
@six.add_metaclass(NeutronCommandMeta)
|
||||
class NeutronCommand(command.Command):
|
||||
|
||||
values_specs = []
|
||||
json_indent = None
|
||||
resource = None
|
||||
shadow_resource = None
|
||||
parent_id = None
|
||||
|
||||
def run(self, parsed_args):
|
||||
self.log.debug('run(%s)', parsed_args)
|
||||
return super(NeutronCommand, self).run(parsed_args)
|
||||
|
||||
@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
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(NeutronCommand, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'--request-format',
|
||||
help=_('DEPRECATED! Only JSON request format is supported.'),
|
||||
default='json',
|
||||
choices=['json', ], )
|
||||
parser.add_argument(
|
||||
'--request_format',
|
||||
choices=['json', ],
|
||||
help=argparse.SUPPRESS)
|
||||
|
||||
return parser
|
||||
|
||||
def cleanup_output_data(self, data):
|
||||
pass
|
||||
|
||||
def format_output_data(self, data):
|
||||
self.cleanup_output_data(data)
|
||||
# Modify data to make it more readable
|
||||
if self.resource in data:
|
||||
for k, v in six.iteritems(data[self.resource]):
|
||||
if isinstance(v, list):
|
||||
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 = jsonutils.dumps(v, indent=self.json_indent)
|
||||
data[self.resource][k] = value
|
||||
elif v is None:
|
||||
data[self.resource][k] = ''
|
||||
|
||||
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."""
|
||||
|
||||
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.'), )
|
||||
parser.add_argument(
|
||||
'--tenant_id',
|
||||
help=argparse.SUPPRESS)
|
||||
self.add_known_arguments(parser)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
self.set_extra_attrs(parsed_args)
|
||||
neutron_client = self.get_client()
|
||||
_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.cmd_resource)
|
||||
if self.parent_id:
|
||||
data = obj_creator(self.parent_id, body)
|
||||
else:
|
||||
data = obj_creator(body)
|
||||
self.format_output_data(data)
|
||||
info = self.resource in data and data[self.resource] or None
|
||||
if info:
|
||||
if parsed_args.formatter == 'table':
|
||||
print(_('Created a new %s:') % self.resource,
|
||||
file=self.app.stdout)
|
||||
else:
|
||||
info = {'': ''}
|
||||
return zip(*sorted(six.iteritems(info)))
|
||||
|
||||
|
||||
class UpdateCommand(NeutronCommand):
|
||||
"""Update resource's information."""
|
||||
|
||||
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=help_str % self.help_resource)
|
||||
self.add_known_arguments(parser)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
self.set_extra_attrs(parsed_args)
|
||||
neutron_client = self.get_client()
|
||||
_extra_values = parse_args_to_dict(self.values_specs)
|
||||
_merge_args(self, parsed_args, _extra_values,
|
||||
self.values_specs)
|
||||
body = self.args2body(parsed_args)
|
||||
if self.resource in body:
|
||||
body[self.resource].update(_extra_values)
|
||||
else:
|
||||
body[self.resource] = _extra_values
|
||||
if not body[self.resource]:
|
||||
raise exceptions.CommandError(
|
||||
_("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,
|
||||
cmd_resource=self.cmd_resource, parent_id=self.parent_id)
|
||||
else:
|
||||
_id = find_resourceid_by_id(
|
||||
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)
|
||||
return
|
||||
|
||||
|
||||
class DeleteCommand(NeutronCommand):
|
||||
"""Delete a given resource."""
|
||||
|
||||
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(s) or name(s) of %s to delete.')
|
||||
else:
|
||||
help_str = _('ID(s) of %s to delete.')
|
||||
parser.add_argument(
|
||||
'id', metavar=self.resource.upper(),
|
||||
nargs='+' if self.bulk_delete else 1,
|
||||
help=help_str % self.help_resource)
|
||||
self.add_known_arguments(parser)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
self.set_extra_attrs(parsed_args)
|
||||
neutron_client = self.get_client()
|
||||
obj_deleter = getattr(neutron_client,
|
||||
"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:
|
||||
print((_("Unable to find %(resource)s(s) with id(s) "
|
||||
"'%(id)s'") %
|
||||
{'resource': self.cmd_resource,
|
||||
'id': ", ".join(non_existent)}),
|
||||
file=self.app.stdout)
|
||||
if multiple_ids:
|
||||
print((_("Multiple %(resource)s(s) matches found for name(s)"
|
||||
" '%(id)s'. Please use an ID to be more specific.")) %
|
||||
{'resource': self.cmd_resource,
|
||||
'id': ", ".join(multiple_ids)},
|
||||
file=self.app.stdout)
|
||||
|
||||
def delete_item(self, obj_deleter, neutron_client, item_id):
|
||||
if self.allow_names:
|
||||
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 = 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."""
|
||||
|
||||
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)
|
||||
add_show_list_common_argument(parser)
|
||||
if self.pagination_support:
|
||||
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
|
||||
if parsed_args.fields:
|
||||
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 = neutron_client.get_resource_plural(self.cmd_resource)
|
||||
obj_lister = getattr(neutron_client, "list_%s" % resource_plural)
|
||||
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."""
|
||||
neutron_client = self.get_client()
|
||||
_extra_values = parse_args_to_dict(self.values_specs)
|
||||
_merge_args(self, parsed_args, _extra_values,
|
||||
self.values_specs)
|
||||
search_opts = self.args2search_opts(parsed_args)
|
||||
search_opts.update(_extra_values)
|
||||
if self.pagination_support:
|
||||
page_size = parsed_args.page_size
|
||||
if page_size:
|
||||
search_opts.update({'limit': page_size})
|
||||
if self.sorting_support:
|
||||
keys = parsed_args.sort_key
|
||||
if keys:
|
||||
search_opts.update({'sort_key': keys})
|
||||
dirs = parsed_args.sort_dir
|
||||
len_diff = len(keys) - len(dirs)
|
||||
if len_diff > 0:
|
||||
dirs += ['asc'] * len_diff
|
||||
elif len_diff < 0:
|
||||
dirs = dirs[:len(keys)]
|
||||
if dirs:
|
||||
search_opts.update({'sort_dir': dirs})
|
||||
data = self.call_server(neutron_client, search_opts, parsed_args)
|
||||
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 an original list returned from
|
||||
the neutron server. For example, you can add subnet cidr information
|
||||
to a network list.
|
||||
"""
|
||||
pass
|
||||
|
||||
def setup_columns(self, info, parsed_args):
|
||||
_columns = len(info) > 0 and sorted(info[0].keys()) or []
|
||||
if not _columns:
|
||||
# clean the parsed_args.columns so that cliff will not break
|
||||
parsed_args.columns = []
|
||||
elif parsed_args.columns:
|
||||
_columns = [x for x in parsed_args.columns if x in _columns]
|
||||
elif self.list_columns:
|
||||
# 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]
|
||||
|
||||
formatters = self._formatters
|
||||
if hasattr(self, '_formatters_csv') and parsed_args.formatter == 'csv':
|
||||
formatters = self._formatters_csv
|
||||
|
||||
return (_columns, (utils.get_item_properties(
|
||||
s, _columns, formatters=formatters, )
|
||||
for s in info), )
|
||||
|
||||
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."""
|
||||
|
||||
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.')
|
||||
else:
|
||||
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.help_resource)
|
||||
self.add_known_arguments(parser)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
self.set_extra_attrs(parsed_args)
|
||||
neutron_client = self.get_client()
|
||||
|
||||
params = {}
|
||||
if parsed_args.show_details:
|
||||
params = {'verbose': 'True'}
|
||||
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,
|
||||
cmd_resource=self.cmd_resource,
|
||||
parent_id=self.parent_id)
|
||||
else:
|
||||
_id = parsed_args.id
|
||||
|
||||
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.format_output_data(data)
|
||||
resource = data[self.resource]
|
||||
if self.resource in data:
|
||||
return zip(*sorted(six.iteritems(resource)))
|
||||
else:
|
||||
return None
|
|
@ -1,84 +0,0 @@
|
|||
# Copyright 2015 Huawei Technologies India Pvt. Ltd..
|
||||
# All Rights Reserved
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
from neutronclient._i18n import _
|
||||
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.'))
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
body = {}
|
||||
neutronV20.update_dict(parsed_args, body, ['name'])
|
||||
return {self.resource: body}
|
|
@ -1,78 +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 neutronclient._i18n import _
|
||||
from neutronclient.neutron import v2_0 as neutronV20
|
||||
|
||||
|
||||
def _format_timestamp(component):
|
||||
try:
|
||||
return component['heartbeat_timestamp'].split(".", 2)[0]
|
||||
except (TypeError, KeyError):
|
||||
return ''
|
||||
|
||||
|
||||
class ListAgent(neutronV20.ListCommand):
|
||||
"""List agents."""
|
||||
|
||||
resource = 'agent'
|
||||
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:
|
||||
if 'alive' in agent:
|
||||
agent['alive'] = ":-)" if agent['alive'] else 'xxx'
|
||||
|
||||
|
||||
class ShowAgent(neutronV20.ShowCommand):
|
||||
"""Show information of a given agent."""
|
||||
|
||||
resource = 'agent'
|
||||
allow_names = False
|
||||
json_indent = 5
|
||||
|
||||
|
||||
class DeleteAgent(neutronV20.DeleteCommand):
|
||||
"""Delete a given agent."""
|
||||
|
||||
resource = 'agent'
|
||||
allow_names = False
|
||||
|
||||
|
||||
class UpdateAgent(neutronV20.UpdateCommand):
|
||||
"""Updates the admin status and description for a specified agent."""
|
||||
|
||||
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}
|
|
@ -1,343 +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
|
||||
|
||||
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
|
||||
|
||||
|
||||
PERFECT_TIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%f"
|
||||
|
||||
|
||||
class AddNetworkToDhcpAgent(neutronV20.NeutronCommand):
|
||||
"""Add a network to a DHCP agent."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(AddNetworkToDhcpAgent, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'dhcp_agent',
|
||||
metavar='DHCP_AGENT',
|
||||
help=_('ID of the DHCP agent.'))
|
||||
parser.add_argument(
|
||||
'network',
|
||||
metavar='NETWORK',
|
||||
help=_('Network to add.'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
_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,
|
||||
{'network_id': _net_id})
|
||||
print(_('Added network %s to DHCP agent') % parsed_args.network,
|
||||
file=self.app.stdout)
|
||||
|
||||
|
||||
class RemoveNetworkFromDhcpAgent(neutronV20.NeutronCommand):
|
||||
"""Remove a network from a DHCP agent."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(RemoveNetworkFromDhcpAgent, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'dhcp_agent',
|
||||
metavar='DHCP_AGENT',
|
||||
help=_('ID of the DHCP agent.'))
|
||||
parser.add_argument(
|
||||
'network',
|
||||
metavar='NETWORK',
|
||||
help=_('Network to remove.'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
_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 from DHCP agent') % parsed_args.network,
|
||||
file=self.app.stdout)
|
||||
|
||||
|
||||
class ListNetworksOnDhcpAgent(network.ListNetwork):
|
||||
"""List the networks on a DHCP agent."""
|
||||
|
||||
unknown_parts_flag = False
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListNetworksOnDhcpAgent,
|
||||
self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'dhcp_agent',
|
||||
metavar='DHCP_AGENT',
|
||||
help=_('ID of the DHCP agent.'))
|
||||
return parser
|
||||
|
||||
def call_server(self, neutron_client, search_opts, parsed_args):
|
||||
data = neutron_client.list_networks_on_dhcp_agent(
|
||||
parsed_args.dhcp_agent, **search_opts)
|
||||
return data
|
||||
|
||||
|
||||
class ListDhcpAgentsHostingNetwork(neutronV20.ListCommand):
|
||||
"""List DHCP agents hosting a network."""
|
||||
|
||||
resource = 'agent'
|
||||
_formatters = {}
|
||||
list_columns = ['id', 'host', 'admin_state_up', 'alive']
|
||||
unknown_parts_flag = False
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListDhcpAgentsHostingNetwork,
|
||||
self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'network',
|
||||
metavar='NETWORK',
|
||||
help=_('Network 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,
|
||||
'network',
|
||||
parsed_args.network)
|
||||
search_opts['network'] = _id
|
||||
data = neutron_client.list_dhcp_agent_hosting_networks(**search_opts)
|
||||
return data
|
||||
|
||||
|
||||
class AddRouterToL3Agent(neutronV20.NeutronCommand):
|
||||
"""Add a router to a L3 agent."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(AddRouterToL3Agent, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'l3_agent',
|
||||
metavar='L3_AGENT',
|
||||
help=_('ID of the L3 agent.'))
|
||||
parser.add_argument(
|
||||
'router',
|
||||
metavar='ROUTER',
|
||||
help=_('Router to add.'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
_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,
|
||||
{'router_id': _id})
|
||||
print(_('Added router %s to L3 agent') % parsed_args.router,
|
||||
file=self.app.stdout)
|
||||
|
||||
|
||||
class RemoveRouterFromL3Agent(neutronV20.NeutronCommand):
|
||||
"""Remove a router from a L3 agent."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(RemoveRouterFromL3Agent, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'l3_agent',
|
||||
metavar='L3_AGENT',
|
||||
help=_('ID of the L3 agent.'))
|
||||
parser.add_argument(
|
||||
'router',
|
||||
metavar='ROUTER',
|
||||
help=_('Router to remove.'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
_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 from L3 agent') % parsed_args.router,
|
||||
file=self.app.stdout)
|
||||
|
||||
|
||||
class ListRoutersOnL3Agent(neutronV20.ListCommand):
|
||||
"""List the routers on a L3 agent."""
|
||||
|
||||
_formatters = {'external_gateway_info':
|
||||
router._format_external_gateway_info}
|
||||
list_columns = ['id', 'name', 'external_gateway_info']
|
||||
resource = 'router'
|
||||
unknown_parts_flag = False
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListRoutersOnL3Agent,
|
||||
self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'l3_agent',
|
||||
metavar='L3_AGENT',
|
||||
help=_('ID of the L3 agent to query.'))
|
||||
return parser
|
||||
|
||||
def call_server(self, neutron_client, search_opts, parsed_args):
|
||||
data = neutron_client.list_routers_on_l3_agent(
|
||||
parsed_args.l3_agent, **search_opts)
|
||||
return data
|
||||
|
||||
|
||||
class ListL3AgentsHostingRouter(neutronV20.ListCommand):
|
||||
"""List L3 agents hosting a router."""
|
||||
|
||||
resource = 'agent'
|
||||
_formatters = {}
|
||||
list_columns = ['id', 'host', 'admin_state_up', 'alive']
|
||||
unknown_parts_flag = False
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListL3AgentsHostingRouter,
|
||||
self).get_parser(prog_name)
|
||||
parser.add_argument('router',
|
||||
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'
|
||||
|
||||
def call_server(self, neutron_client, search_opts, parsed_args):
|
||||
_id = neutronV20.find_resourceid_by_name_or_id(neutron_client,
|
||||
'router',
|
||||
parsed_args.router)
|
||||
search_opts['router'] = _id
|
||||
data = neutron_client.list_l3_agent_hosting_routers(**search_opts)
|
||||
return data
|
||||
|
||||
|
||||
class ListPoolsOnLbaasAgent(neutronV20.ListCommand):
|
||||
"""List the pools on a loadbalancer agent."""
|
||||
|
||||
list_columns = ['id', 'name', 'lb_method', 'protocol',
|
||||
'admin_state_up', 'status']
|
||||
resource = 'pool'
|
||||
unknown_parts_flag = False
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListPoolsOnLbaasAgent, 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_pools_on_lbaas_agent(
|
||||
parsed_args.lbaas_agent, **search_opts)
|
||||
return data
|
||||
|
||||
|
||||
class GetLbaasAgentHostingPool(neutronV20.ListCommand):
|
||||
"""Get loadbalancer agent hosting a pool.
|
||||
|
||||
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(GetLbaasAgentHostingPool,
|
||||
self).get_parser(prog_name)
|
||||
parser.add_argument('pool',
|
||||
metavar='POOL',
|
||||
help=_('Pool 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,
|
||||
'pool',
|
||||
parsed_args.pool)
|
||||
search_opts['pool'] = _id
|
||||
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
|
|
@ -1,81 +0,0 @@
|
|||
# Copyright 2016 IBM
|
||||
# All Rights Reserved
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
import 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 <tenant-id>
|
||||
# (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
|
|
@ -1,38 +0,0 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutronclient._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
|
|
@ -1,117 +0,0 @@
|
|||
# Copyright 2016 Huawei Technologies India Pvt. Ltd.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
from neutronclient._i18n import _
|
||||
from neutronclient.neutron import v2_0 as neutronV20
|
||||
from neutronclient.neutron.v2_0.bgp import speaker as bgp_speaker
|
||||
|
||||
|
||||
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
|
|
@ -1,127 +0,0 @@
|
|||
# Copyright 2016 Huawei Technologies India Pvt. Ltd.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
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'
|
|
@ -1,277 +0,0 @@
|
|||
# Copyright 2016 Huawei Technologies India Pvt. Ltd.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
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 = 65535
|
||||
|
||||
|
||||
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
|
|
@ -1,93 +0,0 @@
|
|||
# Copyright 2015 Rackspace Hosting Inc.
|
||||
# All Rights Reserved
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
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'
|
|
@ -1,67 +0,0 @@
|
|||
# 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] = ""
|
|
@ -1,31 +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.
|
||||
#
|
||||
|
||||
from neutronclient.neutron import v2_0 as cmd_base
|
||||
|
||||
|
||||
class ListExt(cmd_base.ListCommand):
|
||||
"""List all extensions."""
|
||||
|
||||
resource = 'extension'
|
||||
list_columns = ['alias', 'name']
|
||||
|
||||
|
||||
class ShowExt(cmd_base.ShowCommand):
|
||||
"""Show information of a given resource."""
|
||||
|
||||
resource = "extension"
|
||||
allow_names = False
|
|
@ -1,165 +0,0 @@
|
|||
# Copyright 2015 Hewlett-Packard Development Company, L.P.
|
||||
# All Rights Reserved
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
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)
|
|
@ -1,99 +0,0 @@
|
|||
# Copyright 2015 Hewlett-Packard Development Company, L.P.
|
||||
# All Rights Reserved
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import 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}
|
|
@ -1,150 +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.
|
||||
#
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
|
||||
from neutronclient._i18n import _
|
||||
from neutronclient.neutron import v2_0 as neutronV20
|
||||
from neutronclient.neutron.v2_0 import dns
|
||||
|
||||
|
||||
class ListFloatingIP(neutronV20.ListCommand):
|
||||
"""List floating IPs that belong to a given tenant."""
|
||||
|
||||
resource = 'floatingip'
|
||||
list_columns = ['id', 'fixed_ip_address', 'floating_ip_address',
|
||||
'port_id']
|
||||
pagination_support = True
|
||||
sorting_support = True
|
||||
|
||||
|
||||
class ShowFloatingIP(neutronV20.ShowCommand):
|
||||
"""Show information of a given floating IP."""
|
||||
|
||||
resource = 'floatingip'
|
||||
allow_names = False
|
||||
|
||||
|
||||
class CreateFloatingIP(neutronV20.CreateCommand):
|
||||
"""Create a floating IP for a given tenant."""
|
||||
|
||||
resource = 'floatingip'
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'floating_network_id', metavar='FLOATING_NETWORK',
|
||||
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 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).'))
|
||||
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 = {'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."""
|
||||
|
||||
resource = 'floatingip'
|
||||
allow_names = False
|
||||
|
||||
|
||||
class AssociateFloatingIP(neutronV20.NeutronCommand):
|
||||
"""Create a mapping between a floating IP and a fixed IP."""
|
||||
|
||||
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.'))
|
||||
parser.add_argument(
|
||||
'port_id', metavar='PORT',
|
||||
help=_('ID or name of the port to be associated with the '
|
||||
'floating IP.'))
|
||||
parser.add_argument(
|
||||
'--fixed-ip-address',
|
||||
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 take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
update_dict = {}
|
||||
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 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."""
|
||||
|
||||
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 disassociate.'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
neutron_client.update_floatingip(parsed_args.floatingip_id,
|
||||
{'floatingip': {'port_id': None}})
|
||||
print(_('Disassociated floating IP %s') % parsed_args.floatingip_id,
|
||||
file=self.app.stdout)
|
|
@ -1,128 +0,0 @@
|
|||
# Copyright 2013 Big Switch Networks
|
||||
# All Rights Reserved
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
from neutronclient._i18n import _
|
||||
from neutronclient.common import utils
|
||||
from neutronclient.neutron import v2_0 as neutronv20
|
||||
|
||||
|
||||
def add_common_args(parser):
|
||||
parser.add_argument(
|
||||
'--name',
|
||||
help=_('Name for the 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'
|
||||
list_columns = ['id', 'name', 'firewall_policy_id']
|
||||
_formatters = {}
|
||||
pagination_support = True
|
||||
sorting_support = True
|
||||
|
||||
|
||||
class ShowFirewall(neutronv20.ShowCommand):
|
||||
"""Show information of a given firewall."""
|
||||
|
||||
resource = 'firewall'
|
||||
|
||||
|
||||
class CreateFirewall(neutronv20.CreateCommand):
|
||||
"""Create a firewall."""
|
||||
|
||||
resource = 'firewall'
|
||||
|
||||
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.'))
|
||||
parser.add_argument(
|
||||
'--admin-state-down',
|
||||
dest='admin_state',
|
||||
action='store_false',
|
||||
help=_('Set admin state up to false.'))
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
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'
|
||||
|
||||
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'
|
|
@ -1,229 +0,0 @@
|
|||
# Copyright 2013 Big Switch Networks
|
||||
# All Rights Reserved
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
|
||||
from neutronclient._i18n import _
|
||||
from neutronclient.common import utils
|
||||
from neutronclient.neutron import v2_0 as neutronv20
|
||||
|
||||
|
||||
def _format_firewall_rules(firewall_policy):
|
||||
try:
|
||||
output = '[' + ',\n '.join([rule for rule in
|
||||
firewall_policy['firewall_rules']]) + ']'
|
||||
return output
|
||||
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'
|
||||
list_columns = ['id', 'name', 'firewall_rules']
|
||||
_formatters = {'firewall_rules': _format_firewall_rules,
|
||||
}
|
||||
pagination_support = True
|
||||
sorting_support = True
|
||||
|
||||
|
||||
class ShowFirewallPolicy(neutronv20.ShowCommand):
|
||||
"""Show information of a given firewall policy."""
|
||||
|
||||
resource = 'firewall_policy'
|
||||
|
||||
|
||||
class CreateFirewallPolicy(neutronv20.CreateCommand):
|
||||
"""Create a firewall policy."""
|
||||
|
||||
resource = 'firewall_policy'
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'name',
|
||||
metavar='NAME',
|
||||
help=_('Name for the firewall policy.'))
|
||||
parser.add_argument(
|
||||
'--shared',
|
||||
action='store_true',
|
||||
help=_('Create a shared policy.'),
|
||||
default=argparse.SUPPRESS)
|
||||
parser.add_argument(
|
||||
'--audited',
|
||||
action='store_true',
|
||||
help=_('Sets audited to True.'),
|
||||
default=argparse.SUPPRESS)
|
||||
add_common_args(parser)
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
return parse_common_args(self.get_client(), parsed_args)
|
||||
|
||||
|
||||
class UpdateFirewallPolicy(neutronv20.UpdateCommand):
|
||||
"""Update a given firewall policy."""
|
||||
|
||||
resource = 'firewall_policy'
|
||||
|
||||
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'
|
||||
|
||||
|
||||
class FirewallPolicyInsertRule(neutronv20.UpdateCommand):
|
||||
"""Insert a rule into a given firewall policy."""
|
||||
|
||||
resource = 'firewall_policy'
|
||||
|
||||
def call_api(self, neutron_client, firewall_policy_id, body):
|
||||
return neutron_client.firewall_policy_insert_rule(firewall_policy_id,
|
||||
body)
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
_rule = ''
|
||||
if parsed_args.firewall_rule_id:
|
||||
_rule = neutronv20.find_resourceid_by_name_or_id(
|
||||
self.get_client(), 'firewall_rule',
|
||||
parsed_args.firewall_rule_id)
|
||||
_insert_before = ''
|
||||
if 'insert_before' in parsed_args:
|
||||
if parsed_args.insert_before:
|
||||
_insert_before = neutronv20.find_resourceid_by_name_or_id(
|
||||
self.get_client(), 'firewall_rule',
|
||||
parsed_args.insert_before)
|
||||
_insert_after = ''
|
||||
if 'insert_after' in parsed_args:
|
||||
if parsed_args.insert_after:
|
||||
_insert_after = neutronv20.find_resourceid_by_name_or_id(
|
||||
self.get_client(), 'firewall_rule',
|
||||
parsed_args.insert_after)
|
||||
body = {'firewall_rule_id': _rule,
|
||||
'insert_before': _insert_before,
|
||||
'insert_after': _insert_after}
|
||||
return body
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(FirewallPolicyInsertRule, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'--insert-before',
|
||||
metavar='FIREWALL_RULE',
|
||||
help=_('Insert before this rule.'))
|
||||
parser.add_argument(
|
||||
'--insert-after',
|
||||
metavar='FIREWALL_RULE',
|
||||
help=_('Insert after this rule.'))
|
||||
parser.add_argument(
|
||||
'firewall_rule_id',
|
||||
metavar='FIREWALL_RULE',
|
||||
help=_('New rule to insert.'))
|
||||
self.add_known_arguments(parser)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
body = self.args2body(parsed_args)
|
||||
_id = neutronv20.find_resourceid_by_name_or_id(neutron_client,
|
||||
self.resource,
|
||||
parsed_args.id)
|
||||
self.call_api(neutron_client, _id, body)
|
||||
print((_('Inserted firewall rule in firewall policy %(id)s') %
|
||||
{'id': parsed_args.id}), file=self.app.stdout)
|
||||
|
||||
|
||||
class FirewallPolicyRemoveRule(neutronv20.UpdateCommand):
|
||||
"""Remove a rule from a given firewall policy."""
|
||||
|
||||
resource = 'firewall_policy'
|
||||
|
||||
def call_api(self, neutron_client, firewall_policy_id, body):
|
||||
return neutron_client.firewall_policy_remove_rule(firewall_policy_id,
|
||||
body)
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
_rule = ''
|
||||
if parsed_args.firewall_rule_id:
|
||||
_rule = neutronv20.find_resourceid_by_name_or_id(
|
||||
self.get_client(), 'firewall_rule',
|
||||
parsed_args.firewall_rule_id)
|
||||
body = {'firewall_rule_id': _rule}
|
||||
return body
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(FirewallPolicyRemoveRule, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'firewall_rule_id',
|
||||
metavar='FIREWALL_RULE',
|
||||
help=_('ID or name of the firewall rule to be removed '
|
||||
'from the policy.'))
|
||||
self.add_known_arguments(parser)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
body = self.args2body(parsed_args)
|
||||
_id = neutronv20.find_resourceid_by_name_or_id(neutron_client,
|
||||
self.resource,
|
||||
parsed_args.id)
|
||||
self.call_api(neutron_client, _id, body)
|
||||
print((_('Removed firewall rule from firewall policy %(id)s') %
|
||||
{'id': parsed_args.id}), file=self.app.stdout)
|
|
@ -1,168 +0,0 @@
|
|||
# Copyright 2013 Big Switch Networks
|
||||
# All Rights Reserved
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
import argparse
|
||||
|
||||
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):
|
||||
"""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'
|
||||
list_columns = ['id', 'name', 'firewall_policy_id', 'summary', 'enabled']
|
||||
pagination_support = True
|
||||
sorting_support = True
|
||||
|
||||
def extend_list(self, data, parsed_args):
|
||||
for d in data:
|
||||
val = []
|
||||
if d.get('protocol'):
|
||||
protocol = d['protocol'].upper()
|
||||
else:
|
||||
protocol = 'no-protocol'
|
||||
val.append(protocol)
|
||||
if 'source_ip_address' in d and 'source_port' in d:
|
||||
src = 'source: ' + str(d['source_ip_address']).lower()
|
||||
src = src + '(' + str(d['source_port']).lower() + ')'
|
||||
else:
|
||||
src = 'source: none specified'
|
||||
val.append(src)
|
||||
if 'destination_ip_address' in d and 'destination_port' in d:
|
||||
dst = 'dest: ' + str(d['destination_ip_address']).lower()
|
||||
dst = dst + '(' + str(d['destination_port']).lower() + ')'
|
||||
else:
|
||||
dst = 'dest: none specified'
|
||||
val.append(dst)
|
||||
if 'action' in d:
|
||||
action = d['action']
|
||||
else:
|
||||
action = 'no-action'
|
||||
val.append(action)
|
||||
d['summary'] = ',\n '.join(val)
|
||||
|
||||
|
||||
class ShowFirewallRule(neutronv20.ShowCommand):
|
||||
"""Show information of a given firewall rule."""
|
||||
|
||||
resource = 'firewall_rule'
|
||||
|
||||
|
||||
class CreateFirewallRule(neutronv20.CreateCommand):
|
||||
"""Create a firewall rule."""
|
||||
|
||||
resource = 'firewall_rule'
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'--shared',
|
||||
action='store_true',
|
||||
help=_('Set shared flag for the firewall rule.'),
|
||||
default=argparse.SUPPRESS)
|
||||
_add_common_args(parser)
|
||||
parser.add_argument(
|
||||
'--ip-version',
|
||||
type=int, choices=[4, 6], default=4,
|
||||
help=_('IP version for the firewall rule (default is 4).'))
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
return {self.resource: common_args2body(parsed_args)}
|
||||
|
||||
|
||||
class UpdateFirewallRule(neutronv20.UpdateCommand):
|
||||
"""Update a given firewall rule."""
|
||||
|
||||
resource = 'firewall_rule'
|
||||
|
||||
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(
|
||||
'--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):
|
||||
return {self.resource: common_args2body(parsed_args)}
|
||||
|
||||
|
||||
class DeleteFirewallRule(neutronv20.DeleteCommand):
|
||||
"""Delete a given firewall rule."""
|
||||
|
||||
resource = 'firewall_rule'
|
|
@ -1,163 +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.
|
||||
#
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
from neutronclient._i18n import _
|
||||
from neutronclient.neutron import v2_0 as neutronV20
|
||||
|
||||
|
||||
class ListHealthMonitor(neutronV20.ListCommand):
|
||||
"""List health monitors that belong to a given tenant."""
|
||||
|
||||
resource = 'health_monitor'
|
||||
list_columns = ['id', 'type', 'admin_state_up']
|
||||
pagination_support = True
|
||||
sorting_support = True
|
||||
|
||||
|
||||
class ShowHealthMonitor(neutronV20.ShowCommand):
|
||||
"""Show information of a given health monitor."""
|
||||
|
||||
resource = 'health_monitor'
|
||||
allow_names = False
|
||||
|
||||
|
||||
class CreateHealthMonitor(neutronV20.CreateCommand):
|
||||
"""Create a health monitor."""
|
||||
|
||||
resource = 'health_monitor'
|
||||
|
||||
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(
|
||||
'--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".'))
|
||||
parser.add_argument(
|
||||
'--http-method',
|
||||
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(
|
||||
'--delay',
|
||||
required=True,
|
||||
help=_('The time in milliseconds between sending probes to '
|
||||
'members.'))
|
||||
parser.add_argument(
|
||||
'--max-retries',
|
||||
required=True,
|
||||
help=_('Number of permissible connection failures before changing '
|
||||
'the member status to INACTIVE. [1..10]'))
|
||||
parser.add_argument(
|
||||
'--timeout',
|
||||
required=True,
|
||||
help=_('Maximum number of milliseconds for a monitor to wait for '
|
||||
'a connection to be established before it times out. The '
|
||||
'value must be less than the delay value.'))
|
||||
parser.add_argument(
|
||||
'--type',
|
||||
required=True, choices=['PING', 'TCP', 'HTTP', 'HTTPS'],
|
||||
help=_('One of the predefined health monitor types.'))
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
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,
|
||||
['expected_codes', 'http_method', 'url_path',
|
||||
'tenant_id'])
|
||||
return {self.resource: body}
|
||||
|
||||
|
||||
class UpdateHealthMonitor(neutronV20.UpdateCommand):
|
||||
"""Update a given health monitor."""
|
||||
|
||||
resource = 'health_monitor'
|
||||
allow_names = False
|
||||
|
||||
|
||||
class DeleteHealthMonitor(neutronV20.DeleteCommand):
|
||||
"""Delete a given health monitor."""
|
||||
|
||||
resource = 'health_monitor'
|
||||
allow_names = False
|
||||
|
||||
|
||||
class AssociateHealthMonitor(neutronV20.NeutronCommand):
|
||||
"""Create a mapping between a health monitor and a pool."""
|
||||
|
||||
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.'))
|
||||
parser.add_argument(
|
||||
'pool_id', metavar='POOL',
|
||||
help=_('ID of the pool to be associated with the health monitor.'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
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)
|
||||
neutron_client.associate_health_monitor(pool_id, body)
|
||||
print((_('Associated health monitor '
|
||||
'%s') % parsed_args.health_monitor_id),
|
||||
file=self.app.stdout)
|
||||
|
||||
|
||||
class DisassociateHealthMonitor(neutronV20.NeutronCommand):
|
||||
"""Remove a mapping from a health monitor to a pool."""
|
||||
|
||||
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.'))
|
||||
parser.add_argument(
|
||||
'pool_id', metavar='POOL',
|
||||
help=_('ID of the pool to be associated with the health monitor.'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
pool_id = neutronV20.find_resourceid_by_name_or_id(
|
||||
neutron_client, 'pool', parsed_args.pool_id)
|
||||
neutron_client.disassociate_health_monitor(pool_id,
|
||||
parsed_args
|
||||
.health_monitor_id)
|
||||
print((_('Disassociated health monitor '
|
||||
'%s') % parsed_args.health_monitor_id),
|
||||
file=self.app.stdout)
|
|
@ -1,87 +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.
|
||||
#
|
||||
|
||||
from neutronclient._i18n import _
|
||||
from neutronclient.neutron import v2_0 as neutronV20
|
||||
|
||||
|
||||
class ListMember(neutronV20.ListCommand):
|
||||
"""List members that belong to a given tenant."""
|
||||
|
||||
resource = 'member'
|
||||
list_columns = [
|
||||
'id', 'address', 'protocol_port', 'weight', 'admin_state_up', 'status'
|
||||
]
|
||||
pagination_support = True
|
||||
sorting_support = True
|
||||
|
||||
|
||||
class ShowMember(neutronV20.ShowCommand):
|
||||
"""Show information of a given member."""
|
||||
|
||||
resource = 'member'
|
||||
allow_names = False
|
||||
|
||||
|
||||
class CreateMember(neutronV20.CreateCommand):
|
||||
"""Create a member."""
|
||||
|
||||
resource = 'member'
|
||||
|
||||
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(
|
||||
'--weight',
|
||||
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.'))
|
||||
parser.add_argument(
|
||||
'--protocol-port',
|
||||
required=True,
|
||||
help=_('Port on which the pool member listens for requests or '
|
||||
'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 = {'pool_id': _pool_id,
|
||||
'admin_state_up': parsed_args.admin_state}
|
||||
neutronV20.update_dict(
|
||||
parsed_args,
|
||||
body,
|
||||
['address', 'protocol_port', 'weight', 'tenant_id']
|
||||
)
|
||||
return {self.resource: body}
|
||||
|
||||
|
||||
class UpdateMember(neutronV20.UpdateCommand):
|
||||
"""Update a given member."""
|
||||
|
||||
resource = 'member'
|
||||
|
||||
|
||||
class DeleteMember(neutronV20.DeleteCommand):
|
||||
"""Delete a given member."""
|
||||
|
||||
resource = 'member'
|
|
@ -1,124 +0,0 @@
|
|||
# Copyright 2013 Mirantis Inc.
|
||||
# All Rights Reserved
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
|
||||
import six
|
||||
|
||||
from neutronclient._i18n import _
|
||||
from neutronclient.neutron import v2_0 as neutronV20
|
||||
|
||||
|
||||
def _format_provider(pool):
|
||||
return pool.get('provider') or 'N/A'
|
||||
|
||||
|
||||
class ListPool(neutronV20.ListCommand):
|
||||
"""List pools that belong to a given tenant."""
|
||||
|
||||
resource = 'pool'
|
||||
list_columns = ['id', 'name', 'provider', 'lb_method', 'protocol',
|
||||
'admin_state_up', 'status']
|
||||
_formatters = {'provider': _format_provider}
|
||||
pagination_support = True
|
||||
sorting_support = True
|
||||
|
||||
|
||||
class ShowPool(neutronV20.ShowCommand):
|
||||
"""Show information of a given pool."""
|
||||
|
||||
resource = 'pool'
|
||||
|
||||
|
||||
class CreatePool(neutronV20.CreateCommand):
|
||||
"""Create a pool."""
|
||||
|
||||
resource = 'pool'
|
||||
|
||||
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(
|
||||
'--description',
|
||||
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.'))
|
||||
parser.add_argument(
|
||||
'--name',
|
||||
required=True,
|
||||
help=_('The name of the pool.'))
|
||||
parser.add_argument(
|
||||
'--protocol',
|
||||
required=True,
|
||||
choices=['HTTP', 'HTTPS', 'TCP'],
|
||||
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.'))
|
||||
parser.add_argument(
|
||||
'--provider',
|
||||
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 = {'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 {self.resource: body}
|
||||
|
||||
|
||||
class UpdatePool(neutronV20.UpdateCommand):
|
||||
"""Update a given pool."""
|
||||
|
||||
resource = 'pool'
|
||||
|
||||
|
||||
class DeletePool(neutronV20.DeleteCommand):
|
||||
"""Delete a given pool."""
|
||||
|
||||
resource = 'pool'
|
||||
|
||||
|
||||
class RetrievePoolStats(neutronV20.ShowCommand):
|
||||
"""Retrieve stats for a given pool."""
|
||||
|
||||
resource = 'pool'
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
pool_id = neutronV20.find_resourceid_by_name_or_id(
|
||||
self.get_client(), 'pool', parsed_args.id)
|
||||
params = {}
|
||||
if parsed_args.fields:
|
||||
params = {'fields': parsed_args.fields}
|
||||
|
||||
data = neutron_client.retrieve_pool_stats(pool_id, **params)
|
||||
self.format_output_data(data)
|
||||
stats = data['stats']
|
||||
if 'stats' in data:
|
||||
return zip(*sorted(six.iteritems(stats)))
|
||||
else:
|
||||
return None
|
|
@ -1,143 +0,0 @@
|
|||
# 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'
|
|
@ -1,155 +0,0 @@
|
|||
# Copyright 2016 Radware LTD.
|
||||
# All Rights Reserved
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
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'
|
|
@ -1,148 +0,0 @@
|
|||
# Copyright 2016 Radware LTD.
|
||||
# All Rights Reserved
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
|
||||
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'
|
|
@ -1,168 +0,0 @@
|
|||
# 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 vip. 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'
|
|
@ -1,179 +0,0 @@
|
|||
# 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()
|
||||
neutron_client.format = parsed_args.request_format
|
||||
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))
|
|
@ -1,160 +0,0 @@
|
|||
# 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.
|
||||
#
|
||||
import argparse
|
||||
|
||||
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(
|
||||
'--admin-state-down',
|
||||
dest='admin_state', action='store_false',
|
||||
default=argparse.SUPPRESS,
|
||||
help=_('[DEPRECATED in Mitaka] Set admin state up to false.'))
|
||||
parser.add_argument(
|
||||
'pool', metavar='POOL',
|
||||
help=_('ID or name of the pool that this member belongs to.'))
|
||||
utils.add_boolean_argument(
|
||||
parser, '--admin-state-up',
|
||||
dest='admin_state',
|
||||
help=_('Update the administrative state of '
|
||||
'the member (True meaning "Up").'))
|
||||
# ToDo(reedip): After Mitaka, remove admin-state-down
|
||||
_add_common_args(parser)
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
self.parent_id = _get_pool_id(self.get_client(), parsed_args.pool)
|
||||
body = {}
|
||||
if hasattr(parsed_args, 'admin_state'):
|
||||
body['admin_state_up'] = parsed_args.admin_state
|
||||
_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'
|
|
@ -1,180 +0,0 @@
|
|||
# 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):
|
||||
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=True,
|
||||
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',
|
||||
'session_persistence'])
|
||||
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_method', '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'])
|
||||
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").'))
|
||||
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.'))
|
||||
_add_common_args(parser)
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
body = _parse_common_args(parsed_args)
|
||||
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'
|
|
@ -1,104 +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.
|
||||
#
|
||||
|
||||
from neutronclient._i18n import _
|
||||
from neutronclient.neutron import v2_0 as neutronV20
|
||||
|
||||
|
||||
class ListVip(neutronV20.ListCommand):
|
||||
"""List vips that belong to a given tenant."""
|
||||
|
||||
resource = 'vip'
|
||||
list_columns = ['id', 'name', 'algorithm', 'address', 'protocol',
|
||||
'admin_state_up', 'status']
|
||||
pagination_support = True
|
||||
sorting_support = True
|
||||
|
||||
|
||||
class ShowVip(neutronV20.ShowCommand):
|
||||
"""Show information of a given vip."""
|
||||
|
||||
resource = 'vip'
|
||||
|
||||
|
||||
class CreateVip(neutronV20.CreateCommand):
|
||||
"""Create a vip."""
|
||||
|
||||
resource = 'vip'
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'pool_id', metavar='POOL',
|
||||
help=_('ID or name of the pool to which this vip belongs.'))
|
||||
parser.add_argument(
|
||||
'--address',
|
||||
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.'))
|
||||
parser.add_argument(
|
||||
'--connection-limit',
|
||||
help=_('The maximum number of connections per second allowed for '
|
||||
'the vip. Valid values: a positive integer or -1 '
|
||||
'for unlimited (default).'))
|
||||
parser.add_argument(
|
||||
'--description',
|
||||
help=_('Description of the vip to be created.'))
|
||||
parser.add_argument(
|
||||
'--name',
|
||||
required=True,
|
||||
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.'))
|
||||
parser.add_argument(
|
||||
'--protocol',
|
||||
required=True, choices=['TCP', 'HTTP', 'HTTPS'],
|
||||
help=_('Protocol for balancing.'))
|
||||
parser.add_argument(
|
||||
'--subnet-id', metavar='SUBNET',
|
||||
required=True,
|
||||
help=_('The subnet on which to allocate the vip address.'))
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
_pool_id = neutronV20.find_resourceid_by_name_or_id(
|
||||
self.get_client(), 'pool', parsed_args.pool_id)
|
||||
_subnet_id = neutronV20.find_resourceid_by_name_or_id(
|
||||
self.get_client(), 'subnet', parsed_args.subnet_id)
|
||||
|
||||
body = {'pool_id': _pool_id,
|
||||
'admin_state_up': parsed_args.admin_state,
|
||||
'subnet_id': _subnet_id}
|
||||
neutronV20.update_dict(parsed_args, body,
|
||||
['address', 'connection_limit', 'description',
|
||||
'name', 'protocol_port', 'protocol',
|
||||
'tenant_id'])
|
||||
return {self.resource: body}
|
||||
|
||||
|
||||
class UpdateVip(neutronV20.UpdateCommand):
|
||||
"""Update a given vip."""
|
||||
|
||||
resource = 'vip'
|
||||
|
||||
|
||||
class DeleteVip(neutronV20.DeleteCommand):
|
||||
"""Delete a given vip."""
|
||||
|
||||
resource = 'vip'
|
|
@ -1,117 +0,0 @@
|
|||
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# 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
|
||||
|
||||
|
||||
class ListMeteringLabel(neutronv20.ListCommand):
|
||||
"""List metering labels that belong to a given tenant."""
|
||||
|
||||
resource = 'metering_label'
|
||||
list_columns = ['id', 'name', 'description', 'shared']
|
||||
pagination_support = True
|
||||
sorting_support = True
|
||||
|
||||
|
||||
class ShowMeteringLabel(neutronv20.ShowCommand):
|
||||
"""Show information of a given metering label."""
|
||||
|
||||
resource = 'metering_label'
|
||||
allow_names = True
|
||||
|
||||
|
||||
class CreateMeteringLabel(neutronv20.CreateCommand):
|
||||
"""Create a metering label for a given tenant."""
|
||||
|
||||
resource = 'metering_label'
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'name', metavar='NAME',
|
||||
help=_('Name of the metering label to be created.'))
|
||||
parser.add_argument(
|
||||
'--description',
|
||||
help=_('Description of the metering label to be created.'))
|
||||
parser.add_argument(
|
||||
'--shared',
|
||||
action='store_true',
|
||||
help=_('Set the label as shared.'))
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
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."""
|
||||
|
||||
resource = 'metering_label'
|
||||
allow_names = True
|
||||
|
||||
|
||||
class ListMeteringLabelRule(neutronv20.ListCommand):
|
||||
"""List metering labels that belong to a given label."""
|
||||
|
||||
resource = 'metering_label_rule'
|
||||
list_columns = ['id', 'excluded', 'direction', 'remote_ip_prefix']
|
||||
pagination_support = True
|
||||
sorting_support = True
|
||||
|
||||
|
||||
class ShowMeteringLabelRule(neutronv20.ShowCommand):
|
||||
"""Show information of a given metering label rule."""
|
||||
|
||||
resource = 'metering_label_rule'
|
||||
|
||||
|
||||
class CreateMeteringLabelRule(neutronv20.CreateCommand):
|
||||
"""Create a metering label rule for a given label."""
|
||||
|
||||
resource = 'metering_label_rule'
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'label_id', metavar='LABEL',
|
||||
help=_('ID or name of the label.'))
|
||||
parser.add_argument(
|
||||
'remote_ip_prefix', metavar='REMOTE_IP_PREFIX',
|
||||
help=_('CIDR to match on.'))
|
||||
parser.add_argument(
|
||||
'--direction',
|
||||
default='ingress', choices=['ingress', 'egress'],
|
||||
help=_('Direction of traffic, default: ingress.'))
|
||||
parser.add_argument(
|
||||
'--excluded',
|
||||
action='store_true',
|
||||
help=_('Exclude this CIDR from the label, default: not excluded.'))
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
label_id = neutronv20.find_resourceid_by_name_or_id(
|
||||
neutron_client, 'metering_label', parsed_args.label_id)
|
||||
|
||||
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."""
|
||||
|
||||
resource = 'metering_label_rule'
|
|
@ -1,232 +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 argparse
|
||||
|
||||
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 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 (TypeError, KeyError):
|
||||
return ''
|
||||
|
||||
|
||||
class ListNetwork(neutronV20.ListCommand):
|
||||
"""List networks that belong to a given tenant."""
|
||||
|
||||
# Length of a query filter on subnet id
|
||||
# id=<uuid>& (with len(uuid)=36)
|
||||
subnet_id_filter_len = 40
|
||||
resource = 'network'
|
||||
_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 <tag[,tag...]>"),
|
||||
'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 <tag[,tag...]>"),
|
||||
'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 <tag[,tag...]>"),
|
||||
'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 "
|
||||
"<tag[,tag...]>"),
|
||||
'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()
|
||||
search_opts = {'fields': ['id', 'cidr']}
|
||||
if self.pagination_support:
|
||||
page_size = parsed_args.page_size
|
||||
if page_size:
|
||||
search_opts.update({'limit': page_size})
|
||||
subnet_ids = []
|
||||
for n in data:
|
||||
if 'subnets' in n:
|
||||
subnet_ids.extend(n['subnets'])
|
||||
|
||||
def _get_subnet_list(sub_ids):
|
||||
search_opts['id'] = sub_ids
|
||||
return neutron_client.list_subnets(
|
||||
**search_opts).get('subnets', [])
|
||||
|
||||
try:
|
||||
subnets = _get_subnet_list(subnet_ids)
|
||||
except exceptions.RequestURITooLong as uri_len_exc:
|
||||
# The URI is too long because of too many subnet_id filters
|
||||
# Use the excess attribute of the exception to know how many
|
||||
# subnet_id filters can be inserted into a single request
|
||||
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
|
||||
subnets = []
|
||||
for i in range(0, subnet_count, chunk_size):
|
||||
subnets.extend(
|
||||
_get_subnet_list(subnet_ids[i: i + chunk_size]))
|
||||
|
||||
subnet_dict = dict([(s['id'], s) for s in subnets])
|
||||
for n in data:
|
||||
if 'subnets' in n:
|
||||
n['subnets'] = [(subnet_dict.get(s) or {"id": s})
|
||||
for s in n['subnets']]
|
||||
|
||||
|
||||
class ListExternalNetwork(ListNetwork):
|
||||
"""List external networks that belong to a given tenant."""
|
||||
|
||||
pagination_support = True
|
||||
sorting_support = True
|
||||
|
||||
def retrieve_list(self, parsed_args):
|
||||
external = '--router:external=True'
|
||||
if external not in self.values_specs:
|
||||
self.values_specs.append('--router:external=True')
|
||||
return super(ListExternalNetwork, self).retrieve_list(parsed_args)
|
||||
|
||||
|
||||
class ShowNetwork(neutronV20.ShowCommand):
|
||||
"""Show information of a given network."""
|
||||
|
||||
resource = 'network'
|
||||
|
||||
|
||||
class CreateNetwork(neutronV20.CreateCommand, qos_policy.CreateQosPolicyMixin):
|
||||
"""Create a network for a given tenant."""
|
||||
|
||||
resource = 'network'
|
||||
|
||||
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(
|
||||
'--admin_state_down',
|
||||
dest='admin_state', action='store_false',
|
||||
help=argparse.SUPPRESS)
|
||||
parser.add_argument(
|
||||
'--shared',
|
||||
action='store_true',
|
||||
help=_('Set the network as shared.'),
|
||||
default=argparse.SUPPRESS)
|
||||
parser.add_argument(
|
||||
'--provider:network_type',
|
||||
metavar='<network_type>',
|
||||
help=_('The physical mechanism by which the virtual network'
|
||||
' is implemented.'))
|
||||
parser.add_argument(
|
||||
'--provider:physical_network',
|
||||
metavar='<physical_network_name>',
|
||||
help=_('Name of the physical network over which the virtual '
|
||||
'network is implemented.'))
|
||||
parser.add_argument(
|
||||
'--provider:segmentation_id',
|
||||
metavar='<segmentation_id>',
|
||||
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 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 = {'name': parsed_args.name,
|
||||
'admin_state_up': parsed_args.admin_state}
|
||||
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."""
|
||||
|
||||
resource = 'network'
|
||||
|
||||
|
||||
class UpdateNetwork(neutronV20.UpdateCommand, qos_policy.UpdateQosPolicyMixin):
|
||||
"""Update network's information."""
|
||||
|
||||
resource = 'network'
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
self.add_arguments_qos_policy(parser)
|
||||
dns.add_dns_argument_update(parser, self.resource, 'domain')
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
body = {}
|
||||
self.args2body_qos_policy(parsed_args, body)
|
||||
dns.args2body_dns_update(parsed_args, body, 'domain')
|
||||
return {'network': body}
|
|
@ -1,73 +0,0 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
from cliff import show
|
||||
import six
|
||||
|
||||
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(six.iteritems(resource)))
|
||||
else:
|
||||
return None
|
|
@ -1,344 +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 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 import dns
|
||||
from neutronclient.neutron.v2_0.qos import policy as qos_policy
|
||||
|
||||
|
||||
def _format_fixed_ips(port):
|
||||
try:
|
||||
return '\n'.join([jsonutils.dumps(ip) for ip in port['fixed_ips']])
|
||||
except (TypeError, KeyError):
|
||||
return ''
|
||||
|
||||
|
||||
def _format_fixed_ips_csv(port):
|
||||
try:
|
||||
return jsonutils.dumps(port['fixed_ips'])
|
||||
except (TypeError, KeyError):
|
||||
return ''
|
||||
|
||||
|
||||
def _add_updatable_args(parser):
|
||||
parser.add_argument(
|
||||
'--name',
|
||||
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=<name_or_id>,ip_address=<ip>. '
|
||||
'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'
|
||||
_formatters = {'fixed_ips': _format_fixed_ips, }
|
||||
_formatters_csv = {'fixed_ips': _format_fixed_ips_csv, }
|
||||
list_columns = ['id', 'name', 'mac_address', 'fixed_ips']
|
||||
pagination_support = True
|
||||
sorting_support = True
|
||||
|
||||
|
||||
class ListRouterPort(neutronV20.ListCommand):
|
||||
"""List ports that belong to a given tenant, with specified router."""
|
||||
|
||||
resource = 'port'
|
||||
_formatters = {'fixed_ips': _format_fixed_ips, }
|
||||
list_columns = ['id', 'name', 'mac_address', 'fixed_ips']
|
||||
pagination_support = True
|
||||
sorting_support = True
|
||||
|
||||
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 the router to look up.'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
_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).take_action(parsed_args)
|
||||
|
||||
|
||||
class ShowPort(neutronV20.ShowCommand):
|
||||
"""Show information of a given port."""
|
||||
|
||||
resource = 'port'
|
||||
|
||||
|
||||
class UpdatePortSecGroupMixin(object):
|
||||
def add_arguments_secgroup(self, parser):
|
||||
group_sg = parser.add_mutually_exclusive_group()
|
||||
group_sg.add_argument(
|
||||
'--security-group', metavar='SECURITY_GROUP',
|
||||
default=[], action='append', dest='security_groups',
|
||||
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.'))
|
||||
|
||||
def _resolv_sgid(self, secgroup):
|
||||
return neutronV20.find_resourceid_by_name_or_id(
|
||||
self.get_client(), 'security_group', secgroup)
|
||||
|
||||
def args2body_secgroup(self, parsed_args, port):
|
||||
if parsed_args.security_groups:
|
||||
port['security_groups'] = [self._resolv_sgid(sg) for sg
|
||||
in parsed_args.security_groups]
|
||||
elif parsed_args.no_security_groups:
|
||||
port['security_groups'] = []
|
||||
|
||||
|
||||
class UpdateExtraDhcpOptMixin(object):
|
||||
def add_arguments_extradhcpopt(self, parser):
|
||||
group_sg = parser.add_mutually_exclusive_group()
|
||||
group_sg.add_argument(
|
||||
'--extra-dhcp-opt',
|
||||
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=<dhcp_option_name>,opt_value=<value>,'
|
||||
'ip_version={4,6}. You can repeat this option.'))
|
||||
|
||||
def args2body_extradhcpopt(self, parsed_args, port):
|
||||
ops = []
|
||||
if parsed_args.extra_dhcp_opts:
|
||||
# the extra_dhcp_opt params (opt_name & opt_value)
|
||||
# must come in pairs, if there is a parm error
|
||||
# both must be thrown out.
|
||||
opt_ele = {}
|
||||
edo_err_msg = _("Invalid --extra-dhcp-opt option, can only be: "
|
||||
"opt_name=<dhcp_option_name>,opt_value=<value>,"
|
||||
"ip_version={4,6}. "
|
||||
"You can repeat this option.")
|
||||
for opt in parsed_args.extra_dhcp_opts:
|
||||
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['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[,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. '
|
||||
'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, qos_policy.CreateQosPolicyMixin,
|
||||
UpdatePortAllowedAddressPair):
|
||||
"""Create a port for a given tenant."""
|
||||
|
||||
resource = 'port'
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
_add_updatable_args(parser)
|
||||
parser.add_argument(
|
||||
'--admin-state-down',
|
||||
dest='admin_state', action='store_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.'))
|
||||
parser.add_argument(
|
||||
'--mac_address',
|
||||
help=argparse.SUPPRESS)
|
||||
parser.add_argument(
|
||||
'--vnic-type',
|
||||
metavar='<direct | direct-physical | macvtap '
|
||||
'| normal | baremetal>',
|
||||
choices=['direct', 'direct-physical', 'macvtap',
|
||||
'normal', 'baremetal'],
|
||||
help=_('VNIC type for this port.'))
|
||||
parser.add_argument(
|
||||
'--vnic_type',
|
||||
choices=['direct', 'direct-physical', 'macvtap',
|
||||
'normal', 'baremetal'],
|
||||
help=argparse.SUPPRESS)
|
||||
parser.add_argument(
|
||||
'--binding-profile',
|
||||
help=_('Custom data to be passed as binding:profile.'))
|
||||
parser.add_argument(
|
||||
'--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=_('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(
|
||||
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'
|
||||
|
||||
|
||||
class UpdatePort(neutronV20.UpdateCommand, UpdatePortSecGroupMixin,
|
||||
UpdateExtraDhcpOptMixin, qos_policy.UpdateQosPolicyMixin,
|
||||
UpdatePortAllowedAddressPair):
|
||||
"""Update port's information."""
|
||||
|
||||
resource = 'port'
|
||||
|
||||
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 = {}
|
||||
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}
|
|
@ -1,150 +0,0 @@
|
|||
# Copyright 2016 Cisco Systems
|
||||
# All Rights Reserved
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
import sys
|
||||
|
||||
from neutronclient._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))
|
|
@ -1,101 +0,0 @@
|
|||
# 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=_('max bandwidth in kbps.'))
|
||||
parser.add_argument(
|
||||
'--max-burst-kbps',
|
||||
help=_('max 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
|
|
@ -1,112 +0,0 @@
|
|||
# Copyright 2016 Comcast, Inc.
|
||||
# All Rights Reserved
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
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
|
|
@ -1,160 +0,0 @@
|
|||
# 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'
|
|
@ -1,63 +0,0 @@
|
|||
# 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
|
|
@ -1,243 +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.
|
||||
#
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
|
||||
from cliff import lister
|
||||
from cliff import show
|
||||
from oslo_serialization import jsonutils
|
||||
import six
|
||||
|
||||
from neutronclient._i18n import _
|
||||
from neutronclient.common import exceptions
|
||||
from neutronclient.common import utils
|
||||
from neutronclient.neutron import v2_0 as neutronV20
|
||||
|
||||
|
||||
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."""
|
||||
|
||||
resource = 'quota'
|
||||
|
||||
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.'))
|
||||
parser.add_argument(
|
||||
'--tenant_id',
|
||||
help=argparse.SUPPRESS)
|
||||
parser.add_argument(
|
||||
'pos_tenant_id',
|
||||
help=argparse.SUPPRESS, nargs='?')
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
tenant_id = get_tenant_id(parsed_args, neutron_client)
|
||||
obj_deleter = getattr(neutron_client,
|
||||
"delete_%s" % self.resource)
|
||||
obj_deleter(tenant_id)
|
||||
print((_('Deleted %(resource)s: %(tenant_id)s')
|
||||
% {'tenant_id': tenant_id,
|
||||
'resource': self.resource}),
|
||||
file=self.app.stdout)
|
||||
return
|
||||
|
||||
|
||||
class ListQuota(neutronV20.NeutronCommand, lister.Lister):
|
||||
"""List quotas of all tenants who have non-default quota values."""
|
||||
|
||||
resource = 'quota'
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListQuota, self).get_parser(prog_name)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
search_opts = {}
|
||||
self.log.debug('search options: %s', search_opts)
|
||||
obj_lister = getattr(neutron_client,
|
||||
"list_%ss" % self.resource)
|
||||
data = obj_lister(**search_opts)
|
||||
info = []
|
||||
collection = self.resource + "s"
|
||||
if collection in data:
|
||||
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))
|
||||
|
||||
|
||||
class ShowQuota(neutronV20.NeutronCommand, show.ShowOne):
|
||||
"""Show quotas of a given tenant.
|
||||
|
||||
"""
|
||||
resource = "quota"
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ShowQuota, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'--tenant-id', metavar='tenant-id',
|
||||
help=_('The owner tenant ID.'))
|
||||
parser.add_argument(
|
||||
'--tenant_id',
|
||||
help=argparse.SUPPRESS)
|
||||
# allow people to do neutron quota-show <tenant-id>.
|
||||
# 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 take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
tenant_id = get_tenant_id(parsed_args, neutron_client)
|
||||
params = {}
|
||||
obj_shower = getattr(neutron_client,
|
||||
"show_%s" % self.resource)
|
||||
data = obj_shower(tenant_id, **params)
|
||||
if self.resource in data:
|
||||
for k, v in six.iteritems(data[self.resource]):
|
||||
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 is None:
|
||||
data[self.resource][k] = ''
|
||||
return zip(*sorted(six.iteritems(data[self.resource])))
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
class UpdateQuota(neutronV20.NeutronCommand, show.ShowOne):
|
||||
"""Define tenant's quotas not to use defaults."""
|
||||
|
||||
resource = 'quota'
|
||||
|
||||
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.'))
|
||||
parser.add_argument(
|
||||
'--tenant_id',
|
||||
help=argparse.SUPPRESS)
|
||||
parser.add_argument(
|
||||
'--network', metavar='networks',
|
||||
help=_('The limit of networks.'))
|
||||
parser.add_argument(
|
||||
'--subnet', metavar='subnets',
|
||||
help=_('The limit of subnets.'))
|
||||
parser.add_argument(
|
||||
'--port', metavar='ports',
|
||||
help=_('The limit of ports.'))
|
||||
parser.add_argument(
|
||||
'--router', metavar='routers',
|
||||
help=_('The limit of routers.'))
|
||||
parser.add_argument(
|
||||
'--floatingip', metavar='floatingips',
|
||||
help=_('The limit of floating IPs.'))
|
||||
parser.add_argument(
|
||||
'--security-group', metavar='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.'))
|
||||
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',
|
||||
help=_('The limit of health monitors.'))
|
||||
parser.add_argument(
|
||||
'pos_tenant_id',
|
||||
help=argparse.SUPPRESS, nargs='?')
|
||||
|
||||
return parser
|
||||
|
||||
def _validate_int(self, name, value):
|
||||
try:
|
||||
return_value = int(value)
|
||||
except Exception:
|
||||
message = (_('Quota limit for %(name)s must be an integer') %
|
||||
{'name': name})
|
||||
raise exceptions.NeutronClientException(message=message)
|
||||
return return_value
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
quota = {}
|
||||
for resource in ('network', 'subnet', 'port', 'router', 'floatingip',
|
||||
'security_group', 'security_group_rule',
|
||||
'vip', 'pool', 'member', 'health_monitor'):
|
||||
if getattr(parsed_args, resource):
|
||||
quota[resource] = self._validate_int(
|
||||
resource,
|
||||
getattr(parsed_args, resource))
|
||||
return {self.resource: quota}
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
_extra_values = neutronV20.parse_args_to_dict(self.values_specs)
|
||||
neutronV20._merge_args(self, parsed_args, _extra_values,
|
||||
self.values_specs)
|
||||
body = self.args2body(parsed_args)
|
||||
if self.resource in body:
|
||||
body[self.resource].update(_extra_values)
|
||||
else:
|
||||
body[self.resource] = _extra_values
|
||||
obj_updator = getattr(neutron_client,
|
||||
"update_%s" % self.resource)
|
||||
tenant_id = get_tenant_id(parsed_args, neutron_client)
|
||||
data = obj_updator(tenant_id, body)
|
||||
if self.resource in data:
|
||||
for k, v in six.iteritems(data[self.resource]):
|
||||
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 is None:
|
||||
data[self.resource][k] = ''
|
||||
return zip(*sorted(six.iteritems(data[self.resource])))
|
||||
else:
|
||||
return None
|
|
@ -1,115 +0,0 @@
|
|||
# Copyright 2015 Huawei Technologies India Pvt Ltd.
|
||||
# All Rights Reserved
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutronclient._i18n import _
|
||||
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,
|
||||
help=_('Type of the object that RBAC policy affects.'))
|
||||
parser.add_argument(
|
||||
'--target-tenant',
|
||||
help=_('ID of the tenant to which the RBAC '
|
||||
'policy will be enforced.'))
|
||||
parser.add_argument(
|
||||
'--action', choices=['access_as_external', 'access_as_shared'],
|
||||
required=True,
|
||||
help=_('Action for the RBAC policy.'))
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
neutron_client.format = parsed_args.request_format
|
||||
_object_id, _object_type = get_rbac_obj_params(neutron_client,
|
||||
parsed_args.type,
|
||||
parsed_args.name)
|
||||
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
|
|
@ -1,287 +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.
|
||||
#
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
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 import availability_zone
|
||||
|
||||
|
||||
def _format_external_gateway_info(router):
|
||||
try:
|
||||
return jsonutils.dumps(router['external_gateway_info'])
|
||||
except (TypeError, KeyError):
|
||||
return ''
|
||||
|
||||
|
||||
class ListRouter(neutronV20.ListCommand):
|
||||
"""List routers that belong to a given tenant."""
|
||||
|
||||
resource = 'router'
|
||||
_formatters = {'external_gateway_info': _format_external_gateway_info, }
|
||||
list_columns = ['id', 'name', 'external_gateway_info', 'distributed', 'ha']
|
||||
pagination_support = True
|
||||
sorting_support = True
|
||||
|
||||
|
||||
class ShowRouter(neutronV20.ShowCommand):
|
||||
"""Show information of a given router."""
|
||||
|
||||
resource = 'router'
|
||||
|
||||
|
||||
class CreateRouter(neutronV20.CreateCommand):
|
||||
"""Create a router for a given tenant."""
|
||||
|
||||
resource = 'router'
|
||||
_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.'))
|
||||
parser.add_argument(
|
||||
'--admin_state_down',
|
||||
dest='admin_state', action='store_false',
|
||||
help=argparse.SUPPRESS)
|
||||
parser.add_argument(
|
||||
'name', metavar='NAME',
|
||||
help=_('Name of the router to be created.'))
|
||||
parser.add_argument(
|
||||
'--description',
|
||||
help=_('Description of router.'))
|
||||
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 = {'admin_state_up': parsed_args.admin_state}
|
||||
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."""
|
||||
|
||||
resource = 'router'
|
||||
|
||||
|
||||
class UpdateRouter(neutronV20.UpdateCommand):
|
||||
"""Update router's information."""
|
||||
|
||||
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."""
|
||||
|
||||
resource = 'router'
|
||||
|
||||
def call_api(self, neutron_client, router_id, body):
|
||||
raise NotImplementedError()
|
||||
|
||||
def success_message(self, router_id, portinfo):
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(RouterInterfaceCommand, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'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 a '
|
||||
'subnet.'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
|
||||
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.'))
|
||||
else:
|
||||
resource = 'subnet'
|
||||
value = parsed_args.interface
|
||||
|
||||
_router_id = neutronV20.find_resourceid_by_name_or_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, portinfo),
|
||||
file=self.app.stdout)
|
||||
|
||||
|
||||
class AddInterfaceRouter(RouterInterfaceCommand):
|
||||
"""Add an internal network interface to a router."""
|
||||
|
||||
def call_api(self, neutron_client, router_id, body):
|
||||
return neutron_client.add_interface_router(router_id, body)
|
||||
|
||||
def success_message(self, router_id, portinfo):
|
||||
return (_('Added interface %(port)s to router %(router)s.') %
|
||||
{'router': router_id, 'port': portinfo['port_id']})
|
||||
|
||||
|
||||
class RemoveInterfaceRouter(RouterInterfaceCommand):
|
||||
"""Remove an internal network interface from a router."""
|
||||
|
||||
def call_api(self, neutron_client, router_id, body):
|
||||
return neutron_client.remove_interface_router(router_id, body)
|
||||
|
||||
def success_message(self, router_id, portinfo):
|
||||
# portinfo is not used since it is None for router-interface-delete.
|
||||
return _('Removed interface from router %s.') % router_id
|
||||
|
||||
|
||||
class SetGatewayRouter(neutronV20.NeutronCommand):
|
||||
"""Set the external network gateway for a router."""
|
||||
|
||||
resource = 'router'
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(SetGatewayRouter, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'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(
|
||||
'--disable-snat', action='store_true',
|
||||
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=<name_or_id>,ip_address=<ip>. '
|
||||
'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 take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
_router_id = neutronV20.find_resourceid_by_name_or_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)
|
||||
router_dict = {'network_id': _ext_net_id}
|
||||
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,
|
||||
file=self.app.stdout)
|
||||
|
||||
|
||||
class RemoveGatewayRouter(neutronV20.NeutronCommand):
|
||||
"""Remove an external network gateway from a router."""
|
||||
|
||||
resource = 'router'
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(RemoveGatewayRouter, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'router', metavar='ROUTER',
|
||||
help=_('ID or name of the router.'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
neutron_client = self.get_client()
|
||||
_router_id = neutronV20.find_resourceid_by_name_or_id(
|
||||
neutron_client, self.resource, parsed_args.router)
|
||||
neutron_client.remove_gateway_router(_router_id)
|
||||
print(_('Removed gateway from router %s') % parsed_args.router,
|
||||
file=self.app.stdout)
|
|
@ -1,376 +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 argparse
|
||||
|
||||
from neutronclient._i18n import _
|
||||
from neutronclient.common import exceptions
|
||||
from neutronclient.neutron import v2_0 as neutronV20
|
||||
|
||||
|
||||
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'
|
||||
list_columns = ['id', 'name', 'security_group_rules']
|
||||
_formatters = {'security_group_rules': _format_sg_rules}
|
||||
pagination_support = True
|
||||
sorting_support = True
|
||||
|
||||
|
||||
class ShowSecurityGroup(neutronV20.ShowCommand):
|
||||
"""Show information of a given security group."""
|
||||
|
||||
resource = 'security_group'
|
||||
allow_names = True
|
||||
json_indent = 5
|
||||
|
||||
|
||||
class CreateSecurityGroup(neutronV20.CreateCommand):
|
||||
"""Create a security group."""
|
||||
|
||||
resource = 'security_group'
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'name', metavar='NAME',
|
||||
help=_('Name of the security group to be created.'))
|
||||
parser.add_argument(
|
||||
'--description',
|
||||
help=_('Description of the security group to be created.'))
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
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."""
|
||||
|
||||
resource = 'security_group'
|
||||
allow_names = True
|
||||
|
||||
|
||||
class UpdateSecurityGroup(neutronV20.UpdateCommand):
|
||||
"""Update a given security group."""
|
||||
|
||||
resource = 'security_group'
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'--name',
|
||||
help=_('Updated name of the security group.'))
|
||||
parser.add_argument(
|
||||
'--description',
|
||||
help=_('Updated description of the security group.'))
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
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'
|
||||
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
|
||||
|
||||
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.'))
|
||||
return parser
|
||||
|
||||
@staticmethod
|
||||
def replace_columns(cols, rules, reverse=False):
|
||||
if reverse:
|
||||
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.get_required_fields(parsed_args.fields)
|
||||
return super(ListSecurityGroupRule, self).retrieve_list(parsed_args)
|
||||
|
||||
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:
|
||||
if page_size:
|
||||
search_opts.update({'limit': page_size})
|
||||
sec_group_ids = set()
|
||||
for rule in data:
|
||||
for key in self.replace_rules:
|
||||
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=<uuid>& (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:
|
||||
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)
|
||||
# NOTE(amotoki): 2nd element of the tuple returned by setup_columns()
|
||||
# is a generator, so if you need to create a look using the generator
|
||||
# object, you need to recreate a generator to show a list expectedly.
|
||||
info = super(ListSecurityGroupRule, self).setup_columns(info,
|
||||
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])
|
||||
|
||||
|
||||
class ShowSecurityGroupRule(neutronV20.ShowCommand):
|
||||
"""Show information of a given security group rule."""
|
||||
|
||||
resource = 'security_group_rule'
|
||||
allow_names = False
|
||||
|
||||
|
||||
class CreateSecurityGroupRule(neutronV20.CreateCommand):
|
||||
"""Create a security group rule."""
|
||||
|
||||
resource = 'security_group_rule'
|
||||
|
||||
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=_('ID or name of the security group to '
|
||||
'which the rule is added.'))
|
||||
parser.add_argument(
|
||||
'--direction',
|
||||
default='ingress', choices=['ingress', 'egress'],
|
||||
help=_('Direction of traffic: ingress/egress.'))
|
||||
parser.add_argument(
|
||||
'--ethertype',
|
||||
help=_('IPv4/IPv6'))
|
||||
parser.add_argument(
|
||||
'--protocol',
|
||||
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. For ICMP it is type.'))
|
||||
parser.add_argument(
|
||||
'--port_range_min',
|
||||
help=argparse.SUPPRESS)
|
||||
parser.add_argument(
|
||||
'--port-range-max',
|
||||
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.'))
|
||||
parser.add_argument(
|
||||
'--remote_ip_prefix',
|
||||
help=argparse.SUPPRESS)
|
||||
parser.add_argument(
|
||||
'--remote-group-id', metavar='REMOTE_GROUP',
|
||||
help=_('ID or name of the remote security group '
|
||||
'to which the rule is applied.'))
|
||||
parser.add_argument(
|
||||
'--remote_group_id',
|
||||
help=argparse.SUPPRESS)
|
||||
|
||||
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_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['remote_group_id'] = _remote_group_id
|
||||
return {'security_group_rule': body}
|
||||
|
||||
|
||||
class DeleteSecurityGroupRule(neutronV20.DeleteCommand):
|
||||
"""Delete a given security group rule."""
|
||||
|
||||
resource = 'security_group_rule'
|
||||
allow_names = False
|
|
@ -1,27 +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 neutronclient.neutron import v2_0 as neutronV20
|
||||
|
||||
|
||||
class ListServiceProvider(neutronV20.ListCommand):
|
||||
"""List service providers."""
|
||||
|
||||
resource = 'service_provider'
|
||||
list_columns = ['service_type', 'name', 'default']
|
||||
_formatters = {}
|
||||
pagination_support = True
|
||||
sorting_support = True
|
|
@ -1,274 +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 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
|
||||
|
||||
|
||||
def _format_allocation_pools(subnet):
|
||||
try:
|
||||
return '\n'.join([jsonutils.dumps(pool) for pool in
|
||||
subnet['allocation_pools']])
|
||||
except (TypeError, KeyError):
|
||||
return ''
|
||||
|
||||
|
||||
def _format_dns_nameservers(subnet):
|
||||
try:
|
||||
return '\n'.join([jsonutils.dumps(server) for server in
|
||||
subnet['dns_nameservers']])
|
||||
except (TypeError, KeyError):
|
||||
return ''
|
||||
|
||||
|
||||
def _format_host_routes(subnet):
|
||||
try:
|
||||
return '\n'.join([jsonutils.dumps(route) for route in
|
||||
subnet['host_routes']])
|
||||
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'
|
||||
_formatters = {'allocation_pools': _format_allocation_pools,
|
||||
'dns_nameservers': _format_dns_nameservers,
|
||||
'host_routes': _format_host_routes, }
|
||||
list_columns = ['id', 'name', 'cidr', 'allocation_pools']
|
||||
pagination_support = True
|
||||
sorting_support = True
|
||||
|
||||
|
||||
class ShowSubnet(neutronV20.ShowCommand):
|
||||
"""Show information of a given subnet."""
|
||||
|
||||
resource = 'subnet'
|
||||
|
||||
|
||||
class CreateSubnet(neutronV20.CreateCommand):
|
||||
"""Create a subnet for a given tenant."""
|
||||
|
||||
resource = 'subnet'
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
add_updatable_arguments(parser)
|
||||
parser.add_argument(
|
||||
'--ip-version',
|
||||
type=int,
|
||||
default=4, choices=[4, 6],
|
||||
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(
|
||||
'network_id', metavar='NETWORK',
|
||||
help=_('Network ID or name this subnet belongs to.'))
|
||||
parser.add_argument(
|
||||
'cidr', nargs='?', metavar='CIDR',
|
||||
help=_('CIDR of subnet to create.'))
|
||||
parser.add_argument(
|
||||
'--ipv6-ra-mode',
|
||||
choices=['dhcpv6-stateful', 'dhcpv6-stateless', 'slaac'],
|
||||
help=_('IPv6 RA (Router Advertisement) mode.'))
|
||||
parser.add_argument(
|
||||
'--ipv6-address-mode',
|
||||
choices=['dhcpv6-stateful', 'dhcpv6-stateless', 'slaac'],
|
||||
help=_('IPv6 address mode.'))
|
||||
parser.add_argument(
|
||||
'--subnetpool', metavar='SUBNETPOOL',
|
||||
help=_('ID or name of subnetpool from which this subnet '
|
||||
'will obtain a CIDR.'))
|
||||
parser.add_argument(
|
||||
'--use-default-subnetpool',
|
||||
action='store_true',
|
||||
help=_('Use default subnetpool for ip_version, if it exists.'))
|
||||
parser.add_argument(
|
||||
'--prefixlen', metavar='PREFIX_LENGTH',
|
||||
help=_('Prefix length for subnet allocation from subnetpool.'))
|
||||
parser.add_argument(
|
||||
'--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 = {'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['tenant_id'] = parsed_args.tenant_id
|
||||
|
||||
return {'subnet': body}
|
||||
|
||||
|
||||
class DeleteSubnet(neutronV20.DeleteCommand):
|
||||
"""Delete a given subnet."""
|
||||
|
||||
resource = 'subnet'
|
||||
|
||||
|
||||
class UpdateSubnet(neutronV20.UpdateCommand):
|
||||
"""Update subnet's information."""
|
||||
|
||||
resource = 'subnet'
|
||||
|
||||
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}
|
|
@ -1,153 +0,0 @@
|
|||
# Copyright 2015 OpenStack Foundation.
|
||||
# All Rights Reserved
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
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)
|
||||
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}
|
|
@ -1,104 +0,0 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutronclient._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']
|
||||
|
||||
|
||||
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()
|
||||
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)
|
|
@ -1,100 +0,0 @@
|
|||
# (c) Copyright 2015 Cisco Systems, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
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'
|
|
@ -1,119 +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.
|
||||
#
|
||||
|
||||
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
|
||||
|
||||
|
||||
class ListIKEPolicy(neutronv20.ListCommand):
|
||||
"""List IKE policies that belong to a tenant."""
|
||||
|
||||
resource = 'ikepolicy'
|
||||
list_columns = ['id', 'name', 'auth_algorithm',
|
||||
'encryption_algorithm', 'ike_version', 'pfs']
|
||||
_formatters = {}
|
||||
pagination_support = True
|
||||
sorting_support = True
|
||||
|
||||
|
||||
class ShowIKEPolicy(neutronv20.ShowCommand):
|
||||
"""Show information of a given IKE policy."""
|
||||
|
||||
resource = 'ikepolicy'
|
||||
help_resource = 'IKE policy'
|
||||
|
||||
|
||||
class CreateIKEPolicy(neutronv20.CreateCommand):
|
||||
"""Create an IKE policy."""
|
||||
|
||||
resource = 'ikepolicy'
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'--description',
|
||||
help=_('Description of the IKE policy'))
|
||||
parser.add_argument(
|
||||
'--auth-algorithm',
|
||||
default='sha1', choices=['sha1', 'sha256'],
|
||||
help=_('Authentication algorithm in lowercase. '
|
||||
'Default:sha1'))
|
||||
parser.add_argument(
|
||||
'--encryption-algorithm',
|
||||
default='aes-128',
|
||||
help=_('Encryption algorithm in lowercase, default:aes-128'))
|
||||
parser.add_argument(
|
||||
'--phase1-negotiation-mode',
|
||||
default='main', choices=['main'],
|
||||
help=_('IKE Phase1 negotiation mode in lowercase, default:main'))
|
||||
parser.add_argument(
|
||||
'--ike-version',
|
||||
default='v1', choices=['v1', 'v2'],
|
||||
help=_('IKE version in lowercase, default:v1'))
|
||||
parser.add_argument(
|
||||
'--pfs',
|
||||
default='group5', choices=['group2', 'group5', 'group14'],
|
||||
help=_('Perfect Forward Secrecy in lowercase, default:group5'))
|
||||
parser.add_argument(
|
||||
'--lifetime',
|
||||
metavar="units=UNITS,value=VALUE",
|
||||
type=utils.str2dict_type(optional_keys=['units', 'value']),
|
||||
help=vpn_utils.lifetime_help("IKE"))
|
||||
parser.add_argument(
|
||||
'name', metavar='NAME',
|
||||
help=_('Name of the IKE policy.'))
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
|
||||
body = {}
|
||||
neutronv20.update_dict(parsed_args, body,
|
||||
['auth_algorithm', 'encryption_algorithm',
|
||||
'phase1_negotiation_mode', 'ike_version',
|
||||
'pfs', 'name', 'description', 'tenant_id'])
|
||||
if parsed_args.lifetime:
|
||||
vpn_utils.validate_lifetime_dict(parsed_args.lifetime)
|
||||
body['lifetime'] = parsed_args.lifetime
|
||||
return {'ikepolicy': body}
|
||||
|
||||
|
||||
class UpdateIKEPolicy(neutronv20.UpdateCommand):
|
||||
"""Update a given IKE policy."""
|
||||
|
||||
resource = 'ikepolicy'
|
||||
help_resource = 'IKE policy'
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'--lifetime',
|
||||
metavar="units=UNITS,value=VALUE",
|
||||
type=utils.str2dict_type(optional_keys=['units', 'value']),
|
||||
help=vpn_utils.lifetime_help("IKE"))
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
|
||||
body = {}
|
||||
if parsed_args.lifetime:
|
||||
vpn_utils.validate_lifetime_dict(parsed_args.lifetime)
|
||||
body['lifetime'] = parsed_args.lifetime
|
||||
return {'ikepolicy': body}
|
||||
|
||||
|
||||
class DeleteIKEPolicy(neutronv20.DeleteCommand):
|
||||
"""Delete a given IKE policy."""
|
||||
|
||||
resource = 'ikepolicy'
|
||||
help_resource = 'IKE policy'
|
|
@ -1,208 +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.
|
||||
#
|
||||
|
||||
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
|
||||
|
||||
|
||||
def _format_peer_cidrs(ipsec_site_connection):
|
||||
try:
|
||||
return '\n'.join([jsonutils.dumps(cidrs) for cidrs in
|
||||
ipsec_site_connection['peer_cidrs']])
|
||||
except (TypeError, KeyError):
|
||||
return ''
|
||||
|
||||
|
||||
class ListIPsecSiteConnection(neutronv20.ListCommand):
|
||||
"""List IPsec site connections that belong to a given tenant."""
|
||||
|
||||
resource = 'ipsec_site_connection'
|
||||
_formatters = {'peer_cidrs': _format_peer_cidrs}
|
||||
list_columns = [
|
||||
'id', 'name', 'peer_address', 'auth_mode', 'status']
|
||||
pagination_support = True
|
||||
sorting_support = True
|
||||
|
||||
|
||||
class ShowIPsecSiteConnection(neutronv20.ShowCommand):
|
||||
"""Show information of a given IPsec site connection."""
|
||||
|
||||
resource = 'ipsec_site_connection'
|
||||
help_resource = 'IPsec site connection'
|
||||
|
||||
|
||||
class IPsecSiteConnectionMixin(object):
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'--dpd',
|
||||
metavar="action=ACTION,interval=INTERVAL,timeout=TIMEOUT",
|
||||
type=utils.str2dict_type(
|
||||
optional_keys=['action', 'interval', 'timeout']),
|
||||
help=vpn_utils.dpd_help("IPsec connection."))
|
||||
parser.add_argument(
|
||||
'--local-ep-group',
|
||||
help=_('Local endpoint group ID/name with subnet(s) for '
|
||||
'the IPsec connection.'))
|
||||
parser.add_argument(
|
||||
'--peer-ep-group',
|
||||
help=_('Peer endpoint group ID/name with CIDR(s) for '
|
||||
'the IPsec connection.'))
|
||||
|
||||
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
|
||||
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(
|
||||
'--name',
|
||||
help=_('Set a name for the connection.'))
|
||||
parser.add_argument(
|
||||
'--description',
|
||||
help=_('Set a description for the connection.'))
|
||||
parser.add_argument(
|
||||
'--mtu',
|
||||
default='1500',
|
||||
help=_('MTU size for the connection, default:1500'))
|
||||
parser.add_argument(
|
||||
'--initiator',
|
||||
default='bi-directional', choices=['bi-directional',
|
||||
'response-only'],
|
||||
help=_('Initiator state in lowercase, default:bi-directional'))
|
||||
parser.add_argument(
|
||||
'--vpnservice-id', metavar='VPNSERVICE',
|
||||
required=True,
|
||||
help=_('VPN service instance ID associated with this connection.'))
|
||||
parser.add_argument(
|
||||
'--ikepolicy-id', metavar='IKEPOLICY',
|
||||
required=True,
|
||||
help=_('IKE policy ID associated with this connection.'))
|
||||
parser.add_argument(
|
||||
'--ipsecpolicy-id', metavar='IPSECPOLICY',
|
||||
required=True,
|
||||
help=_('IPsec policy ID associated with this connection.'))
|
||||
parser.add_argument(
|
||||
'--peer-address',
|
||||
required=True,
|
||||
help=_('Peer gateway public IPv4/IPv6 address or FQDN.'))
|
||||
parser.add_argument(
|
||||
'--peer-id',
|
||||
required=True,
|
||||
help=_('Peer router identity for authentication. Can be '
|
||||
'IPv4/IPv6 address, e-mail address, key id, or FQDN.'))
|
||||
parser.add_argument(
|
||||
'--peer-cidr',
|
||||
action='append', dest='peer_cidrs',
|
||||
help=_('[DEPRECATED in Mitaka] Remote subnet(s) in CIDR format. '
|
||||
'Cannot be specified when using endpoint groups. Only '
|
||||
'applicable, if subnet provided for VPN service.'))
|
||||
parser.add_argument(
|
||||
'--psk',
|
||||
required=True,
|
||||
help=_('Pre-shared key string.'))
|
||||
super(CreateIPsecSiteConnection, self).add_known_arguments(parser)
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
_vpnservice_id = neutronv20.find_resourceid_by_name_or_id(
|
||||
self.get_client(), 'vpnservice',
|
||||
parsed_args.vpnservice_id)
|
||||
_ikepolicy_id = neutronv20.find_resourceid_by_name_or_id(
|
||||
self.get_client(), 'ikepolicy',
|
||||
parsed_args.ikepolicy_id)
|
||||
_ipsecpolicy_id = neutronv20.find_resourceid_by_name_or_id(
|
||||
self.get_client(), 'ipsecpolicy',
|
||||
parsed_args.ipsecpolicy_id)
|
||||
if int(parsed_args.mtu) < 68:
|
||||
message = _("Invalid MTU value: MTU must be "
|
||||
"greater than or equal to 68")
|
||||
raise exceptions.CommandError(message)
|
||||
if (bool(parsed_args.local_ep_group) !=
|
||||
bool(parsed_args.peer_ep_group)):
|
||||
message = _("You must specify both local and peer endpoint "
|
||||
"groups.")
|
||||
raise exceptions.CommandError(message)
|
||||
if parsed_args.peer_cidrs and parsed_args.local_ep_group:
|
||||
message = _("You cannot specify both endpoint groups and peer "
|
||||
"CIDR(s).")
|
||||
raise exceptions.CommandError(message)
|
||||
if not parsed_args.peer_cidrs and not parsed_args.local_ep_group:
|
||||
message = _("You must specify endpoint groups or peer CIDR(s).")
|
||||
raise exceptions.CommandError(message)
|
||||
body = {
|
||||
'vpnservice_id': _vpnservice_id,
|
||||
'ikepolicy_id': _ikepolicy_id,
|
||||
'ipsecpolicy_id': _ipsecpolicy_id,
|
||||
'admin_state_up': parsed_args.admin_state_down,
|
||||
}
|
||||
neutronv20.update_dict(parsed_args, body,
|
||||
['peer_id', 'mtu', 'initiator', 'psk',
|
||||
'peer_address'])
|
||||
if parsed_args.name:
|
||||
body['name'] = parsed_args.name
|
||||
if parsed_args.description:
|
||||
body['description'] = parsed_args.description
|
||||
if parsed_args.tenant_id:
|
||||
body['tenant_id'] = parsed_args.tenant_id
|
||||
if parsed_args.peer_cidrs:
|
||||
body['peer_cidrs'] = parsed_args.peer_cidrs
|
||||
return super(CreateIPsecSiteConnection, self).args2body(parsed_args,
|
||||
body)
|
||||
|
||||
|
||||
class UpdateIPsecSiteConnection(IPsecSiteConnectionMixin,
|
||||
neutronv20.UpdateCommand):
|
||||
"""Update a given IPsec site connection."""
|
||||
|
||||
resource = 'ipsec_site_connection'
|
||||
help_resource = 'IPsec site connection'
|
||||
|
||||
|
||||
class DeleteIPsecSiteConnection(neutronv20.DeleteCommand):
|
||||
"""Delete a given IPsec site connection."""
|
||||
|
||||
resource = 'ipsec_site_connection'
|
||||
help_resource = 'IPsec site connection'
|
|
@ -1,118 +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.
|
||||
#
|
||||
|
||||
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
|
||||
|
||||
|
||||
class ListIPsecPolicy(neutronv20.ListCommand):
|
||||
"""List IPsec policies that belong to a given tenant connection."""
|
||||
|
||||
resource = 'ipsecpolicy'
|
||||
list_columns = ['id', 'name', 'auth_algorithm',
|
||||
'encryption_algorithm', 'pfs']
|
||||
_formatters = {}
|
||||
pagination_support = True
|
||||
sorting_support = True
|
||||
|
||||
|
||||
class ShowIPsecPolicy(neutronv20.ShowCommand):
|
||||
"""Show information of a given IPsec policy."""
|
||||
|
||||
resource = 'ipsecpolicy'
|
||||
help_resource = 'IPsec policy'
|
||||
|
||||
|
||||
class CreateIPsecPolicy(neutronv20.CreateCommand):
|
||||
"""Create an IPsec policy."""
|
||||
|
||||
resource = 'ipsecpolicy'
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'--description',
|
||||
help=_('Description of the IPsec policy.'))
|
||||
parser.add_argument(
|
||||
'--transform-protocol',
|
||||
default='esp', choices=['esp', 'ah', 'ah-esp'],
|
||||
help=_('Transform protocol in lowercase, default:esp'))
|
||||
parser.add_argument(
|
||||
'--auth-algorithm',
|
||||
default='sha1', choices=['sha1', 'sha256'],
|
||||
help=_('Authentication algorithm in lowercase, default:sha1'))
|
||||
parser.add_argument(
|
||||
'--encryption-algorithm',
|
||||
default='aes-128',
|
||||
help=_('Encryption algorithm in lowercase, default:aes-128'))
|
||||
parser.add_argument(
|
||||
'--encapsulation-mode',
|
||||
default='tunnel', choices=['tunnel', 'transport'],
|
||||
help=_('Encapsulation mode in lowercase, default:tunnel'))
|
||||
parser.add_argument(
|
||||
'--pfs',
|
||||
default='group5', choices=['group2', 'group5', 'group14'],
|
||||
help=_('Perfect Forward Secrecy in lowercase, default:group5'))
|
||||
parser.add_argument(
|
||||
'--lifetime',
|
||||
metavar="units=UNITS,value=VALUE",
|
||||
type=utils.str2dict_type(optional_keys=['units', 'value']),
|
||||
help=vpn_utils.lifetime_help("IPsec"))
|
||||
parser.add_argument(
|
||||
'name', metavar='NAME',
|
||||
help=_('Name of the IPsec policy.'))
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
|
||||
body = {}
|
||||
neutronv20.update_dict(parsed_args, body,
|
||||
['auth_algorithm', 'encryption_algorithm',
|
||||
'encapsulation_mode', 'transform_protocol',
|
||||
'pfs', 'name', 'description', 'tenant_id'])
|
||||
if parsed_args.lifetime:
|
||||
vpn_utils.validate_lifetime_dict(parsed_args.lifetime)
|
||||
body['lifetime'] = parsed_args.lifetime
|
||||
return {'ipsecpolicy': body}
|
||||
|
||||
|
||||
class UpdateIPsecPolicy(neutronv20.UpdateCommand):
|
||||
"""Update a given IPsec policy."""
|
||||
|
||||
resource = 'ipsecpolicy'
|
||||
help_resource = 'IPsec policy'
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'--lifetime',
|
||||
metavar="units=UNITS,value=VALUE",
|
||||
type=utils.str2dict_type(optional_keys=['units', 'value']),
|
||||
help=vpn_utils.lifetime_help("IPsec"))
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
|
||||
body = {}
|
||||
if parsed_args.lifetime:
|
||||
vpn_utils.validate_lifetime_dict(parsed_args.lifetime)
|
||||
body['lifetime'] = parsed_args.lifetime
|
||||
return {'ipsecpolicy': body}
|
||||
|
||||
|
||||
class DeleteIPsecPolicy(neutronv20.DeleteCommand):
|
||||
"""Delete a given IPsec policy."""
|
||||
|
||||
resource = 'ipsecpolicy'
|
||||
help_resource = 'IPsec policy'
|
|
@ -1,112 +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.
|
||||
#
|
||||
|
||||
|
||||
"""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
|
|
@ -1,94 +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.
|
||||
#
|
||||
|
||||
from neutronclient._i18n import _
|
||||
from neutronclient.neutron import v2_0 as neutronv20
|
||||
|
||||
|
||||
class ListVPNService(neutronv20.ListCommand):
|
||||
"""List VPN service configurations that belong to a given tenant."""
|
||||
|
||||
resource = 'vpnservice'
|
||||
list_columns = [
|
||||
'id', 'name', 'router_id', 'status'
|
||||
]
|
||||
_formatters = {}
|
||||
pagination_support = True
|
||||
sorting_support = True
|
||||
|
||||
|
||||
class ShowVPNService(neutronv20.ShowCommand):
|
||||
"""Show information of a given VPN service."""
|
||||
|
||||
resource = 'vpnservice'
|
||||
help_resource = 'VPN service'
|
||||
|
||||
|
||||
class CreateVPNService(neutronv20.CreateCommand):
|
||||
"""Create a VPN service."""
|
||||
resource = 'vpnservice'
|
||||
|
||||
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 VPN service.'))
|
||||
parser.add_argument(
|
||||
'--description',
|
||||
help=_('Set a description for the VPN service.'))
|
||||
parser.add_argument(
|
||||
'router', metavar='ROUTER',
|
||||
help=_('Router unique identifier for the VPN service.'))
|
||||
parser.add_argument(
|
||||
'subnet', nargs='?', metavar='SUBNET',
|
||||
help=_('[DEPRECATED in Mitaka] Unique identifier for the local '
|
||||
'private subnet.'))
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
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 = {'subnet_id': _subnet_id,
|
||||
'router_id': _router_id,
|
||||
'admin_state_up': parsed_args.admin_state}
|
||||
neutronv20.update_dict(parsed_args, body,
|
||||
['name', 'description',
|
||||
'tenant_id'])
|
||||
|
||||
return {self.resource: body}
|
||||
|
||||
|
||||
class UpdateVPNService(neutronv20.UpdateCommand):
|
||||
"""Update a given VPN service."""
|
||||
|
||||
resource = 'vpnservice'
|
||||
help_resource = 'VPN service'
|
||||
|
||||
|
||||
class DeleteVPNService(neutronv20.DeleteCommand):
|
||||
"""Delete a given VPN service."""
|
||||
|
||||
resource = 'vpnservice'
|
||||
help_resource = 'VPN service'
|
|
@ -1,61 +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
|
||||
|
||||
# TODO(rtheis/amotoki): Add functional test infrastructure for OSC
|
||||
# plugin commands.
|
||||
# TODO(amotoki): Add and update document on OSC pluign.
|
||||
|
||||
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=instance._insecure,
|
||||
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
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue