Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b93e46dfc | ||
|
|
ec0629c98e | ||
|
|
9399c8de37 | ||
|
|
23e759da36 | ||
|
|
16c31a29ff | ||
|
|
e584661305 | ||
|
|
fd38742d52 | ||
|
|
13ab0b98dd |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -17,10 +17,5 @@ run_tests.log
|
||||
.autogenerated
|
||||
.coverage
|
||||
.testrepository/
|
||||
.idea/
|
||||
.tox/
|
||||
.venv/
|
||||
.stestr/
|
||||
|
||||
# Files created by releasenotes build
|
||||
releasenotes/build
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
[gerrit]
|
||||
host=review.opendev.org
|
||||
host=review.openstack.org
|
||||
port=29418
|
||||
project=openstack/python-tackerclient.git
|
||||
defaultbranch=stable/liberty
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
[DEFAULT]
|
||||
test_path=./tackerclient/tests/unit
|
||||
top_path=./
|
||||
4
.testr.conf
Normal file
4
.testr.conf
Normal file
@@ -0,0 +1,4 @@
|
||||
[DEFAULT]
|
||||
test_command=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 ${PYTHON:-python} -m subunit.run discover -t ./ ${OS_TEST_PATH:-./tackerclient/tests/unit/vm} $LISTOPT $IDOPTION
|
||||
test_id_option=--load-list $IDFILE
|
||||
test_list_option=--list
|
||||
@@ -1,7 +0,0 @@
|
||||
- project:
|
||||
templates:
|
||||
- check-requirements
|
||||
- openstack-lower-constraints-jobs
|
||||
- openstack-python3-ussuri-jobs
|
||||
- publish-openstack-docs-pti
|
||||
- release-notes-jobs-python3
|
||||
@@ -1,16 +0,0 @@
|
||||
If you would like to contribute to the development of OpenStack,
|
||||
you must follow the steps in this page:
|
||||
|
||||
https://docs.openstack.org/infra/manual/developers.html
|
||||
|
||||
Once those steps have been completed, changes to OpenStack
|
||||
should be submitted for review via the Gerrit tool, following
|
||||
the workflow documented at:
|
||||
|
||||
https://docs.openstack.org/infra/manual/developers.html#development-workflow
|
||||
|
||||
Pull requests submitted through GitHub will be ignored.
|
||||
|
||||
Bugs should be filed on Launchpad, not GitHub:
|
||||
|
||||
https://bugs.launchpad.net/python-tackerclient
|
||||
26
HACKING.rst
26
HACKING.rst
@@ -1,26 +1,26 @@
|
||||
Tacker Style Commandments
|
||||
=========================
|
||||
================================
|
||||
|
||||
- Step 1: Read the OpenStack Style Commandments
|
||||
https://docs.openstack.org/hacking/latest
|
||||
http://docs.openstack.org/developer/hacking/
|
||||
- Step 2: Read on
|
||||
|
||||
|
||||
Running Tests
|
||||
-------------
|
||||
The testing system is based on a combination of tox and stestr. The canonical
|
||||
approach to running tests is to simply run the command ``tox``. This will
|
||||
create virtual environments, populate them with dependencies and run all of
|
||||
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
|
||||
``stestr run``, but is set up such that you can supply any additional
|
||||
stestr arguments that are needed to tox. For example, you can run:
|
||||
``tox -- --analyze-isolation`` to cause tox to tell stestr to add
|
||||
`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 stestr
|
||||
command directly. Running ``stestr run`` will run the entire test suite.
|
||||
``stestr run --concurrency=1`` will run tests serially (by default, stestr runs
|
||||
tests in parallel). More information about stestr can be found at:
|
||||
http://stestr.readthedocs.io/
|
||||
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
|
||||
|
||||
70
README.rst
70
README.rst
@@ -1,69 +1 @@
|
||||
========================
|
||||
Team and repository tags
|
||||
========================
|
||||
|
||||
.. image:: https://governance.openstack.org/tc/badges/python-tackerclient.svg
|
||||
:target: https://governance.openstack.org/tc/reference/tags/index.html
|
||||
|
||||
.. Change things from this point on
|
||||
|
||||
NFV Orchestration (Tacker) Client
|
||||
=================================
|
||||
|
||||
CLI and Client Library for OpenStack Tacker
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
**Note:** The paths we are using for configuration files in these steps
|
||||
are with reference to Ubuntu Operating System. The paths may vary for
|
||||
other Operating Systems.
|
||||
|
||||
The branch_name which is used in commands, specify the branch_name
|
||||
as stable/<branch> for any stable branch installation. For eg:
|
||||
stable/queens, stable/pike. If unspecified the default will be
|
||||
master branch.
|
||||
|
||||
Using python install
|
||||
--------------------
|
||||
Clone python-tackerclient repository.
|
||||
|
||||
::
|
||||
|
||||
$ cd ~/
|
||||
$ git clone https://github.com/openstack/python-tackerclient -b <branch_name>
|
||||
|
||||
|
||||
Install python-tackerclient.
|
||||
|
||||
::
|
||||
|
||||
$ cd python-tackerclient
|
||||
$ sudo python setup.py install
|
||||
|
||||
|
||||
Using pip
|
||||
---------
|
||||
|
||||
You can also install the latest version by using ``pip`` command:
|
||||
|
||||
::
|
||||
|
||||
$ pip install python-tackerclient
|
||||
|
||||
|
||||
Or, if it is needed to install ``python-tackerclient`` from master branch,
|
||||
type
|
||||
|
||||
::
|
||||
|
||||
$ pip install git+https://github.com/openstack/python-tackerclient.git
|
||||
|
||||
|
||||
More Information
|
||||
================
|
||||
|
||||
* Python-tackerclient documentation: https://docs.openstack.org/python-tackerclient/latest/
|
||||
* Tacker Documentation: https://docs.openstack.org/tacker/latest/
|
||||
* Tacker Wiki: https://wiki.openstack.org/wiki/Tacker
|
||||
* Release Notes: https://docs.openstack.org/releasenotes/python-tackerclient
|
||||
This is the client API library for Tacker.
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
# The order of packages is significant, because pip processes them in the order
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
# These are needed for docs generation
|
||||
sphinx!=1.6.6,!=1.6.7,>=1.6.2,<2.0.0;python_version=='2.7' # BSD
|
||||
sphinx!=1.6.6,!=1.6.7,!=2.1.0,>=1.6.2;python_version>='3.4' # BSD
|
||||
openstackdocstheme>=1.18.1 # Apache-2.0
|
||||
reno>=2.5.0 # Apache-2.0
|
||||
@@ -1,85 +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 List
|
||||
=============
|
||||
|
||||
The following list covers the extended commands for Tacker services
|
||||
available in **openstack** command.
|
||||
|
||||
These commands can be referenced by doing **openstack help** and the detail
|
||||
of individual command can be referred by **openstack help <command-name>**.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
openstack vnf create Create a VNF.
|
||||
openstack vnf delete Delete given VNF(s).
|
||||
openstack vnf list List VNF(s) that belong to a given tenant.
|
||||
openstack vnf resource list List resources of a VNF like VDU, CP, etc.
|
||||
openstack vnf scale Scale a VNF.
|
||||
openstack vnf show Show information of a given VNF.
|
||||
openstack vnf set Update a given VNF.
|
||||
openstack vnf descriptor create Create a VNFD.
|
||||
openstack vnf descriptor delete Delete given VNFD(s).
|
||||
openstack vnf descriptor list List VNFD(s) that belong to a given tenant.
|
||||
openstack vnf descriptor show Show information of a given VNFD.
|
||||
openstack vnf descriptor template show Show template of a given VNFD.
|
||||
openstack vim list List VIM(s) that belong to a given tenant.
|
||||
openstack vim register Create a VIM.
|
||||
openstack vim show Show information of a given VIM.
|
||||
openstack vim set Update a given VIM.
|
||||
openstack vim delete Delete given VIM(s).
|
||||
openstack ns create Create a NS.
|
||||
openstack ns delete Delete given NS(s).
|
||||
openstack ns list List NS that belong to a given tenant.
|
||||
openstack ns show Show information of a given NS.
|
||||
openstack ns descriptor create Create a NSD.
|
||||
openstack ns descriptor delete Delete a given NSD.
|
||||
openstack ns descriptor list List NSD(s) that belong to a given tenant.
|
||||
openstack ns descriptor show Show information of a given NSD.
|
||||
openstack ns descriptor template show Show template of a given NSD.
|
||||
openstack vnf graph create Create a VNFFG.
|
||||
openstack vnf graph delete Delete a given VNFFG.
|
||||
openstack vnf graph list List VNFFG(s) that belong to a given tenant.
|
||||
openstack vnf graph show Show information of a given VNFFG.
|
||||
openstack vnf graph set Update a given VNFFG.
|
||||
openstack vnf graph descriptor create Create a VNFFGD.
|
||||
openstack vnf graph descriptor delete Delete a given VNFFGD.
|
||||
openstack vnf graph descriptor list List VNFFGD(s) that belong to a given tenant.
|
||||
openstack vnf graph descriptor show Show information of a given VNFFGD.
|
||||
openstack vnf graph descriptor template show Show template of a given VNFFGD.
|
||||
openstack vnf chain list List SFC(s) that belong to a given tenant.
|
||||
openstack vnf chain show Show information of a given SFC.
|
||||
openstack vnf classifier list List FC(s) that belong to a given tenant.
|
||||
openstack vnf classifier show Show information of a given FC.
|
||||
openstack vnf network forwarding path list List NFP(s) that belong to a given tenant.
|
||||
openstack vnf network forwarding path show Show information of a given NFP.
|
||||
openstack nfv event show Show event given the event id.
|
||||
openstack nfv event list List events of resources.
|
||||
openstack vnf package create Create a new individual VNF package resource.
|
||||
openstack vnf package list List all VNF packages.
|
||||
openstack vnf package show Show package details.
|
||||
openstack vnf package upload Upload a VNF package by providing the address information
|
||||
of the VNF package.
|
||||
openstack vnf package delete Delete given VNF package(s).
|
||||
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
============
|
||||
CLI Usage
|
||||
============
|
||||
|
||||
.. toctree::
|
||||
:glob:
|
||||
:maxdepth: 3
|
||||
|
||||
*
|
||||
@@ -1,9 +0,0 @@
|
||||
====================
|
||||
VNF Package commands
|
||||
====================
|
||||
|
||||
VNF Package commands are CLI interface of VNF Package Management Interface in
|
||||
`ETSI NFV-SOL 005 <https://www.etsi.org/deliver/etsi_gs/NFV-SOL/001_099/005/02.06.01_60/gs_NFV-SOL005v020601p.pdf>`_.
|
||||
|
||||
.. autoprogram-cliff:: openstack.tackerclient.v1
|
||||
:command: vnf package *
|
||||
@@ -1,38 +1,13 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
# python-tackerclient documentation build configuration file
|
||||
project = 'python-tackerclient'
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
# sys.path.append(os.path.abspath('.'))
|
||||
|
||||
sys.path.insert(0, os.path.abspath('../..'))
|
||||
|
||||
# -- General configuration ----------------------------------------------------
|
||||
# -- 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',
|
||||
'reno.sphinxext',
|
||||
'openstackdocstheme',
|
||||
'cliff.sphinxext',
|
||||
]
|
||||
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
@@ -44,8 +19,7 @@ source_suffix = '.rst'
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = 'python-tackerclient'
|
||||
copyright = 'OpenStack Contributors'
|
||||
copyright = u'OpenStack Foundation'
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
add_function_parentheses = True
|
||||
@@ -57,32 +31,25 @@ add_module_names = True
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# -- Options for HTML output --------------------------------------------------
|
||||
# -- 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 = 'openstackdocs'
|
||||
html_theme = 'nature'
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'tackerclientdoc'
|
||||
htmlhelp_basename = '%sdoc' % project
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
html_last_updated_fmt = '%Y-%m-%d %H:%M'
|
||||
|
||||
# -- Options for manual page output -------------------------------------------
|
||||
|
||||
man_pages = [
|
||||
('cli/index', 'tacker', u'Client for Tacker API',
|
||||
[u'OpenStack Contributors'], 1),
|
||||
# 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'),
|
||||
]
|
||||
|
||||
# -- Options for openstackdocstheme -------------------------------------------
|
||||
|
||||
repository_name = 'openstack/python-tackerclient'
|
||||
bug_project = 'python-tackerclient'
|
||||
bug_tag = 'doc'
|
||||
|
||||
# -- Options for cliff.sphinxext plugin ---------------------------------------
|
||||
|
||||
autoprogram_cliff_application = 'openstack'
|
||||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
intersphinx_mapping = {'http://docs.python.org/': None}
|
||||
|
||||
@@ -1,26 +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.)
|
||||
|
||||
============
|
||||
Contributing
|
||||
============
|
||||
|
||||
.. include:: ../../../CONTRIBUTING.rst
|
||||
@@ -1,195 +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.)
|
||||
|
||||
===================================
|
||||
Developing with Python-TackerClient
|
||||
===================================
|
||||
|
||||
Project Info
|
||||
============
|
||||
|
||||
* **Free software:** under the `Apache license <http://www.apache.org/licenses/LICENSE-2.0>`_
|
||||
* **Tacker Service:** https://opendev.org/openstack/tacker
|
||||
* **Tacker Client Library:** https://opendev.org/openstack/python-tackerclient
|
||||
* **Tacker Service Bugs:** https://bugs.launchpad.net/tacker
|
||||
* **Client Bugs:** https://bugs.launchpad.net/python-tackerclient
|
||||
* **Blueprints:** https://blueprints.launchpad.net/tacker
|
||||
|
||||
Meetings
|
||||
========
|
||||
For details please refer to the `OpenStack IRC meetings`_ page.
|
||||
|
||||
.. _`OpenStack IRC meetings`: http://eavesdrop.openstack.org/#Tacker_(NFV_Orchestrator_and_VNF_Manager)_Team_Meeting
|
||||
|
||||
Testing
|
||||
=======
|
||||
|
||||
Install the prerequisites for Tox:
|
||||
|
||||
* On Ubuntu or Debian:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ apt-get install gcc gettext python-dev libxml2-dev libxslt1-dev \
|
||||
zlib1g-dev
|
||||
|
||||
You may need to use pip install for some packages.
|
||||
|
||||
|
||||
* On RHEL or CentOS including Fedora:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ yum install gcc python-devel libxml2-devel libxslt-devel
|
||||
|
||||
* On openSUSE or SUSE linux Enterprise:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ zypper install gcc python-devel libxml2-devel libxslt-devel
|
||||
|
||||
Install python-tox:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pip install tox
|
||||
|
||||
To run the full suite of tests maintained within TackerClient.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ tox
|
||||
|
||||
.. NOTE::
|
||||
|
||||
The first time you run ``tox``, it will take additional time to build
|
||||
virtualenvs. You can later use the ``-r`` option with ``tox`` to rebuild
|
||||
your virtualenv in a similar manner.
|
||||
|
||||
|
||||
To run tests for one or more specific test environments(for example, the
|
||||
most common configuration of Python 2.7, Python 3.5 and PEP-8), list the
|
||||
environments with the ``-e`` option, separated by spaces:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ tox -e py27,py35,pep8
|
||||
|
||||
See ``tox.ini`` for the full list of available test environments.
|
||||
|
||||
Building the Documentation
|
||||
==========================
|
||||
|
||||
The documentation is generated with Sphinx using the ``tox`` command. To
|
||||
create HTML docs, run the commands:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ tox -e docs
|
||||
|
||||
The resultant HTML will be in the ``doc/build/html`` directory.
|
||||
|
||||
Release Notes
|
||||
=============
|
||||
|
||||
The release notes for a patch should be included in the patch. See the
|
||||
`Project Team Guide`_ for more information on using reno in OpenStack.
|
||||
|
||||
.. _`Project Team Guide`: http://docs.openstack.org/project-team-guide/release-management.html#managing-release-notes
|
||||
|
||||
If any of the following applies to the patch, a release note is required:
|
||||
|
||||
* The deployer needs to take an action when upgrading
|
||||
* The plugin interface changes
|
||||
* A new feature is implemented
|
||||
* A command or option is removed
|
||||
* Current behavior is changed
|
||||
* A security bug is fixed
|
||||
|
||||
Reno is used to generate release notes. Use the commands:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ tox -e venv -- reno new <bug-,bp-,whatever>
|
||||
|
||||
Then edit the sample file that was created and push it with your change.
|
||||
|
||||
To run the commands and see results:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ git commit # Commit the change because reno scans git log.
|
||||
|
||||
$ tox -e releasenotes
|
||||
|
||||
At last, look at the generated release notes
|
||||
files in ``releasenotes/build/html`` in your browser.
|
||||
|
||||
Testing new code
|
||||
================
|
||||
|
||||
If a developer wants to test new code (feature, command or option) that
|
||||
they have written, Python-TackerClient may be installed from source by running
|
||||
the following commands in the base directory of the project:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ python setup.py install
|
||||
|
||||
or
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pip install -e .
|
||||
|
||||
Standardize Import Format
|
||||
=========================
|
||||
|
||||
.. _`Import Order Guide`: https://docs.openstack.org/hacking/latest/user/hacking.html#imports
|
||||
|
||||
The import order shows below:
|
||||
|
||||
* {{stdlib imports in human alphabetical order}}
|
||||
* \n
|
||||
* {{third-party lib imports in human alphabetical order}}
|
||||
* \n
|
||||
* {{project imports in human alphabetical order}}
|
||||
* \n
|
||||
* \n
|
||||
* {{begin your code}}
|
||||
|
||||
Example
|
||||
~~~~~~~
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import copy
|
||||
import fixtures
|
||||
import mock
|
||||
import os
|
||||
|
||||
from osc_lib.api import auth
|
||||
from osc_lib import utils
|
||||
import six
|
||||
|
||||
from openstackclient import shell
|
||||
from openstackclient.tests import utils
|
||||
|
||||
@@ -1,36 +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.)
|
||||
|
||||
=================
|
||||
Contributor Guide
|
||||
=================
|
||||
|
||||
In the Contributor Guide, you will find information on tackerclient's
|
||||
lower level programming details or APIs as well as the transition to
|
||||
OpenStack client.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
contributing.rst
|
||||
developing.rst
|
||||
|
||||
|
||||
@@ -1,56 +1,25 @@
|
||||
..
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
not use this file except in compliance with the License. You may obtain
|
||||
a copy of the License at
|
||||
Python bindings to the OpenStack Tacker API
|
||||
============================================
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
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-TackerClient Documentation
|
||||
=================================
|
||||
|
||||
This is a client for OpenStack NFV MANO (Tacker) API. It provides
|
||||
:doc:`Python API bindings <reference/index>` (the tackerclient module) and
|
||||
:doc:`command-line interface (CLI) <cli/index>`.
|
||||
|
||||
Contents
|
||||
--------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
install/index
|
||||
cli/index
|
||||
contributor/index
|
||||
reference/index
|
||||
In order to use the python tacker client directly, you must first obtain an auth token and identify which endpoint you wish to speak to. Once you have done so, you can use the API.
|
||||
|
||||
|
||||
Release Notes
|
||||
-------------
|
||||
Command-line Tool
|
||||
=================
|
||||
In order to use the CLI, you must provide your OpenStack username, password, tenant, and auth endpoint. Use the corresponding configuration options (``--os-username``, ``--os-password``, ``--os-tenant-name``, and ``--os-auth-url``) or set them in environment variables::
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
export OS_USERNAME=user
|
||||
export OS_PASSWORD=pass
|
||||
export OS_TENANT_NAME=tenant
|
||||
export OS_AUTH_URL=http://auth.example.com:5000/v2.0
|
||||
|
||||
Release Notes <https://docs.openstack.org/releasenotes/python-tackerclient/>
|
||||
The command line tool will attempt to reauthenticate using your provided credentials for every request. You can override this behavior by manually supplying an auth token using ``--os-url`` and ``--os-auth-token``. You can alternatively set these environment variables::
|
||||
|
||||
export OS_URL=http://tacker.example.org:8888/
|
||||
export OS_TOKEN=3bcc3d3a03f44e3d8377f9247b0ad155
|
||||
|
||||
Indices and Tables
|
||||
------------------
|
||||
If tacker 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::
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
export OS_AUTH_STRATEGY=noauth
|
||||
|
||||
Once you've configured your authentication parameters, you can run ``tacker -h`` to see a complete listing of available commands.
|
||||
|
||||
@@ -1,69 +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.)
|
||||
|
||||
============
|
||||
Installation
|
||||
============
|
||||
|
||||
**Note:** The paths we are using for configuration files in these steps
|
||||
are with reference to Ubuntu Operating System. The paths may vary for
|
||||
other Operating Systems.
|
||||
|
||||
The branch_name which is used in commands, specify the branch_name
|
||||
as stable/<branch> for any stable branch installation. For eg:
|
||||
stable/queens, stable/pike. If unspecified the default will be
|
||||
master branch.
|
||||
|
||||
Using python install
|
||||
====================
|
||||
1. Clone python-tackerclient repository.
|
||||
|
||||
::
|
||||
|
||||
$ cd ~/
|
||||
$ git clone https://github.com/openstack/python-tackerclient -b <branch_name>
|
||||
|
||||
|
||||
2. Install python-tackerclient.
|
||||
|
||||
::
|
||||
|
||||
$ cd python-tackerclient
|
||||
$ sudo python setup.py install
|
||||
|
||||
|
||||
Using pip
|
||||
=========
|
||||
|
||||
You can also install the latest version by using ``pip`` command:
|
||||
|
||||
::
|
||||
|
||||
$ pip install python-tackerclient
|
||||
|
||||
|
||||
Or, if it is needed to install ``python-tackerclient`` from master branch,
|
||||
type
|
||||
|
||||
::
|
||||
|
||||
$ pip install git+https://github.com/openstack/python-tackerclient.git
|
||||
|
||||
@@ -1,26 +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.)
|
||||
|
||||
=========
|
||||
Reference
|
||||
=========
|
||||
|
||||
(To be updated)
|
||||
@@ -1,63 +0,0 @@
|
||||
appdirs==1.3.0
|
||||
Babel==2.3.4
|
||||
cliff==2.8.0
|
||||
cmd2==0.8.0
|
||||
coverage==4.0
|
||||
ddt==1.0.1
|
||||
debtcollector==1.2.0
|
||||
decorator==3.4.0
|
||||
deprecation==1.0
|
||||
dogpile.cache==0.6.2
|
||||
extras==1.0.0
|
||||
fixtures==3.0.0
|
||||
flake8==2.5.5
|
||||
hacking==0.12.0
|
||||
iso8601==0.1.11
|
||||
jmespath==0.9.0
|
||||
jsonpatch==1.16
|
||||
jsonpointer==1.13
|
||||
keystoneauth1==3.4.0
|
||||
linecache2==1.0.0
|
||||
mccabe==0.2.1
|
||||
mock==2.0.0
|
||||
monotonic==0.6
|
||||
msgpack-python==0.4.0
|
||||
munch==2.1.0
|
||||
netaddr==0.7.18
|
||||
netifaces==0.10.4
|
||||
openstacksdk==0.11.2
|
||||
os-client-config==1.28.0
|
||||
os-service-types==1.2.0
|
||||
osc-lib==1.8.0
|
||||
oslo.config==5.2.0
|
||||
oslo.context==2.19.2
|
||||
oslo.i18n==3.15.3
|
||||
oslo.log==3.36.0
|
||||
oslo.serialization==2.18.0
|
||||
oslo.utils==3.33.0
|
||||
pbr==2.0.0
|
||||
pep8==1.5.7
|
||||
positional==1.2.1
|
||||
prettytable==0.7.2
|
||||
pyflakes==0.8.1
|
||||
pyinotify==0.9.6
|
||||
pyparsing==2.1.0
|
||||
pyperclip==1.5.27
|
||||
python-dateutil==2.5.3
|
||||
python-keystoneclient==3.8.0
|
||||
python-mimeparse==1.6.0
|
||||
python-subunit==1.0.0
|
||||
pytz==2013.6
|
||||
PyYAML==3.12
|
||||
requests==2.14.2
|
||||
requests-mock==1.2.0
|
||||
requestsexceptions==1.2.0
|
||||
rfc3986==0.3.1
|
||||
simplejson==3.5.1
|
||||
six==1.10.0
|
||||
stestr==2.0.0
|
||||
stevedore==1.20.0
|
||||
testtools==2.2.0
|
||||
traceback2==1.4.0
|
||||
unittest2==1.1.0
|
||||
wrapt==1.7.0
|
||||
7
openstack-common.conf
Normal file
7
openstack-common.conf
Normal file
@@ -0,0 +1,7 @@
|
||||
[DEFAULT]
|
||||
|
||||
# The list of modules to copy from openstack-common
|
||||
modules=gettextutils,jsonutils,strutils,timeutils
|
||||
|
||||
# The base module to hold the copy of openstack.common
|
||||
base=tackerclient
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Support to create directly VNFFG and NS from its descriptor template
|
||||
without creating VNFFGD and NSD.
|
||||
@@ -1,9 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Add 'vnf_ids' and 'vnffg_ids' fields in outputs from network
|
||||
service list command. Users can know which VNFs or VNFFGs,
|
||||
belongs to specific NS.
|
||||
Add 'ns_id' field to VNFFG list command, that shows the
|
||||
network service the current VNFFG belongs to.
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
fixes:
|
||||
- |
|
||||
The VNFFGD CLI cannot show the VNFFGD template.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
fixes:
|
||||
- |
|
||||
Add documentation for python-tackerclient.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
fixes:
|
||||
- |
|
||||
Fix the VNFFG update osc command misusing create_vnffg function.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
fixes:
|
||||
- |
|
||||
Fix local test fail with pypy.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Enable CLI to support clustering service in Tacker Server.
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
features:
|
||||
-
|
||||
As user gives input of project and user name in vim_config.yaml,
|
||||
delete the user and project id from the vim specific commands
|
||||
output.
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
deprecations:
|
||||
- |
|
||||
Direct YAML input of any kind is now deprecated. Only file based YAML
|
||||
input is supported. This deprecation is across all resources like VNFFGD
|
||||
template, VNFD template and VNF configuration input.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
deprecations:
|
||||
- infra_driver and mgmt_driver attributes in VNFD client
|
||||
attribute is deprecated and will be removed in Ocata.
|
||||
@@ -1,7 +0,0 @@
|
||||
---
|
||||
deprecations:
|
||||
- |
|
||||
tacker CLI is deprecated, will be deleted after Rocky release.
|
||||
Use `openstack CLI`_ instead.
|
||||
|
||||
.. _openstack CLI: https://docs.openstack.org/python-tackerclient/latest/cli/index.html
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
upgrade:
|
||||
- |
|
||||
Python 2.7 support has been dropped. Last release of python-tackerclient
|
||||
to support python 2.7 is OpenStack Train. The minimum version of Python now
|
||||
supported by python-tackerclient is Python 3.6.
|
||||
@@ -1,3 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- Add support for multi delete feature for resources.
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
CLIs to onboard Network Services Descriptor (NSD) based on TOSCA Simple
|
||||
Profile for NFV and to create Network Services using NSD to create multiple
|
||||
related VNFs using a single TOSCA template.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- Adds new CLI command 'vnf-resource-list' to view VNF
|
||||
resources, such as VDU, CP, etc.
|
||||
@@ -1,3 +0,0 @@
|
||||
---
|
||||
fixes:
|
||||
- Remove passing mgmt and infra driver from client.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Add python-vnfd, vnf, nsd, ns, vnffgd, vnffg, event commands support.
|
||||
@@ -1,3 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- VIM can be updated without config-file argument in tacker vim-update command.
|
||||
@@ -1,3 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- Support to create VNF with direct VNFD template input from CLI.
|
||||
@@ -1,3 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- Made VNFD/VNF/VIM names mandatory in tacker CLI.
|
||||
@@ -1,3 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- Add client support for VNFFG.
|
||||
@@ -1,264 +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.
|
||||
#
|
||||
# tacker client documentation build configuration file, created by
|
||||
# sphinx-quickstart on Tue May 31 19:07:30 2016.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its
|
||||
# containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
#
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
# -- General configuration -------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
# needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = [
|
||||
'reno.sphinxext',
|
||||
'openstackdocstheme',
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
# source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'Tacker Client Release Notes'
|
||||
copyright = u'2016, Tacker Developers'
|
||||
|
||||
# Release notes are version independent.
|
||||
release = ''
|
||||
version = ''
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
# language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
# today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
# today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = []
|
||||
|
||||
# The reST default role (used for this markup: `text`) to
|
||||
# use for all documents.
|
||||
# default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
# add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
# add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
# show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
# modindex_common_prefix = []
|
||||
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'openstackdocs'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
# html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
# html_theme_path = []
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
# html_title = None
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
# html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
# html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
# html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
# html_last_updated_fmt = '%b %d, %Y'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
# html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
# html_sidebars = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
# html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
# html_domain_indices = True
|
||||
|
||||
# If false, no index is generated.
|
||||
# html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
# html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
# html_show_sourcelink = True
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
# html_show_sphinx = True
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
# html_show_copyright = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
# html_use_opensearch = ''
|
||||
|
||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
# html_file_suffix = None
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'tackerclientdoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output ------------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
# 'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
# 'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
# 'preamble': '',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass
|
||||
# [howto/manual]).
|
||||
latex_documents = [
|
||||
('index', 'TackerClientReleaseNotes.tex',
|
||||
u'Tacker Client Release Notes Documentation',
|
||||
u'Tacker Developers', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
# latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
# latex_use_parts = False
|
||||
|
||||
# If true, show page references after internal links.
|
||||
# latex_show_pagerefs = False
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
# latex_show_urls = False
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
# latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
# latex_domain_indices = True
|
||||
|
||||
|
||||
# -- Options for manual page output ------------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'tackerreleasenotes',
|
||||
u'Tacker Client Release Notes Documentation',
|
||||
[u'Tacker Developers'], 1)
|
||||
]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
# man_show_urls = False
|
||||
|
||||
|
||||
# -- Options for Texinfo output ----------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
('index', 'TackerClientReleaseNotes',
|
||||
u'Tacker Client Release Notes Documentation',
|
||||
u'Tacker Developers', 'TackerClientReleaseNotes',
|
||||
'Tacker Client Project.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
# texinfo_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
# texinfo_domain_indices = True
|
||||
|
||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||
# texinfo_show_urls = 'footnote'
|
||||
|
||||
# -- Options for Internationalization output ------------------------------
|
||||
locale_dirs = ['locale/']
|
||||
|
||||
# -- Options for openstackdocstheme -------------------------------------------
|
||||
repository_name = 'openstack/python-tackerclient'
|
||||
bug_project = 'python-tackerclient'
|
||||
bug_tag = ''
|
||||
@@ -1,16 +0,0 @@
|
||||
Python-TackerClient Release Notes
|
||||
=================================
|
||||
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
unreleased
|
||||
train
|
||||
stein
|
||||
rocky
|
||||
queens
|
||||
pike
|
||||
ocata
|
||||
newton
|
||||
@@ -1,6 +0,0 @@
|
||||
===================================
|
||||
Newton Series Release Notes
|
||||
===================================
|
||||
|
||||
.. release-notes::
|
||||
:branch: origin/stable/newton
|
||||
@@ -1,6 +0,0 @@
|
||||
===================================
|
||||
Ocata Series Release Notes
|
||||
===================================
|
||||
|
||||
.. release-notes::
|
||||
:branch: origin/stable/ocata
|
||||
@@ -1,6 +0,0 @@
|
||||
===================================
|
||||
Pike Series Release Notes
|
||||
===================================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/pike
|
||||
@@ -1,6 +0,0 @@
|
||||
===================================
|
||||
Queens Series Release Notes
|
||||
===================================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/queens
|
||||
@@ -1,6 +0,0 @@
|
||||
===================================
|
||||
Rocky Series Release Notes
|
||||
===================================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/rocky
|
||||
@@ -1,6 +0,0 @@
|
||||
===================================
|
||||
Stein Series Release Notes
|
||||
===================================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/stein
|
||||
@@ -1,6 +0,0 @@
|
||||
==========================
|
||||
Train Series Release Notes
|
||||
==========================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/train
|
||||
@@ -1,5 +0,0 @@
|
||||
============================
|
||||
Current Series Release Notes
|
||||
============================
|
||||
|
||||
.. release-notes::
|
||||
@@ -1,18 +1,13 @@
|
||||
# The order of packages is significant, because pip processes them in the order
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
pbr!=2.1.0,>=2.0.0 # Apache-2.0
|
||||
cliff!=2.9.0,>=2.8.0 # Apache-2.0
|
||||
iso8601>=0.1.11 # MIT
|
||||
netaddr>=0.7.18 # BSD
|
||||
requests>=2.14.2 # Apache-2.0
|
||||
python-keystoneclient>=3.8.0 # Apache-2.0
|
||||
simplejson>=3.5.1 # MIT
|
||||
six>=1.10.0 # MIT
|
||||
stevedore>=1.20.0 # Apache-2.0
|
||||
Babel!=2.4.0,>=2.3.4 # BSD
|
||||
oslo.i18n>=3.15.3 # Apache-2.0
|
||||
osc-lib>=1.8.0 # Apache-2.0
|
||||
oslo.log>=3.36.0 # Apache-2.0
|
||||
oslo.utils>=3.33.0 # Apache-2.0
|
||||
oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0
|
||||
pbr>=1.6
|
||||
argparse
|
||||
cliff>=1.14.0 # Apache-2.0
|
||||
iso8601>=0.1.9
|
||||
netaddr>=0.7.12,!=0.7.16
|
||||
requests>=2.5.2,!=2.8.0,!=2.9.0
|
||||
python-keystoneclient>=1.6.0,!=1.8.0
|
||||
simplejson>=2.2.0
|
||||
six>=1.9.0
|
||||
Babel>=1.3
|
||||
|
||||
73
setup.cfg
73
setup.cfg
@@ -1,11 +1,11 @@
|
||||
[metadata]
|
||||
name = python-tackerclient
|
||||
summary = CLI and Client Library for OpenStack Tacker
|
||||
summary = CLI and Client Library for OpenStack Tacker
|
||||
description-file =
|
||||
README.rst
|
||||
author = OpenStack
|
||||
author-email = openstack-discuss@lists.openstack.org
|
||||
home-page = https://docs.openstack.org/python-tackerclient/
|
||||
author-email = openstack-dev@lists.openstack.org
|
||||
home-page = http://www.openstack.org/
|
||||
classifier =
|
||||
Environment :: OpenStack
|
||||
Intended Audience :: Developers
|
||||
@@ -14,9 +14,9 @@ classifier =
|
||||
License :: OSI Approved :: Apache Software License
|
||||
Operating System :: POSIX :: Linux
|
||||
Programming Language :: Python
|
||||
Programming Language :: Python :: 3
|
||||
Programming Language :: Python :: 3.6
|
||||
Programming Language :: Python :: 3.7
|
||||
Programming Language :: Python :: 2
|
||||
Programming Language :: Python :: 2.7
|
||||
Programming Language :: Python :: 2.6
|
||||
|
||||
[files]
|
||||
packages =
|
||||
@@ -30,65 +30,10 @@ setup-hooks =
|
||||
console_scripts =
|
||||
tacker = tackerclient.shell:main
|
||||
|
||||
openstack.cli.extension =
|
||||
tackerclient = tackerclient.osc.plugin
|
||||
|
||||
openstack.tackerclient.v1 =
|
||||
vim_register = tackerclient.osc.v1.nfvo.vim:CreateVIM
|
||||
vim_list = tackerclient.osc.v1.nfvo.vim:ListVIM
|
||||
vim_set = tackerclient.osc.v1.nfvo.vim:UpdateVIM
|
||||
vim_delete = tackerclient.osc.v1.nfvo.vim:DeleteVIM
|
||||
vim_show = tackerclient.osc.v1.nfvo.vim:ShowVIM
|
||||
vnf_descriptor_create = tackerclient.osc.v1.vnfm.vnfd:CreateVNFD
|
||||
vnf_descriptor_delete = tackerclient.osc.v1.vnfm.vnfd:DeleteVNFD
|
||||
vnf_descriptor_list = tackerclient.osc.v1.vnfm.vnfd:ListVNFD
|
||||
vnf_descriptor_show = tackerclient.osc.v1.vnfm.vnfd:ShowVNFD
|
||||
vnf_descriptor_template_show = tackerclient.osc.v1.vnfm.vnfd:ShowTemplateVNFD
|
||||
vnf_create = tackerclient.osc.v1.vnfm.vnf:CreateVNF
|
||||
vnf_delete = tackerclient.osc.v1.vnfm.vnf:DeleteVNF
|
||||
vnf_list = tackerclient.osc.v1.vnfm.vnf:ListVNF
|
||||
vnf_show = tackerclient.osc.v1.vnfm.vnf:ShowVNF
|
||||
vnf_resource_list = tackerclient.osc.v1.vnfm.vnf:ListVNFResources
|
||||
vnf_set = tackerclient.osc.v1.vnfm.vnf:UpdateVNF
|
||||
vnf_scale = tackerclient.osc.v1.vnfm.vnf:ScaleVNF
|
||||
vnf_graph_descriptor_create = tackerclient.osc.v1.nfvo.vnffgd:CreateVNFFGD
|
||||
vnf_graph_descriptor_delete = tackerclient.osc.v1.nfvo.vnffgd:DeleteVNFFGD
|
||||
vnf_graph_descriptor_list = tackerclient.osc.v1.nfvo.vnffgd:ListVNFFGD
|
||||
vnf_graph_descriptor_show = tackerclient.osc.v1.nfvo.vnffgd:ShowVNFFGD
|
||||
vnf_graph_descriptor_template_show = tackerclient.osc.v1.nfvo.vnffgd:ShowTemplateVNFFGD
|
||||
ns_descriptor_create = tackerclient.osc.v1.nfvo.nsd:CreateNSD
|
||||
ns_descriptor_delete = tackerclient.osc.v1.nfvo.nsd:DeleteNSD
|
||||
ns_descriptor_list = tackerclient.osc.v1.nfvo.nsd:ListNSD
|
||||
ns_descriptor_show = tackerclient.osc.v1.nfvo.nsd:ShowNSD
|
||||
ns_descriptor_template_show = tackerclient.osc.v1.nfvo.nsd:ShowTemplateNSD
|
||||
nfv_event_show = tackerclient.osc.v1.events.events:ShowEvent
|
||||
nfv_event_list = tackerclient.osc.v1.events.events:ListEvent
|
||||
ns_create = tackerclient.osc.v1.nfvo.ns:CreateNS
|
||||
ns_delete = tackerclient.osc.v1.nfvo.ns:DeleteNS
|
||||
ns_list = tackerclient.osc.v1.nfvo.ns:ListNS
|
||||
ns_show = tackerclient.osc.v1.nfvo.ns:ShowNS
|
||||
vnf_graph_create = tackerclient.osc.v1.nfvo.vnffg:CreateVNFFG
|
||||
vnf_graph_delete = tackerclient.osc.v1.nfvo.vnffg:DeleteVNFFG
|
||||
vnf_graph_set = tackerclient.osc.v1.nfvo.vnffg:UpdateVNFFG
|
||||
vnf_graph_list = tackerclient.osc.v1.nfvo.vnffg:ListVNFFG
|
||||
vnf_graph_show = tackerclient.osc.v1.nfvo.vnffg:ShowVNFFG
|
||||
vnf_network_forwarding_path_list = tackerclient.osc.v1.nfvo.vnffg:ListNFP
|
||||
vnf_network_forwarding_path_show = tackerclient.osc.v1.nfvo.vnffg:ShowNFP
|
||||
vnf_classifier_list = tackerclient.osc.v1.nfvo.vnffg:ListFC
|
||||
vnf_classifier_show = tackerclient.osc.v1.nfvo.vnffg:ShowFC
|
||||
vnf_chain_list = tackerclient.osc.v1.nfvo.vnffg:ListSFC
|
||||
vnf_chain_show = tackerclient.osc.v1.nfvo.vnffg:ShowSFC
|
||||
vnf_package_create = tackerclient.osc.v1.vnfpkgm.vnf_package:CreateVnfPackage
|
||||
vnf_package_list = tackerclient.osc.v1.vnfpkgm.vnf_package:ListVnfPackage
|
||||
vnf_package_show = tackerclient.osc.v1.vnfpkgm.vnf_package:ShowVnfPackage
|
||||
vnf_package_upload = tackerclient.osc.v1.vnfpkgm.vnf_package:UploadVnfPackage
|
||||
vnf_package_delete = tackerclient.osc.v1.vnfpkgm.vnf_package:DeleteVnfPackage
|
||||
|
||||
|
||||
[build_releasenotes]
|
||||
[build_sphinx]
|
||||
all_files = 1
|
||||
build-dir = releasenotes/build
|
||||
source-dir = releasenotes/source
|
||||
build-dir = doc/build
|
||||
source-dir = doc/source
|
||||
|
||||
[wheel]
|
||||
universal = 1
|
||||
|
||||
2
setup.py
2
setup.py
@@ -25,5 +25,5 @@ except ImportError:
|
||||
pass
|
||||
|
||||
setuptools.setup(
|
||||
setup_requires=['pbr>=2.0.0'],
|
||||
setup_requires=['pbr>=1.8'],
|
||||
pbr=True)
|
||||
|
||||
@@ -56,7 +56,7 @@ class HTTPClient(object):
|
||||
endpoint_url=None, insecure=False,
|
||||
endpoint_type='publicURL',
|
||||
auth_strategy='keystone', ca_cert=None, log_credentials=False,
|
||||
service_type='nfv-orchestration',
|
||||
service_type='servicevm',
|
||||
**kwargs):
|
||||
|
||||
self.username = username
|
||||
@@ -171,6 +171,8 @@ class HTTPClient(object):
|
||||
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
|
||||
@@ -185,7 +187,7 @@ class HTTPClient(object):
|
||||
|
||||
if not self.endpoint_url:
|
||||
self.endpoint_url = self.service_catalog.url_for(
|
||||
region_name=self.region_name,
|
||||
attr='region', filter_value=self.region_name,
|
||||
service_type=self.service_type,
|
||||
endpoint_type=self.endpoint_type)
|
||||
|
||||
@@ -254,7 +256,7 @@ class HTTPClient(object):
|
||||
|
||||
body = json.loads(body)
|
||||
for endpoint in body.get('endpoints', []):
|
||||
if (endpoint['type'] == 'nfv-orchestration' and
|
||||
if (endpoint['type'] == 'servicevm' and
|
||||
endpoint.get('region') == self.region_name):
|
||||
if self.endpoint_type not in endpoint:
|
||||
raise exceptions.EndpointTypeNotFound(
|
||||
@@ -357,7 +359,7 @@ def construct_http_client(username=None,
|
||||
log_credentials=None,
|
||||
auth_strategy='keystone',
|
||||
ca_cert=None,
|
||||
service_type='nfv-orchestration',
|
||||
service_type='servicevm',
|
||||
session=None,
|
||||
**kwargs):
|
||||
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
# Copyright 2016 OpenStack Foundation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""oslo.i18n integration module.
|
||||
|
||||
See https://docs.openstack.org/oslo.i18n/latest/user/index.html.
|
||||
|
||||
"""
|
||||
|
||||
import oslo_i18n
|
||||
|
||||
DOMAIN = "tackerclient"
|
||||
|
||||
_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"
|
||||
# requires oslo.i18n >=2.1.0
|
||||
_C = _translators.contextual_form
|
||||
|
||||
# The plural translation function using the name "_P"
|
||||
# requires oslo.i18n >=2.1.0
|
||||
_P = _translators.plural_form
|
||||
|
||||
|
||||
def get_available_languages():
|
||||
return oslo_i18n.get_available_languages(DOMAIN)
|
||||
@@ -17,10 +17,15 @@
|
||||
"""Manage access to the clients, including authenticating when needed.
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
from tackerclient import client
|
||||
from tackerclient.tacker import client as tacker_client
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ClientCache(object):
|
||||
"""Descriptor class for caching created client handles."""
|
||||
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
# under the License.
|
||||
#
|
||||
|
||||
from sys import stderr
|
||||
|
||||
from cliff import command
|
||||
|
||||
|
||||
@@ -25,8 +23,6 @@ class OpenStackCommand(command.Command):
|
||||
api = None
|
||||
|
||||
def run(self, parsed_args):
|
||||
stderr.write("Deprecated: tacker command line is deprecated, "
|
||||
"will be deleted after Rocky is released.\n")
|
||||
if not self.api:
|
||||
return
|
||||
else:
|
||||
|
||||
@@ -148,6 +148,12 @@ class OverQuotaClient(Conflict):
|
||||
pass
|
||||
|
||||
|
||||
# TODO(amotoki): It is unused in Tacker, but it is referred to
|
||||
# in Horizon code. After Horizon code is updated, remove it.
|
||||
class AlreadyAttachedClient(Conflict):
|
||||
pass
|
||||
|
||||
|
||||
class IpAddressGenerationFailureClient(Conflict):
|
||||
pass
|
||||
|
||||
@@ -203,10 +209,6 @@ class InvalidContentType(TackerClientException):
|
||||
message = _("Invalid content type %(content_type)s.")
|
||||
|
||||
|
||||
class InvalidInput(TackerClientException):
|
||||
message = _("Invalid input: %(reason)s")
|
||||
|
||||
|
||||
# Command line exceptions
|
||||
|
||||
class TackerCLIError(TackerException):
|
||||
@@ -219,9 +221,7 @@ class CommandError(TackerCLIError):
|
||||
|
||||
|
||||
class UnsupportedVersion(TackerCLIError):
|
||||
"""Unsupported Version.
|
||||
|
||||
Indicates that the user is trying to use an unsupported version of
|
||||
"""Indicates that the user is trying to use an unsupported version of
|
||||
the API.
|
||||
"""
|
||||
pass
|
||||
|
||||
@@ -18,10 +18,11 @@
|
||||
"""Utilities and helper functions."""
|
||||
|
||||
import argparse
|
||||
import functools
|
||||
import logging
|
||||
import os
|
||||
import warnings
|
||||
|
||||
from oslo_log import versionutils
|
||||
from oslo_utils import encodeutils
|
||||
from oslo_utils import importutils
|
||||
import six
|
||||
@@ -174,13 +175,14 @@ def add_boolean_argument(parser, name, **kwargs):
|
||||
**kwargs)
|
||||
|
||||
|
||||
def get_file_path(filename):
|
||||
file_path = os.path.abspath(os.path.join(os.path.dirname(__file__),
|
||||
'../%s' % filename))
|
||||
return file_path
|
||||
|
||||
|
||||
def deprecate_warning(what, as_of, in_favor_of=None, remove_in=1):
|
||||
versionutils.deprecation_warning(as_of=as_of, what=what,
|
||||
in_favor_of=in_favor_of,
|
||||
remove_in=remove_in)
|
||||
def deprecated(name):
|
||||
"""This decorator can be used to mark a call as deprecated."""
|
||||
def _deprecate(func):
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
warnings.simplefilter('always', DeprecationWarning)
|
||||
warnings.warn("Call to %s deprecated, avoid its usage." % name,
|
||||
category=DeprecationWarning)
|
||||
return func(*args, **kwargs)
|
||||
return wrapper
|
||||
return _deprecate
|
||||
|
||||
@@ -16,3 +16,13 @@ _translators = i18n.TranslatorFactory(domain='tackerclient')
|
||||
|
||||
# The primary translation function using the well-known name "_"
|
||||
_ = _translators.primary
|
||||
|
||||
# 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
|
||||
|
||||
17
tackerclient/openstack/common/__init__.py
Normal file
17
tackerclient/openstack/common/__init__.py
Normal file
@@ -0,0 +1,17 @@
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# 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
|
||||
|
||||
|
||||
six.add_move(six.MovedModule('mox', 'mox', 'mox3.mox'))
|
||||
320
tackerclient/openstack/common/gettextutils.py
Normal file
320
tackerclient/openstack/common/gettextutils.py
Normal file
@@ -0,0 +1,320 @@
|
||||
# Copyright 2012 Red Hat, Inc.
|
||||
# Copyright 2013 IBM Corp.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
gettext for openstack-common modules.
|
||||
|
||||
Usual usage in an openstack.common module:
|
||||
|
||||
from tackerclient.openstack.common.gettextutils import _
|
||||
"""
|
||||
|
||||
import copy
|
||||
import gettext
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import UserString
|
||||
|
||||
from babel import localedata
|
||||
import six
|
||||
|
||||
_localedir = os.environ.get('tackerclient'.upper() + '_LOCALEDIR')
|
||||
_t = gettext.translation('tackerclient', localedir=_localedir, fallback=True)
|
||||
|
||||
_AVAILABLE_LANGUAGES = {}
|
||||
USE_LAZY = False
|
||||
|
||||
|
||||
def enable_lazy():
|
||||
"""Convenience function for configuring _() to use lazy gettext
|
||||
|
||||
Call this at the start of execution to enable the gettextutils._
|
||||
function to use lazy gettext functionality. This is useful if
|
||||
your project is importing _ directly instead of using the
|
||||
gettextutils.install() way of importing the _ function.
|
||||
"""
|
||||
global USE_LAZY
|
||||
USE_LAZY = True
|
||||
|
||||
|
||||
def _(msg):
|
||||
if USE_LAZY:
|
||||
return Message(msg, 'tackerclient')
|
||||
else:
|
||||
return _t.ugettext(msg)
|
||||
|
||||
|
||||
def install(domain, lazy=False):
|
||||
"""Install a _() function using the given translation domain.
|
||||
|
||||
Given a translation domain, install a _() function using gettext's
|
||||
install() function.
|
||||
|
||||
The main difference from gettext.install() is that we allow
|
||||
overriding the default localedir (e.g. /usr/share/locale) using
|
||||
a translation-domain-specific environment variable (e.g.
|
||||
NOVA_LOCALEDIR).
|
||||
|
||||
:param domain: the translation domain
|
||||
:param lazy: indicates whether or not to install the lazy _() function.
|
||||
The lazy _() introduces a way to do deferred translation
|
||||
of messages by installing a _ that builds Message objects,
|
||||
instead of strings, which can then be lazily translated into
|
||||
any available locale.
|
||||
"""
|
||||
if lazy:
|
||||
# NOTE(mrodden): Lazy gettext functionality.
|
||||
#
|
||||
# The following introduces a deferred way to do translations on
|
||||
# messages in OpenStack. We override the standard _() function
|
||||
# and % (format string) operation to build Message objects that can
|
||||
# later be translated when we have more information.
|
||||
#
|
||||
# Also included below is an example LocaleHandler that translates
|
||||
# Messages to an associated locale, effectively allowing many logs,
|
||||
# each with their own locale.
|
||||
|
||||
def _lazy_gettext(msg):
|
||||
"""Create and return a Message object.
|
||||
|
||||
Lazy gettext function for a given domain, it is a factory method
|
||||
for a project/module to get a lazy gettext function for its own
|
||||
translation domain (i.e. nova, glance, cinder, etc.)
|
||||
|
||||
Message encapsulates a string so that we can translate
|
||||
it later when needed.
|
||||
"""
|
||||
return Message(msg, domain)
|
||||
|
||||
import __builtin__
|
||||
__builtin__.__dict__['_'] = _lazy_gettext
|
||||
else:
|
||||
localedir = '%s_LOCALEDIR' % domain.upper()
|
||||
gettext.install(domain,
|
||||
localedir=os.environ.get(localedir),
|
||||
unicode=True)
|
||||
|
||||
|
||||
class Message(UserString.UserString, object):
|
||||
"""Class used to encapsulate translatable messages."""
|
||||
def __init__(self, msg, domain):
|
||||
# _msg is the gettext msgid and should never change
|
||||
self._msg = msg
|
||||
self._left_extra_msg = ''
|
||||
self._right_extra_msg = ''
|
||||
self.params = None
|
||||
self.locale = None
|
||||
self.domain = domain
|
||||
|
||||
@property
|
||||
def data(self):
|
||||
# NOTE(mrodden): this should always resolve to a unicode string
|
||||
# that best represents the state of the message currently
|
||||
|
||||
localedir = os.environ.get(self.domain.upper() + '_LOCALEDIR')
|
||||
if self.locale:
|
||||
lang = gettext.translation(self.domain,
|
||||
localedir=localedir,
|
||||
languages=[self.locale],
|
||||
fallback=True)
|
||||
else:
|
||||
# use system locale for translations
|
||||
lang = gettext.translation(self.domain,
|
||||
localedir=localedir,
|
||||
fallback=True)
|
||||
|
||||
full_msg = (self._left_extra_msg +
|
||||
lang.ugettext(self._msg) +
|
||||
self._right_extra_msg)
|
||||
|
||||
if self.params is not None:
|
||||
full_msg = full_msg % self.params
|
||||
|
||||
return six.text_type(full_msg)
|
||||
|
||||
def _save_dictionary_parameter(self, dict_param):
|
||||
full_msg = self.data
|
||||
# look for %(blah) fields in string;
|
||||
# ignore %% and deal with the
|
||||
# case where % is first character on the line
|
||||
keys = re.findall('(?:[^%]|^)?%\((\w*)\)[a-z]', full_msg)
|
||||
|
||||
# if we don't find any %(blah) blocks but have a %s
|
||||
if not keys and re.findall('(?:[^%]|^)%[a-z]', full_msg):
|
||||
# apparently the full dictionary is the parameter
|
||||
params = copy.deepcopy(dict_param)
|
||||
else:
|
||||
params = {}
|
||||
for key in keys:
|
||||
try:
|
||||
params[key] = copy.deepcopy(dict_param[key])
|
||||
except TypeError:
|
||||
# cast uncopyable thing to unicode string
|
||||
params[key] = unicode(dict_param[key])
|
||||
|
||||
return params
|
||||
|
||||
def _save_parameters(self, other):
|
||||
# we check for None later to see if
|
||||
# we actually have parameters to inject,
|
||||
# so encapsulate if our parameter is actually None
|
||||
if other is None:
|
||||
self.params = (other, )
|
||||
elif isinstance(other, dict):
|
||||
self.params = self._save_dictionary_parameter(other)
|
||||
else:
|
||||
# fallback to casting to unicode,
|
||||
# this will handle the problematic python code-like
|
||||
# objects that cannot be deep-copied
|
||||
try:
|
||||
self.params = copy.deepcopy(other)
|
||||
except TypeError:
|
||||
self.params = unicode(other)
|
||||
|
||||
return self
|
||||
|
||||
# overrides to be more string-like
|
||||
def __unicode__(self):
|
||||
return self.data
|
||||
|
||||
def __str__(self):
|
||||
return self.data.encode('utf-8')
|
||||
|
||||
def __getstate__(self):
|
||||
to_copy = ['_msg', '_right_extra_msg', '_left_extra_msg',
|
||||
'domain', 'params', 'locale']
|
||||
new_dict = self.__dict__.fromkeys(to_copy)
|
||||
for attr in to_copy:
|
||||
new_dict[attr] = copy.deepcopy(self.__dict__[attr])
|
||||
|
||||
return new_dict
|
||||
|
||||
def __setstate__(self, state):
|
||||
for (k, v) in state.items():
|
||||
setattr(self, k, v)
|
||||
|
||||
# operator overloads
|
||||
def __add__(self, other):
|
||||
copied = copy.deepcopy(self)
|
||||
copied._right_extra_msg += other.__str__()
|
||||
return copied
|
||||
|
||||
def __radd__(self, other):
|
||||
copied = copy.deepcopy(self)
|
||||
copied._left_extra_msg += other.__str__()
|
||||
return copied
|
||||
|
||||
def __mod__(self, other):
|
||||
# do a format string to catch and raise
|
||||
# any possible KeyErrors from missing parameters
|
||||
self.data % other
|
||||
copied = copy.deepcopy(self)
|
||||
return copied._save_parameters(other)
|
||||
|
||||
def __mul__(self, other):
|
||||
return self.data * other
|
||||
|
||||
def __rmul__(self, other):
|
||||
return other * self.data
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self.data[key]
|
||||
|
||||
def __getslice__(self, start, end):
|
||||
return self.data.__getslice__(start, end)
|
||||
|
||||
def __getattribute__(self, name):
|
||||
# NOTE(mrodden): handle lossy operations that we can't deal with yet
|
||||
# These override the UserString implementation, since UserString
|
||||
# uses our __class__ attribute to try and build a new message
|
||||
# after running the inner data string through the operation.
|
||||
# At that point, we have lost the gettext message id and can just
|
||||
# safely resolve to a string instead.
|
||||
ops = ['capitalize', 'center', 'decode', 'encode',
|
||||
'expandtabs', 'ljust', 'lstrip', 'replace', 'rjust', 'rstrip',
|
||||
'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
|
||||
if name in ops:
|
||||
return getattr(self.data, name)
|
||||
else:
|
||||
return UserString.UserString.__getattribute__(self, name)
|
||||
|
||||
|
||||
def get_available_languages(domain):
|
||||
"""Lists the available languages for the given translation domain.
|
||||
|
||||
:param domain: the domain to get languages for
|
||||
"""
|
||||
if domain in _AVAILABLE_LANGUAGES:
|
||||
return copy.copy(_AVAILABLE_LANGUAGES[domain])
|
||||
|
||||
localedir = '%s_LOCALEDIR' % domain.upper()
|
||||
find = lambda x: gettext.find(domain,
|
||||
localedir=os.environ.get(localedir),
|
||||
languages=[x])
|
||||
|
||||
# NOTE(mrodden): en_US should always be available (and first in case
|
||||
# order matters) since our in-line message strings are en_US
|
||||
language_list = ['en_US']
|
||||
# NOTE(luisg): Babel <1.0 used a function called list(), which was
|
||||
# renamed to locale_identifiers() in >=1.0, the requirements master list
|
||||
# requires >=0.9.6, uncapped, so defensively work with both. We can remove
|
||||
# this check when the master list updates to >=1.0, and all projects udpate
|
||||
list_identifiers = (getattr(localedata, 'list', None) or
|
||||
getattr(localedata, 'locale_identifiers'))
|
||||
locale_identifiers = list_identifiers()
|
||||
for i in locale_identifiers:
|
||||
if find(i) is not None:
|
||||
language_list.append(i)
|
||||
_AVAILABLE_LANGUAGES[domain] = language_list
|
||||
return copy.copy(language_list)
|
||||
|
||||
|
||||
def get_localized_message(message, user_locale):
|
||||
"""Gets a localized version of the given message in the given locale."""
|
||||
if isinstance(message, Message):
|
||||
if user_locale:
|
||||
message.locale = user_locale
|
||||
return unicode(message)
|
||||
else:
|
||||
return message
|
||||
|
||||
|
||||
class LocaleHandler(logging.Handler):
|
||||
"""Handler that can have a locale associated to translate Messages.
|
||||
|
||||
A quick example of how to utilize the Message class above.
|
||||
LocaleHandler takes a locale and a target logging.Handler object
|
||||
to forward LogRecord objects to after translating the internal Message.
|
||||
"""
|
||||
|
||||
def __init__(self, locale, target):
|
||||
"""Initialize a LocaleHandler
|
||||
|
||||
:param locale: locale to use for translating messages
|
||||
:param target: logging.Handler object to forward
|
||||
LogRecord objects to after translation
|
||||
"""
|
||||
logging.Handler.__init__(self)
|
||||
self.locale = locale
|
||||
self.target = target
|
||||
|
||||
def emit(self, record):
|
||||
if isinstance(record.msg, Message):
|
||||
# set the locale and resolve to a string
|
||||
record.msg.locale = self.locale
|
||||
|
||||
self.target.emit(record)
|
||||
67
tackerclient/openstack/common/importutils.py
Normal file
67
tackerclient/openstack/common/importutils.py
Normal file
@@ -0,0 +1,67 @@
|
||||
# Copyright 2011 OpenStack Foundation.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Import related utilities and helper functions.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import traceback
|
||||
from tackerclient.openstack.common.gettextutils import _
|
||||
|
||||
|
||||
def import_class(import_str):
|
||||
"""Returns a class from a string including module and class."""
|
||||
mod_str, _sep, class_str = import_str.rpartition('.')
|
||||
try:
|
||||
__import__(mod_str)
|
||||
return getattr(sys.modules[mod_str], class_str)
|
||||
except (ValueError, AttributeError):
|
||||
raise ImportError(_('Class %s cannot be found (%s)') %
|
||||
(class_str,
|
||||
traceback.format_exception(*sys.exc_info())))
|
||||
|
||||
|
||||
def import_object(import_str, *args, **kwargs):
|
||||
"""Import a class and return an instance of it."""
|
||||
return import_class(import_str)(*args, **kwargs)
|
||||
|
||||
|
||||
def import_object_ns(name_space, import_str, *args, **kwargs):
|
||||
"""Tries to import object from default namespace.
|
||||
|
||||
Imports a class and return an instance of it, first by trying
|
||||
to find the class in a default namespace, then failing back to
|
||||
a full path if not found in the default namespace.
|
||||
"""
|
||||
import_value = "%s.%s" % (name_space, import_str)
|
||||
try:
|
||||
return import_class(import_value)(*args, **kwargs)
|
||||
except ImportError:
|
||||
return import_class(import_str)(*args, **kwargs)
|
||||
|
||||
|
||||
def import_module(import_str):
|
||||
"""Import a module."""
|
||||
__import__(import_str)
|
||||
return sys.modules[import_str]
|
||||
|
||||
|
||||
def try_import(import_str, default=None):
|
||||
"""Try to import a module and if it fails return default."""
|
||||
try:
|
||||
return import_module(import_str)
|
||||
except ImportError:
|
||||
return default
|
||||
186
tackerclient/openstack/common/jsonutils.py
Normal file
186
tackerclient/openstack/common/jsonutils.py
Normal file
@@ -0,0 +1,186 @@
|
||||
# Copyright 2010 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# Copyright 2011 Justin Santa Barbara
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
'''
|
||||
JSON related utilities.
|
||||
|
||||
This module provides a few things:
|
||||
|
||||
1) A handy function for getting an object down to something that can be
|
||||
JSON serialized. See to_primitive().
|
||||
|
||||
2) Wrappers around loads() and dumps(). The dumps() wrapper will
|
||||
automatically use to_primitive() for you if needed.
|
||||
|
||||
3) This sets up anyjson to use the loads() and dumps() wrappers if anyjson
|
||||
is available.
|
||||
'''
|
||||
|
||||
|
||||
import codecs
|
||||
import datetime
|
||||
import functools
|
||||
import inspect
|
||||
import itertools
|
||||
import sys
|
||||
|
||||
if sys.version_info < (2, 7):
|
||||
# On Python <= 2.6, json module is not C boosted, so try to use
|
||||
# simplejson module if available
|
||||
try:
|
||||
import simplejson as json
|
||||
except ImportError:
|
||||
import json
|
||||
else:
|
||||
import json
|
||||
|
||||
import six
|
||||
import six.moves.xmlrpc_client as xmlrpclib
|
||||
|
||||
from tackerclient.openstack.common import gettextutils
|
||||
from tackerclient.openstack.common import importutils
|
||||
from tackerclient.openstack.common import strutils
|
||||
from tackerclient.openstack.common import timeutils
|
||||
|
||||
netaddr = importutils.try_import("netaddr")
|
||||
|
||||
_nasty_type_tests = [inspect.ismodule, inspect.isclass, inspect.ismethod,
|
||||
inspect.isfunction, inspect.isgeneratorfunction,
|
||||
inspect.isgenerator, inspect.istraceback, inspect.isframe,
|
||||
inspect.iscode, inspect.isbuiltin, inspect.isroutine,
|
||||
inspect.isabstract]
|
||||
|
||||
_simple_types = (six.string_types + six.integer_types
|
||||
+ (type(None), bool, float))
|
||||
|
||||
|
||||
def to_primitive(value, convert_instances=False, convert_datetime=True,
|
||||
level=0, max_depth=3):
|
||||
"""Convert a complex object into primitives.
|
||||
|
||||
Handy for JSON serialization. We can optionally handle instances,
|
||||
but since this is a recursive function, we could have cyclical
|
||||
data structures.
|
||||
|
||||
To handle cyclical data structures we could track the actual objects
|
||||
visited in a set, but not all objects are hashable. Instead we just
|
||||
track the depth of the object inspections and don't go too deep.
|
||||
|
||||
Therefore, convert_instances=True is lossy ... be aware.
|
||||
|
||||
"""
|
||||
# handle obvious types first - order of basic types determined by running
|
||||
# full tests on nova project, resulting in the following counts:
|
||||
# 572754 <type 'NoneType'>
|
||||
# 460353 <type 'int'>
|
||||
# 379632 <type 'unicode'>
|
||||
# 274610 <type 'str'>
|
||||
# 199918 <type 'dict'>
|
||||
# 114200 <type 'datetime.datetime'>
|
||||
# 51817 <type 'bool'>
|
||||
# 26164 <type 'list'>
|
||||
# 6491 <type 'float'>
|
||||
# 283 <type 'tuple'>
|
||||
# 19 <type 'long'>
|
||||
if isinstance(value, _simple_types):
|
||||
return value
|
||||
|
||||
if isinstance(value, datetime.datetime):
|
||||
if convert_datetime:
|
||||
return timeutils.strtime(value)
|
||||
else:
|
||||
return value
|
||||
|
||||
# value of itertools.count doesn't get caught by nasty_type_tests
|
||||
# and results in infinite loop when list(value) is called.
|
||||
if type(value) == itertools.count:
|
||||
return six.text_type(value)
|
||||
|
||||
# FIXME(vish): Workaround for LP bug 852095. Without this workaround,
|
||||
# tests that raise an exception in a mocked method that
|
||||
# has a @wrap_exception with a notifier will fail. If
|
||||
# we up the dependency to 0.5.4 (when it is released) we
|
||||
# can remove this workaround.
|
||||
if getattr(value, '__module__', None) == 'mox':
|
||||
return 'mock'
|
||||
|
||||
if level > max_depth:
|
||||
return '?'
|
||||
|
||||
# The try block may not be necessary after the class check above,
|
||||
# but just in case ...
|
||||
try:
|
||||
recursive = functools.partial(to_primitive,
|
||||
convert_instances=convert_instances,
|
||||
convert_datetime=convert_datetime,
|
||||
level=level,
|
||||
max_depth=max_depth)
|
||||
if isinstance(value, dict):
|
||||
return dict((k, recursive(v)) for k, v in six.iteritems(value))
|
||||
elif isinstance(value, (list, tuple)):
|
||||
return [recursive(lv) for lv in value]
|
||||
|
||||
# It's not clear why xmlrpclib created their own DateTime type, but
|
||||
# for our purposes, make it a datetime type which is explicitly
|
||||
# handled
|
||||
if isinstance(value, xmlrpclib.DateTime):
|
||||
value = datetime.datetime(*tuple(value.timetuple())[:6])
|
||||
|
||||
if convert_datetime and isinstance(value, datetime.datetime):
|
||||
return timeutils.strtime(value)
|
||||
elif isinstance(value, gettextutils.Message):
|
||||
return value.data
|
||||
elif hasattr(value, 'iteritems'):
|
||||
return recursive(dict(value.iteritems()), level=level + 1)
|
||||
elif hasattr(value, '__iter__'):
|
||||
return recursive(list(value))
|
||||
elif convert_instances and hasattr(value, '__dict__'):
|
||||
# Likely an instance of something. Watch for cycles.
|
||||
# Ignore class member vars.
|
||||
return recursive(value.__dict__, level=level + 1)
|
||||
elif netaddr and isinstance(value, netaddr.IPAddress):
|
||||
return six.text_type(value)
|
||||
else:
|
||||
if any(test(value) for test in _nasty_type_tests):
|
||||
return six.text_type(value)
|
||||
return value
|
||||
except TypeError:
|
||||
# Class objects are tricky since they may define something like
|
||||
# __iter__ defined but it isn't callable as list().
|
||||
return six.text_type(value)
|
||||
|
||||
|
||||
def dumps(value, default=to_primitive, **kwargs):
|
||||
return json.dumps(value, default=default, **kwargs)
|
||||
|
||||
|
||||
def loads(s, encoding='utf-8'):
|
||||
return json.loads(strutils.safe_decode(s, encoding))
|
||||
|
||||
|
||||
def load(fp, encoding='utf-8'):
|
||||
return json.load(codecs.getreader(encoding)(fp))
|
||||
|
||||
|
||||
try:
|
||||
import anyjson
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
anyjson._modules.append((__name__, 'dumps', TypeError,
|
||||
'loads', ValueError, 'load'))
|
||||
anyjson.force_implementation(__name__)
|
||||
216
tackerclient/openstack/common/strutils.py
Normal file
216
tackerclient/openstack/common/strutils.py
Normal file
@@ -0,0 +1,216 @@
|
||||
# Copyright 2011 OpenStack Foundation.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
System-level utilities and helper functions.
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
import unicodedata
|
||||
|
||||
import six
|
||||
|
||||
from tackerclient.openstack.common.gettextutils import _ # noqa
|
||||
|
||||
|
||||
# Used for looking up extensions of text
|
||||
# to their 'multiplied' byte amount
|
||||
BYTE_MULTIPLIERS = {
|
||||
'': 1,
|
||||
't': 1024 ** 4,
|
||||
'g': 1024 ** 3,
|
||||
'm': 1024 ** 2,
|
||||
'k': 1024,
|
||||
}
|
||||
BYTE_REGEX = re.compile(r'(^-?\d+)(\D*)')
|
||||
|
||||
TRUE_STRINGS = ('1', 't', 'true', 'on', 'y', 'yes')
|
||||
FALSE_STRINGS = ('0', 'f', 'false', 'off', 'n', 'no')
|
||||
|
||||
SLUGIFY_STRIP_RE = re.compile(r"[^\w\s-]")
|
||||
SLUGIFY_HYPHENATE_RE = re.compile(r"[-\s]+")
|
||||
|
||||
|
||||
def int_from_bool_as_string(subject):
|
||||
"""Interpret a string as a boolean and return either 1 or 0.
|
||||
|
||||
Any string value in:
|
||||
|
||||
('True', 'true', 'On', 'on', '1')
|
||||
|
||||
is interpreted as a boolean True.
|
||||
|
||||
Useful for JSON-decoded stuff and config file parsing
|
||||
"""
|
||||
return bool_from_string(subject) and 1 or 0
|
||||
|
||||
|
||||
def bool_from_string(subject, strict=False):
|
||||
"""Interpret a string as a boolean.
|
||||
|
||||
A case-insensitive match is performed such that strings matching 't',
|
||||
'true', 'on', 'y', 'yes', or '1' are considered True and, when
|
||||
`strict=False`, anything else is considered False.
|
||||
|
||||
Useful for JSON-decoded stuff and config file parsing.
|
||||
|
||||
If `strict=True`, unrecognized values, including None, will raise a
|
||||
ValueError which is useful when parsing values passed in from an API call.
|
||||
Strings yielding False are 'f', 'false', 'off', 'n', 'no', or '0'.
|
||||
"""
|
||||
if not isinstance(subject, six.string_types):
|
||||
subject = str(subject)
|
||||
|
||||
lowered = subject.strip().lower()
|
||||
|
||||
if lowered in TRUE_STRINGS:
|
||||
return True
|
||||
elif lowered in FALSE_STRINGS:
|
||||
return False
|
||||
elif strict:
|
||||
acceptable = ', '.join(
|
||||
"'%s'" % s for s in sorted(TRUE_STRINGS + FALSE_STRINGS))
|
||||
msg = _("Unrecognized value '%(val)s', acceptable values are:"
|
||||
" %(acceptable)s") % {'val': subject,
|
||||
'acceptable': acceptable}
|
||||
raise ValueError(msg)
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def safe_decode(text, incoming=None, errors='strict'):
|
||||
"""Decodes incoming str using `incoming` if they're not already unicode.
|
||||
|
||||
:param incoming: Text's current encoding
|
||||
:param errors: Errors handling policy. See here for valid
|
||||
values http://docs.python.org/2/library/codecs.html
|
||||
:returns: text or a unicode `incoming` encoded
|
||||
representation of it.
|
||||
:raises TypeError: If text is not an isntance of str
|
||||
"""
|
||||
if not isinstance(text, six.string_types):
|
||||
raise TypeError("%s can't be decoded" % type(text))
|
||||
|
||||
if isinstance(text, six.text_type):
|
||||
return text
|
||||
|
||||
if not incoming:
|
||||
incoming = (sys.stdin.encoding or
|
||||
sys.getdefaultencoding())
|
||||
|
||||
try:
|
||||
return text.decode(incoming, errors)
|
||||
except UnicodeDecodeError:
|
||||
# Note(flaper87) If we get here, it means that
|
||||
# sys.stdin.encoding / sys.getdefaultencoding
|
||||
# didn't return a suitable encoding to decode
|
||||
# text. This happens mostly when global LANG
|
||||
# var is not set correctly and there's no
|
||||
# default encoding. In this case, most likely
|
||||
# python will use ASCII or ANSI encoders as
|
||||
# default encodings but they won't be capable
|
||||
# of decoding non-ASCII characters.
|
||||
#
|
||||
# Also, UTF-8 is being used since it's an ASCII
|
||||
# extension.
|
||||
return text.decode('utf-8', errors)
|
||||
|
||||
|
||||
def safe_encode(text, incoming=None,
|
||||
encoding='utf-8', errors='strict'):
|
||||
"""Encodes incoming str/unicode using `encoding`.
|
||||
|
||||
If incoming is not specified, text is expected to be encoded with
|
||||
current python's default encoding. (`sys.getdefaultencoding`)
|
||||
|
||||
:param incoming: Text's current encoding
|
||||
:param encoding: Expected encoding for text (Default UTF-8)
|
||||
:param errors: Errors handling policy. See here for valid
|
||||
values http://docs.python.org/2/library/codecs.html
|
||||
:returns: text or a bytestring `encoding` encoded
|
||||
representation of it.
|
||||
:raises TypeError: If text is not an isntance of str
|
||||
"""
|
||||
if not isinstance(text, six.string_types):
|
||||
raise TypeError(_("%s can't be encoded") % type(text).capitalize())
|
||||
|
||||
if not incoming:
|
||||
incoming = (sys.stdin.encoding or
|
||||
sys.getdefaultencoding())
|
||||
|
||||
if isinstance(text, six.text_type):
|
||||
return text.encode(encoding, errors)
|
||||
elif text and encoding != incoming:
|
||||
# Decode text before encoding it with `encoding`
|
||||
text = safe_decode(text, incoming, errors)
|
||||
return text.encode(encoding, errors)
|
||||
|
||||
return text
|
||||
|
||||
|
||||
def to_bytes(text, default=0):
|
||||
"""Converts a string into an integer of bytes.
|
||||
|
||||
Looks at the last characters of the text to determine
|
||||
what conversion is needed to turn the input text into a byte number.
|
||||
Supports "B, K(B), M(B), G(B), and T(B)". (case insensitive)
|
||||
|
||||
:param text: String input for bytes size conversion.
|
||||
:param default: Default return value when text is blank.
|
||||
|
||||
"""
|
||||
match = BYTE_REGEX.search(text)
|
||||
if match:
|
||||
magnitude = int(match.group(1))
|
||||
mult_key_org = match.group(2)
|
||||
if not mult_key_org:
|
||||
return magnitude
|
||||
elif text:
|
||||
msg = _('Invalid string format: %s') % text
|
||||
raise TypeError(msg)
|
||||
else:
|
||||
return default
|
||||
mult_key = mult_key_org.lower().replace('b', '', 1)
|
||||
multiplier = BYTE_MULTIPLIERS.get(mult_key)
|
||||
if multiplier is None:
|
||||
msg = _('Unknown byte multiplier: %s') % mult_key_org
|
||||
raise TypeError(msg)
|
||||
return magnitude * multiplier
|
||||
|
||||
|
||||
def to_slug(value, incoming=None, errors="strict"):
|
||||
"""Normalize string.
|
||||
|
||||
Convert to lowercase, remove non-word characters, and convert spaces
|
||||
to hyphens.
|
||||
|
||||
Inspired by Django's `slugify` filter.
|
||||
|
||||
:param value: Text to slugify
|
||||
:param incoming: Text's current encoding
|
||||
:param errors: Errors handling policy. See here for valid
|
||||
values http://docs.python.org/2/library/codecs.html
|
||||
:returns: slugified unicode representation of `value`
|
||||
:raises TypeError: If text is not an instance of str
|
||||
"""
|
||||
value = safe_decode(value, incoming, errors)
|
||||
# NOTE(aababilov): no need to use safe_(encode|decode) here:
|
||||
# encodings are always "ascii", error handling is always "ignore"
|
||||
# and types are always known (first: unicode; second: str)
|
||||
value = unicodedata.normalize("NFKD", value).encode(
|
||||
"ascii", "ignore").decode("ascii")
|
||||
value = SLUGIFY_STRIP_RE.sub("", value).strip().lower()
|
||||
return SLUGIFY_HYPHENATE_RE.sub("-", value)
|
||||
186
tackerclient/openstack/common/timeutils.py
Normal file
186
tackerclient/openstack/common/timeutils.py
Normal file
@@ -0,0 +1,186 @@
|
||||
# Copyright 2011 OpenStack Foundation.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Time related utilities and helper functions.
|
||||
"""
|
||||
|
||||
import calendar
|
||||
import datetime
|
||||
|
||||
import iso8601
|
||||
import six
|
||||
|
||||
|
||||
# ISO 8601 extended time format with microseconds
|
||||
_ISO8601_TIME_FORMAT_SUBSECOND = '%Y-%m-%dT%H:%M:%S.%f'
|
||||
_ISO8601_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S'
|
||||
PERFECT_TIME_FORMAT = _ISO8601_TIME_FORMAT_SUBSECOND
|
||||
|
||||
|
||||
def isotime(at=None, subsecond=False):
|
||||
"""Stringify time in ISO 8601 format."""
|
||||
if not at:
|
||||
at = utcnow()
|
||||
st = at.strftime(_ISO8601_TIME_FORMAT
|
||||
if not subsecond
|
||||
else _ISO8601_TIME_FORMAT_SUBSECOND)
|
||||
tz = at.tzinfo.tzname(None) if at.tzinfo else 'UTC'
|
||||
st += ('Z' if tz == 'UTC' else tz)
|
||||
return st
|
||||
|
||||
|
||||
def parse_isotime(timestr):
|
||||
"""Parse time from ISO 8601 format."""
|
||||
try:
|
||||
return iso8601.parse_date(timestr)
|
||||
except iso8601.ParseError as e:
|
||||
raise ValueError(unicode(e))
|
||||
except TypeError as e:
|
||||
raise ValueError(unicode(e))
|
||||
|
||||
|
||||
def strtime(at=None, fmt=PERFECT_TIME_FORMAT):
|
||||
"""Returns formatted utcnow."""
|
||||
if not at:
|
||||
at = utcnow()
|
||||
return at.strftime(fmt)
|
||||
|
||||
|
||||
def parse_strtime(timestr, fmt=PERFECT_TIME_FORMAT):
|
||||
"""Turn a formatted time back into a datetime."""
|
||||
return datetime.datetime.strptime(timestr, fmt)
|
||||
|
||||
|
||||
def normalize_time(timestamp):
|
||||
"""Normalize time in arbitrary timezone to UTC naive object."""
|
||||
offset = timestamp.utcoffset()
|
||||
if offset is None:
|
||||
return timestamp
|
||||
return timestamp.replace(tzinfo=None) - offset
|
||||
|
||||
|
||||
def is_older_than(before, seconds):
|
||||
"""Return True if before is older than seconds."""
|
||||
if isinstance(before, six.string_types):
|
||||
before = parse_strtime(before).replace(tzinfo=None)
|
||||
return utcnow() - before > datetime.timedelta(seconds=seconds)
|
||||
|
||||
|
||||
def is_newer_than(after, seconds):
|
||||
"""Return True if after is newer than seconds."""
|
||||
if isinstance(after, six.string_types):
|
||||
after = parse_strtime(after).replace(tzinfo=None)
|
||||
return after - utcnow() > datetime.timedelta(seconds=seconds)
|
||||
|
||||
|
||||
def utcnow_ts():
|
||||
"""Timestamp version of our utcnow function."""
|
||||
return calendar.timegm(utcnow().timetuple())
|
||||
|
||||
|
||||
def utcnow():
|
||||
"""Overridable version of utils.utcnow."""
|
||||
if utcnow.override_time:
|
||||
try:
|
||||
return utcnow.override_time.pop(0)
|
||||
except AttributeError:
|
||||
return utcnow.override_time
|
||||
return datetime.datetime.utcnow()
|
||||
|
||||
|
||||
def iso8601_from_timestamp(timestamp):
|
||||
"""Returns a iso8601 formated date from timestamp."""
|
||||
return isotime(datetime.datetime.utcfromtimestamp(timestamp))
|
||||
|
||||
|
||||
utcnow.override_time = None
|
||||
|
||||
|
||||
def set_time_override(override_time=datetime.datetime.utcnow()):
|
||||
"""Overrides utils.utcnow.
|
||||
|
||||
Make it return a constant time or a list thereof, one at a time.
|
||||
"""
|
||||
utcnow.override_time = override_time
|
||||
|
||||
|
||||
def advance_time_delta(timedelta):
|
||||
"""Advance overridden time using a datetime.timedelta."""
|
||||
assert(not utcnow.override_time is None)
|
||||
try:
|
||||
for dt in utcnow.override_time:
|
||||
dt += timedelta
|
||||
except TypeError:
|
||||
utcnow.override_time += timedelta
|
||||
|
||||
|
||||
def advance_time_seconds(seconds):
|
||||
"""Advance overridden time by seconds."""
|
||||
advance_time_delta(datetime.timedelta(0, seconds))
|
||||
|
||||
|
||||
def clear_time_override():
|
||||
"""Remove the overridden time."""
|
||||
utcnow.override_time = None
|
||||
|
||||
|
||||
def marshall_now(now=None):
|
||||
"""Make an rpc-safe datetime with microseconds.
|
||||
|
||||
Note: tzinfo is stripped, but not required for relative times.
|
||||
"""
|
||||
if not now:
|
||||
now = utcnow()
|
||||
return dict(day=now.day, month=now.month, year=now.year, hour=now.hour,
|
||||
minute=now.minute, second=now.second,
|
||||
microsecond=now.microsecond)
|
||||
|
||||
|
||||
def unmarshall_time(tyme):
|
||||
"""Unmarshall a datetime dict."""
|
||||
return datetime.datetime(day=tyme['day'],
|
||||
month=tyme['month'],
|
||||
year=tyme['year'],
|
||||
hour=tyme['hour'],
|
||||
minute=tyme['minute'],
|
||||
second=tyme['second'],
|
||||
microsecond=tyme['microsecond'])
|
||||
|
||||
|
||||
def delta_seconds(before, after):
|
||||
"""Return the difference between two timing objects.
|
||||
|
||||
Compute the difference in seconds between two date, time, or
|
||||
datetime objects (as a float, to microsecond resolution).
|
||||
"""
|
||||
delta = after - before
|
||||
try:
|
||||
return delta.total_seconds()
|
||||
except AttributeError:
|
||||
return ((delta.days * 24 * 3600) + delta.seconds +
|
||||
float(delta.microseconds) / (10 ** 6))
|
||||
|
||||
|
||||
def is_soon(dt, window):
|
||||
"""Determines if time is going to happen in the next window seconds.
|
||||
|
||||
:params dt: the time
|
||||
:params window: minimum seconds to remain to consider the time not soon
|
||||
|
||||
:return: True if expiration is within the given duration
|
||||
"""
|
||||
soon = (utcnow() + datetime.timedelta(seconds=window))
|
||||
return normalize_time(dt) <= soon
|
||||
@@ -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.
|
||||
#
|
||||
|
||||
"""OpenStackClient plugin for nfv-orchestration service."""
|
||||
|
||||
import logging
|
||||
|
||||
from osc_lib import utils
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
# Required by the OSC plugin interface
|
||||
DEFAULT_TACKER_API_VERSION = '1'
|
||||
API_NAME = 'tackerclient'
|
||||
API_VERSION_OPTION = 'os_tacker_api_version'
|
||||
API_VERSIONS = {
|
||||
'1': 'tackerclient.v1_0.client.Client',
|
||||
}
|
||||
|
||||
|
||||
def make_client(instance):
|
||||
"""Returns a client to the ClientManager."""
|
||||
|
||||
tacker_client = utils.get_client_class(
|
||||
API_NAME,
|
||||
instance._api_version[API_NAME],
|
||||
API_VERSIONS)
|
||||
LOG.debug('Instantiating tacker client: %s', tacker_client)
|
||||
|
||||
kwargs = {'service_type': 'nfv-orchestration',
|
||||
'region_name': instance._region_name,
|
||||
'endpoint_type': instance._interface,
|
||||
'interface': instance._interface,
|
||||
'session': instance.session
|
||||
}
|
||||
|
||||
client = tacker_client(**kwargs)
|
||||
|
||||
return client
|
||||
|
||||
|
||||
def build_option_parser(parser):
|
||||
"""Hook to add global options."""
|
||||
parser.add_argument(
|
||||
'--os-tacker-api-version',
|
||||
metavar='<tacker-api-version>',
|
||||
default=utils.env(
|
||||
'OS_TACKER_API_VERSION',
|
||||
default=DEFAULT_TACKER_API_VERSION))
|
||||
return parser
|
||||
@@ -1,102 +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.
|
||||
|
||||
|
||||
def get_osc_show_columns_for_sdk_resource(
|
||||
sdk_resource,
|
||||
osc_column_map,
|
||||
invisible_columns=None
|
||||
):
|
||||
"""Get and filter the display and attribute columns for an SDK resource.
|
||||
|
||||
Common utility function for preparing the output of an OSC show command.
|
||||
Some of the columns may need to get renamed, others made invisible.
|
||||
|
||||
:param sdk_resource: An SDK resource
|
||||
:param osc_column_map: A hash of mappings for display column names
|
||||
:param invisible_columns: A list of invisible column names
|
||||
|
||||
:returns: Two tuples containing the names of the display and attribute
|
||||
columns
|
||||
"""
|
||||
|
||||
if getattr(sdk_resource, 'allow_get', None) is not None:
|
||||
resource_dict = sdk_resource.to_dict(
|
||||
body=True, headers=False, ignore_none=False)
|
||||
else:
|
||||
resource_dict = sdk_resource
|
||||
|
||||
# Build the OSC column names to display for the SDK resource.
|
||||
attr_map = {}
|
||||
display_columns = list(resource_dict.keys())
|
||||
invisible_columns = [] if invisible_columns is None else invisible_columns
|
||||
for col_name in invisible_columns:
|
||||
if col_name in display_columns:
|
||||
display_columns.remove(col_name)
|
||||
for sdk_attr, osc_attr in osc_column_map.items():
|
||||
if sdk_attr in display_columns:
|
||||
attr_map[osc_attr] = sdk_attr
|
||||
display_columns.remove(sdk_attr)
|
||||
if osc_attr not in display_columns:
|
||||
display_columns.append(osc_attr)
|
||||
sorted_display_columns = sorted(display_columns)
|
||||
|
||||
# Build the SDK attribute names for the OSC column names.
|
||||
attr_columns = []
|
||||
for column in sorted_display_columns:
|
||||
new_column = attr_map[column] if column in attr_map else column
|
||||
attr_columns.append(new_column)
|
||||
return tuple(sorted_display_columns), tuple(attr_columns)
|
||||
|
||||
|
||||
class DictModel(dict):
|
||||
"""Convert dict into an object that provides attribute access to values."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Convert dict values to DictModel values."""
|
||||
super(DictModel, self).__init__(*args, **kwargs)
|
||||
|
||||
def needs_upgrade(item):
|
||||
return isinstance(item, dict) and not isinstance(item, DictModel)
|
||||
|
||||
def upgrade(item):
|
||||
"""Upgrade item if it needs to be upgraded."""
|
||||
if needs_upgrade(item):
|
||||
return DictModel(item)
|
||||
else:
|
||||
return item
|
||||
|
||||
for key, value in self.items():
|
||||
if isinstance(value, (list, tuple)):
|
||||
# Keep the same type but convert dicts to DictModels
|
||||
self[key] = type(value)(
|
||||
(upgrade(item) for item in value)
|
||||
)
|
||||
elif needs_upgrade(value):
|
||||
# Change dict instance values to DictModel instance values
|
||||
self[key] = DictModel(value)
|
||||
|
||||
def __getattr__(self, name):
|
||||
try:
|
||||
return self[name]
|
||||
except KeyError as e:
|
||||
raise AttributeError(e)
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
self[name] = value
|
||||
|
||||
def __delattr__(self, name):
|
||||
del self[name]
|
||||
|
||||
def __str__(self):
|
||||
pairs = ['%s=%s' % (k, v) for k, v in self.items()]
|
||||
return ', '.join(sorted(pairs))
|
||||
@@ -1,207 +0,0 @@
|
||||
# Copyright 2016 NEC Corporation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""This module should contain OSC plugin generic methods.
|
||||
|
||||
Methods in this module are candidates adopted to osc-lib.
|
||||
|
||||
Stuffs specific to tackerclient OSC plugin should not be added
|
||||
to this module. They should go to tackerclient.osc.v1.utils.
|
||||
"""
|
||||
|
||||
import operator
|
||||
|
||||
from keystoneclient import exceptions as identity_exc
|
||||
from keystoneclient.v3 import domains
|
||||
from keystoneclient.v3 import projects
|
||||
from osc_lib import utils
|
||||
from oslo_serialization import jsonutils
|
||||
|
||||
from tackerclient.i18n import _
|
||||
|
||||
|
||||
LIST_BOTH = 'both'
|
||||
LIST_SHORT_ONLY = 'short_only'
|
||||
LIST_LONG_ONLY = 'long_only'
|
||||
|
||||
|
||||
def format_dict_with_indention(data):
|
||||
"""Return a formatted string of key value pairs
|
||||
|
||||
:param data: a dict
|
||||
:rtype: a string formatted to key='value'
|
||||
"""
|
||||
|
||||
if data is None:
|
||||
return None
|
||||
return jsonutils.dumps(data, indent=4)
|
||||
|
||||
|
||||
def get_column_definitions(attr_map, long_listing):
|
||||
"""Return table headers and column names for a listing table.
|
||||
|
||||
:param attr_map: a list of table entry definitions.
|
||||
Each entry should be a tuple consisting of
|
||||
(API attribute name, header name, listing mode). For example:
|
||||
(('id', 'ID', LIST_BOTH),
|
||||
('name', 'Name', LIST_BOTH),
|
||||
('tenant_id', 'Project', LIST_LONG_ONLY))
|
||||
The third field of each tuple must be one of LIST_BOTH,
|
||||
LIST_LONG_ONLY (a corresponding column is shown only in a long mode), or
|
||||
LIST_SHORT_ONLY (a corresponding column is shown only in a short mode).
|
||||
:param long_listing: A boolean value which indicates a long listing
|
||||
or not. In most cases, parsed_args.long is passed to this argument.
|
||||
:return: A tuple of a list of table headers and a list of column names.
|
||||
"""
|
||||
|
||||
if long_listing:
|
||||
headers = [hdr for col, hdr, listing_mode in attr_map
|
||||
if listing_mode in (LIST_BOTH, LIST_LONG_ONLY)]
|
||||
columns = [col for col, hdr, listing_mode in attr_map
|
||||
if listing_mode in (LIST_BOTH, LIST_LONG_ONLY)]
|
||||
else:
|
||||
headers = [hdr for col, hdr, listing_mode in attr_map
|
||||
if listing_mode in (LIST_BOTH, LIST_SHORT_ONLY)]
|
||||
columns = [col for col, hdr, listing_mode in attr_map
|
||||
if listing_mode in (LIST_BOTH, LIST_SHORT_ONLY)]
|
||||
|
||||
return headers, columns
|
||||
|
||||
|
||||
def get_columns(item, attr_map=None):
|
||||
"""Return pair of resource attributes and corresponding display names.
|
||||
|
||||
Assume the following item and attr_map are passed.
|
||||
item: {'id': 'myid', 'name': 'myname',
|
||||
'foo': 'bar', 'tenant_id': 'mytenan'}
|
||||
attr_map:
|
||||
(('id', 'ID', LIST_BOTH),
|
||||
('name', 'Name', LIST_BOTH),
|
||||
('tenant_id', 'Project', LIST_LONG_ONLY))
|
||||
|
||||
This method returns:
|
||||
|
||||
(('id', 'name', 'tenant_id', 'foo'), # attributes
|
||||
('ID', 'Name', 'Project', 'foo') # display names
|
||||
|
||||
Both tuples of attributes and display names are sorted by display names
|
||||
in the alphabetical order.
|
||||
Attributes not found in a given attr_map are kept as-is.
|
||||
|
||||
:param item: a dictionary which represents a resource.
|
||||
Keys of the dictionary are expected to be attributes of the resource.
|
||||
Values are not referred to by this method.
|
||||
:param attr_map: a list of mapping from attribute to display name.
|
||||
The same format is used as for get_column_definitions attr_map.
|
||||
:return: A pair of tuple of attributes and tuple of display names.
|
||||
"""
|
||||
attr_map = attr_map or tuple([])
|
||||
_attr_map_dict = dict((col, hdr) for col, hdr, listing_mode in attr_map)
|
||||
|
||||
columns = [(column, _attr_map_dict.get(column, column))
|
||||
for column in item.keys()]
|
||||
columns = sorted(columns, key=operator.itemgetter(1))
|
||||
return (tuple(col[0] for col in columns),
|
||||
tuple(col[1] for col in columns))
|
||||
|
||||
|
||||
# TODO(amotoki): Use osc-lib version once osc-lib provides this.
|
||||
def add_project_owner_option_to_parser(parser):
|
||||
"""Register project and project domain options.
|
||||
|
||||
:param parser: argparse.Argument parser object.
|
||||
|
||||
"""
|
||||
parser.add_argument(
|
||||
'--project',
|
||||
metavar='<project>',
|
||||
help=_("Owner's project (name or ID)")
|
||||
)
|
||||
# Borrowed from openstackclient.identity.common
|
||||
# as it is not exposed officially.
|
||||
parser.add_argument(
|
||||
'--project-domain',
|
||||
metavar='<project-domain>',
|
||||
help=_('Domain the project belongs to (name or ID). '
|
||||
'This can be used in case collisions between project names '
|
||||
'exist.'),
|
||||
)
|
||||
|
||||
|
||||
# The following methods are borrowed from openstackclient.identity.common
|
||||
# as it is not exposed officially.
|
||||
# TODO(amotoki): Use osc-lib version once osc-lib provides this.
|
||||
|
||||
|
||||
def find_domain(identity_client, name_or_id):
|
||||
return _find_identity_resource(identity_client.domains, name_or_id,
|
||||
domains.Domain)
|
||||
|
||||
|
||||
def find_project(identity_client, name_or_id, domain_name_or_id=None):
|
||||
domain_id = _get_domain_id_if_requested(identity_client, domain_name_or_id)
|
||||
if not domain_id:
|
||||
return _find_identity_resource(identity_client.projects, name_or_id,
|
||||
projects.Project)
|
||||
else:
|
||||
return _find_identity_resource(identity_client.projects, name_or_id,
|
||||
projects.Project, domain_id=domain_id)
|
||||
|
||||
|
||||
def _get_domain_id_if_requested(identity_client, domain_name_or_id):
|
||||
if not domain_name_or_id:
|
||||
return None
|
||||
domain = find_domain(identity_client, domain_name_or_id)
|
||||
return domain.id
|
||||
|
||||
|
||||
def _find_identity_resource(identity_client_manager, name_or_id,
|
||||
resource_type, **kwargs):
|
||||
"""Find a specific identity resource.
|
||||
|
||||
Using keystoneclient's manager, attempt to find a specific resource by its
|
||||
name or ID. If Forbidden to find the resource (a common case if the user
|
||||
does not have permission), then return the resource by creating a local
|
||||
instance of keystoneclient's Resource.
|
||||
|
||||
The parameter identity_client_manager is a keystoneclient manager,
|
||||
for example: keystoneclient.v3.users or keystoneclient.v3.projects.
|
||||
|
||||
The parameter resource_type is a keystoneclient resource, for example:
|
||||
keystoneclient.v3.users.User or keystoneclient.v3.projects.Project.
|
||||
|
||||
:param identity_client_manager: the manager that contains the resource
|
||||
:type identity_client_manager: `keystoneclient.base.CrudManager`
|
||||
:param name_or_id: the resources's name or ID
|
||||
:type name_or_id: string
|
||||
:param resource_type: class that represents the resource type
|
||||
:type resource_type: `keystoneclient.base.Resource`
|
||||
|
||||
:returns: the resource in question
|
||||
:rtype: `keystoneclient.base.Resource`
|
||||
|
||||
"""
|
||||
|
||||
try:
|
||||
identity_resource = utils.find_resource(identity_client_manager,
|
||||
name_or_id, **kwargs)
|
||||
if identity_resource is not None:
|
||||
return identity_resource
|
||||
except identity_exc.Forbidden:
|
||||
pass
|
||||
|
||||
return resource_type(None, {'id': name_or_id, 'name': name_or_id})
|
||||
|
||||
|
||||
# The above are borrowed from openstackclient.identity.common.
|
||||
@@ -1,116 +0,0 @@
|
||||
# Copyright 2018 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 osc_lib.command import command
|
||||
from osc_lib import utils
|
||||
|
||||
from tackerclient.i18n import _
|
||||
from tackerclient.osc import sdk_utils
|
||||
from tackerclient.osc import utils as tacker_osc_utils
|
||||
from tackerclient.tacker import v1_0 as tackerV10
|
||||
|
||||
_attr_map = (
|
||||
('id', 'ID', tacker_osc_utils.LIST_BOTH),
|
||||
('resource_type', 'Resource Type', tacker_osc_utils.LIST_BOTH),
|
||||
('resource_id', 'Resource ID', tacker_osc_utils.LIST_BOTH),
|
||||
('resource_state', 'Resource State', tacker_osc_utils.LIST_BOTH),
|
||||
('event_type', 'Event Type', tacker_osc_utils.LIST_BOTH),
|
||||
('timestamp', 'Timestamp', tacker_osc_utils.LIST_BOTH),
|
||||
('event_details', 'Event Details', tacker_osc_utils.LIST_LONG_ONLY),
|
||||
)
|
||||
|
||||
_EVENT = "event"
|
||||
|
||||
events_path = '/events'
|
||||
|
||||
|
||||
def _get_columns(item):
|
||||
column_map = {}
|
||||
return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map)
|
||||
|
||||
|
||||
class ShowEvent(command.ShowOne):
|
||||
_description = _("Show event given the event id.")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ShowEvent, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
_EVENT,
|
||||
metavar="ID",
|
||||
help=_("ID of event to display")
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
obj_id = tackerV10.find_resourceid_by_name_or_id(
|
||||
client, _EVENT, parsed_args.event)
|
||||
obj = client.show_event(obj_id)
|
||||
display_columns, columns = _get_columns(obj[_EVENT])
|
||||
data = utils.get_item_properties(
|
||||
sdk_utils.DictModel(obj[_EVENT]),
|
||||
columns,)
|
||||
return (display_columns, data)
|
||||
|
||||
|
||||
class ListEvent(command.Lister):
|
||||
_description = _("List events of resources.")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListEvent, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'--id',
|
||||
help=_("id of the event to look up."))
|
||||
parser.add_argument(
|
||||
'--resource-type',
|
||||
help=_("resource type of the events to look up."))
|
||||
parser.add_argument(
|
||||
'--resource-id',
|
||||
help=_("resource id of the events to look up."))
|
||||
parser.add_argument(
|
||||
'--resource-state',
|
||||
help=_("resource state of the events to look up."))
|
||||
parser.add_argument(
|
||||
'--event-type',
|
||||
help=_("event type of the events to look up."))
|
||||
parser.add_argument(
|
||||
'--long',
|
||||
action='store_true',
|
||||
help=_("List additional fields in output"))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
_params = {}
|
||||
if parsed_args.id:
|
||||
_params['id'] = parsed_args.id
|
||||
if parsed_args.resource_id:
|
||||
_params['resource_id'] = parsed_args.resource_id
|
||||
if parsed_args.resource_state:
|
||||
_params['resource_state'] = parsed_args.resource_id
|
||||
if parsed_args.event_type:
|
||||
_params['event_type'] = parsed_args.event_type
|
||||
if parsed_args.resource_type:
|
||||
_params['resource_type'] = parsed_args.resource_type
|
||||
events = client.list('events', events_path, True, **_params)
|
||||
data = {}
|
||||
data['events'] = events['events']
|
||||
headers, columns = tacker_osc_utils.get_column_definitions(
|
||||
_attr_map, long_listing=parsed_args.long)
|
||||
return (headers,
|
||||
(utils.get_dict_properties(
|
||||
s, columns,
|
||||
) for s in data[_EVENT + 's']))
|
||||
@@ -1,254 +0,0 @@
|
||||
# Copyright 2018 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 yaml
|
||||
|
||||
from osc_lib.command import command
|
||||
from osc_lib import utils
|
||||
|
||||
from tackerclient.common import exceptions
|
||||
from tackerclient.i18n import _
|
||||
from tackerclient.osc import sdk_utils
|
||||
from tackerclient.osc import utils as tacker_osc_utils
|
||||
from tackerclient.tacker import v1_0 as tackerV10
|
||||
|
||||
_attr_map = (
|
||||
('id', 'ID', tacker_osc_utils.LIST_BOTH),
|
||||
('name', 'Name', tacker_osc_utils.LIST_BOTH),
|
||||
('nsd_id', 'NSD ID', tacker_osc_utils.LIST_BOTH),
|
||||
('vnf_ids', 'VNF IDs', tacker_osc_utils.LIST_BOTH),
|
||||
('vnffg_ids', 'VNFFG IDs', tacker_osc_utils.LIST_BOTH),
|
||||
('mgmt_ip_addresses', 'Mgmt Ip Addresses', tacker_osc_utils.LIST_BOTH),
|
||||
('status', 'Status', tacker_osc_utils.LIST_BOTH),
|
||||
)
|
||||
|
||||
_NS = 'ns'
|
||||
_RESOURCE = 'resource'
|
||||
|
||||
|
||||
def _get_columns(item):
|
||||
column_map = {
|
||||
'tenant_id': 'project_id',
|
||||
}
|
||||
return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map)
|
||||
|
||||
|
||||
class CreateNS(command.ShowOne):
|
||||
_description = _("Create a new NS")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CreateNS, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'name', metavar='NAME',
|
||||
help=_('Name for NS'))
|
||||
parser.add_argument(
|
||||
'--tenant-id', metavar='TENANT_ID',
|
||||
help=_('The owner tenant ID'))
|
||||
parser.add_argument(
|
||||
'--description',
|
||||
help=_('Set description for the NS'))
|
||||
nsd_group = parser.add_mutually_exclusive_group(required=True)
|
||||
nsd_group.add_argument(
|
||||
'--nsd-id',
|
||||
help=_('NSD ID to use as template to create NS'))
|
||||
nsd_group.add_argument(
|
||||
'--nsd-template',
|
||||
help=_('NSD file to create NS'))
|
||||
nsd_group.add_argument(
|
||||
'--nsd-name',
|
||||
help=_('NSD name to use as template to create NS'))
|
||||
vim_group = parser.add_mutually_exclusive_group()
|
||||
vim_group.add_argument(
|
||||
'--vim-id',
|
||||
help=_('VIM ID to use to create NS on the specified VIM'))
|
||||
vim_group.add_argument(
|
||||
'--vim-name',
|
||||
help=_('VIM name to use to create NS on the specified VIM'))
|
||||
parser.add_argument(
|
||||
'--vim-region-name',
|
||||
help=_('VIM Region to use to create NS on the specified VIM'))
|
||||
parser.add_argument(
|
||||
'--param-file',
|
||||
help=_('Specify parameter YAML file'))
|
||||
return parser
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
body = {_NS: {}}
|
||||
body[_NS]['attributes'] = {}
|
||||
|
||||
if parsed_args.vim_region_name:
|
||||
body[_NS].setdefault('placement_attr', {})['region_name'] = \
|
||||
parsed_args.vim_region_name
|
||||
|
||||
client = self.app.client_manager.tackerclient
|
||||
if parsed_args.vim_name:
|
||||
_id = tackerV10.find_resourceid_by_name_or_id(client, 'vim',
|
||||
parsed_args.vim_name)
|
||||
parsed_args.vim_id = _id
|
||||
if parsed_args.nsd_name:
|
||||
_id = tackerV10.find_resourceid_by_name_or_id(client, 'nsd',
|
||||
parsed_args.nsd_name)
|
||||
parsed_args.nsd_id = _id
|
||||
elif parsed_args.nsd_template:
|
||||
with open(parsed_args.nsd_template) as f:
|
||||
template = f.read()
|
||||
try:
|
||||
template = yaml.load(
|
||||
template, Loader=yaml.SafeLoader)
|
||||
except yaml.YAMLError as e:
|
||||
raise exceptions.InvalidInput(e)
|
||||
if not template:
|
||||
raise exceptions.InvalidInput('The nsd file is empty')
|
||||
body[_NS]['nsd_template'] = template
|
||||
|
||||
if parsed_args.param_file:
|
||||
with open(parsed_args.param_file) as f:
|
||||
param_yaml = f.read()
|
||||
try:
|
||||
param_yaml = yaml.load(
|
||||
param_yaml, Loader=yaml.SafeLoader)
|
||||
except yaml.YAMLError as e:
|
||||
raise exceptions.InvalidInput(e)
|
||||
if not param_yaml:
|
||||
raise exceptions.InvalidInput('The parameter file is empty')
|
||||
body[_NS]['attributes'] = {'param_values': param_yaml}
|
||||
tackerV10.update_dict(parsed_args, body[_NS],
|
||||
['tenant_id', 'name', 'description',
|
||||
'nsd_id', 'vim_id'])
|
||||
return body
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
ns = client.create_ns(self.args2body(parsed_args))
|
||||
display_columns, columns = _get_columns(ns[_NS])
|
||||
data = utils.get_item_properties(
|
||||
sdk_utils.DictModel(ns[_NS]),
|
||||
columns)
|
||||
lstdata = list(data)
|
||||
for index, value in enumerate(lstdata):
|
||||
if value is None:
|
||||
lstdata[index] = ''
|
||||
data = tuple(lstdata)
|
||||
return (display_columns, data)
|
||||
|
||||
|
||||
class DeleteNS(command.Command):
|
||||
_description = _("Delete NS(s).")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(DeleteNS, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
_NS,
|
||||
metavar="<NS>",
|
||||
nargs="+",
|
||||
help=_("NS(s) to delete (name or ID)")
|
||||
)
|
||||
parser.add_argument(
|
||||
'--force',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help=_('Force delete Network Service')
|
||||
)
|
||||
return parser
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
if parsed_args.force:
|
||||
body = {_NS: {'attributes': {'force': True}}}
|
||||
else:
|
||||
body = dict()
|
||||
return body
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
failure = False
|
||||
deleted_ids = []
|
||||
failed_items = {}
|
||||
body = self.args2body(parsed_args)
|
||||
for resource_id in parsed_args.ns:
|
||||
try:
|
||||
obj = tackerV10.find_resourceid_by_name_or_id(
|
||||
client, _NS, resource_id)
|
||||
client.delete_ns(obj, body)
|
||||
deleted_ids.append(resource_id)
|
||||
except Exception as e:
|
||||
failure = True
|
||||
failed_items[resource_id] = e
|
||||
if failure:
|
||||
msg = ''
|
||||
if deleted_ids:
|
||||
msg = (_('Successfully deleted %(resource)s(s):'
|
||||
' %(deleted_list)s') % {'deleted_list':
|
||||
', '.join(deleted_ids),
|
||||
'resource': _NS})
|
||||
err_msg = _("\n\nUnable to delete the below"
|
||||
" %s(s):") % _NS
|
||||
for failed_id, error in failed_items.iteritems():
|
||||
err_msg += (_('\n Cannot delete %(failed_id)s: %(error)s')
|
||||
% {'failed_id': failed_id,
|
||||
'error': error})
|
||||
msg += err_msg
|
||||
raise exceptions.CommandError(msg)
|
||||
else:
|
||||
print((_('All specified %(resource)s(s) deleted successfully')
|
||||
% {'resource': _NS}))
|
||||
return
|
||||
|
||||
|
||||
class ListNS(command.Lister):
|
||||
_description = ("List (NS)s that belong to a given tenant.")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListNS, self).get_parser(prog_name)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
data = client.list_nss()
|
||||
headers, columns = tacker_osc_utils.get_column_definitions(
|
||||
_attr_map, long_listing=None)
|
||||
return (headers,
|
||||
(utils.get_dict_properties(
|
||||
s, columns,
|
||||
) for s in data[_NS + 's']))
|
||||
|
||||
|
||||
class ShowNS(command.ShowOne):
|
||||
_description = _("Display NS details")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ShowNS, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
_NS,
|
||||
metavar="<NS>",
|
||||
help=_("NS to display (name or ID)")
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
obj_id = tackerV10.find_resourceid_by_name_or_id(
|
||||
client, _NS, parsed_args.ns)
|
||||
obj = client.show_ns(obj_id)
|
||||
display_columns, columns = _get_columns(obj[_NS])
|
||||
data = utils.get_item_properties(
|
||||
sdk_utils.DictModel(obj[_NS]),
|
||||
columns)
|
||||
lstdata = list(data)
|
||||
for index, value in enumerate(lstdata):
|
||||
if value is None:
|
||||
lstdata[index] = ''
|
||||
data = tuple(lstdata)
|
||||
return (display_columns, data)
|
||||
@@ -1,223 +0,0 @@
|
||||
# Copyright 2018 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 yaml
|
||||
|
||||
from osc_lib.command import command
|
||||
from osc_lib import utils
|
||||
|
||||
from tackerclient.common import exceptions
|
||||
from tackerclient.i18n import _
|
||||
from tackerclient.osc import sdk_utils
|
||||
from tackerclient.osc import utils as tacker_osc_utils
|
||||
from tackerclient.tacker import v1_0 as tackerV10
|
||||
|
||||
_attr_map = (
|
||||
('id', 'ID', tacker_osc_utils.LIST_BOTH),
|
||||
('name', 'Name', tacker_osc_utils.LIST_BOTH),
|
||||
('template_source', 'Template_Source',
|
||||
tacker_osc_utils.LIST_BOTH),
|
||||
('description', 'Description', tacker_osc_utils.LIST_BOTH),
|
||||
)
|
||||
|
||||
_NSD = 'nsd'
|
||||
|
||||
_formatters = {
|
||||
'attributes': tacker_osc_utils.format_dict_with_indention,
|
||||
}
|
||||
|
||||
|
||||
def _get_columns(item):
|
||||
column_map = {
|
||||
'tenant_id': 'project_id',
|
||||
}
|
||||
return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map)
|
||||
|
||||
|
||||
class CreateNSD(command.ShowOne):
|
||||
_description = _("Create a new NSD.")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CreateNSD, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'name', metavar='NAME',
|
||||
help=_('Name for NSD'))
|
||||
parser.add_argument(
|
||||
'--tenant-id', metavar='TENANT_ID',
|
||||
help=_('The owner tenant ID or project ID'))
|
||||
parser.add_argument(
|
||||
'--nsd-file',
|
||||
required=True,
|
||||
help=_('YAML file with NSD parameters'))
|
||||
parser.add_argument(
|
||||
'--description',
|
||||
help=_('Set a description for the NSD'))
|
||||
return parser
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
body = {_NSD: {}}
|
||||
nsd = None
|
||||
if not parsed_args.nsd_file:
|
||||
raise exceptions.InvalidInput("Invalid input for nsd file")
|
||||
with open(parsed_args.nsd_file) as f:
|
||||
nsd = f.read()
|
||||
try:
|
||||
nsd = yaml.load(nsd, Loader=yaml.SafeLoader)
|
||||
except yaml.YAMLError as e:
|
||||
raise exceptions.InvalidInput(e)
|
||||
if not nsd:
|
||||
raise exceptions.InvalidInput("nsd file is empty")
|
||||
body[_NSD]['attributes'] = {'nsd': nsd}
|
||||
tackerV10.update_dict(parsed_args, body[_NSD],
|
||||
['tenant_id', 'name', 'description'])
|
||||
return body
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
nsd = client.create_nsd(self.args2body(parsed_args))
|
||||
display_columns, columns = _get_columns(nsd[_NSD])
|
||||
nsd[_NSD]['attributes']['nsd'] = yaml.load(
|
||||
nsd[_NSD]['attributes']['nsd'])
|
||||
data = utils.get_item_properties(
|
||||
sdk_utils.DictModel(nsd[_NSD]),
|
||||
columns, formatters=_formatters)
|
||||
return (display_columns, data)
|
||||
|
||||
|
||||
class DeleteNSD(command.Command):
|
||||
_description = _("Delete NSD(s).")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(DeleteNSD, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
_NSD,
|
||||
metavar="<NSD>",
|
||||
nargs="+",
|
||||
help=_("NSD(s) to delete (name or ID)")
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
failure = False
|
||||
deleted_ids = []
|
||||
failed_items = {}
|
||||
for resource_id in parsed_args.nsd:
|
||||
try:
|
||||
obj = tackerV10.find_resourceid_by_name_or_id(
|
||||
client, _NSD, resource_id)
|
||||
client.delete_nsd(obj)
|
||||
deleted_ids.append(resource_id)
|
||||
except Exception as e:
|
||||
failure = True
|
||||
failed_items[resource_id] = e
|
||||
if failure:
|
||||
msg = ''
|
||||
if deleted_ids:
|
||||
msg = (_('Successfully deleted %(resource)s(s):'
|
||||
' %(deleted_list)s') % {'deleted_list':
|
||||
', '.join(deleted_ids),
|
||||
'resource': _NSD})
|
||||
err_msg = _("\n\nUnable to delete the below"
|
||||
" %s(s):") % _NSD
|
||||
for failed_id, error in failed_items.iteritems():
|
||||
err_msg += (_('\n Cannot delete %(failed_id)s: %(error)s')
|
||||
% {'failed_id': failed_id,
|
||||
'error': error})
|
||||
msg += err_msg
|
||||
raise exceptions.CommandError(msg)
|
||||
else:
|
||||
print((_('All specified %(resource)s(s) deleted successfully')
|
||||
% {'resource': _NSD}))
|
||||
return
|
||||
|
||||
|
||||
class ListNSD(command.Lister):
|
||||
_description = ("List (NSD)s that belong to a given tenant.")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListNSD, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'--template-source',
|
||||
help=_("List NSD with specified template source. Available \
|
||||
options are 'onboared' (default), 'inline' or 'all'"),
|
||||
action='store',
|
||||
default='onboarded')
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
data = client.list_nsds()
|
||||
headers, columns = tacker_osc_utils.get_column_definitions(
|
||||
_attr_map, long_listing=None)
|
||||
return (headers,
|
||||
(utils.get_dict_properties(
|
||||
s, columns,
|
||||
) for s in data[_NSD + 's']))
|
||||
|
||||
|
||||
class ShowNSD(command.ShowOne):
|
||||
_description = _("Display NSD details")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ShowNSD, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
_NSD,
|
||||
metavar="<NSD>",
|
||||
help=_("NSD to display (name or ID)")
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
obj_id = tackerV10.find_resourceid_by_name_or_id(
|
||||
client, _NSD, parsed_args.nsd)
|
||||
obj = client.show_nsd(obj_id)
|
||||
obj[_NSD]['attributes']['nsd'] = yaml.load(
|
||||
obj[_NSD]['attributes']['nsd'])
|
||||
display_columns, columns = _get_columns(obj[_NSD])
|
||||
data = utils.get_item_properties(
|
||||
sdk_utils.DictModel(obj[_NSD]),
|
||||
columns,
|
||||
formatters=_formatters)
|
||||
return (display_columns, data)
|
||||
|
||||
|
||||
class ShowTemplateNSD(command.ShowOne):
|
||||
_description = _("Display NSD Template details")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ShowTemplateNSD, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
_NSD,
|
||||
metavar="<NSD>",
|
||||
help=_("NSD to display (name or ID)")
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
obj_id = tackerV10.find_resourceid_by_name_or_id(
|
||||
client, _NSD, parsed_args.nsd)
|
||||
obj = client.show_nsd(obj_id)
|
||||
obj[_NSD]['attributes']['nsd'] = yaml.load(
|
||||
obj[_NSD]['attributes']['nsd'])
|
||||
data = utils.get_item_properties(
|
||||
sdk_utils.DictModel(obj[_NSD]),
|
||||
(u'attributes',),
|
||||
formatters=_formatters)
|
||||
data = (data or _('Unable to display NSD template!'))
|
||||
return ((u'attributes',), data)
|
||||
@@ -1,270 +0,0 @@
|
||||
# Copyright 2016 Brocade Communications Systems Inc
|
||||
# All Rights Reserved.
|
||||
#
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import yaml
|
||||
|
||||
from osc_lib.command import command
|
||||
from osc_lib import utils
|
||||
from oslo_utils import strutils
|
||||
|
||||
from tackerclient.common import exceptions
|
||||
from tackerclient.i18n import _
|
||||
from tackerclient.osc import sdk_utils
|
||||
from tackerclient.osc import utils as tacker_osc_utils
|
||||
from tackerclient.tacker import v1_0 as tackerV10
|
||||
from tackerclient.tacker.v1_0.nfvo import vim_utils
|
||||
|
||||
_attr_map = (
|
||||
('id', 'ID', tacker_osc_utils.LIST_BOTH),
|
||||
('name', 'Name', tacker_osc_utils.LIST_BOTH),
|
||||
('tenant_id', 'Tenant_id', tacker_osc_utils.LIST_BOTH),
|
||||
('type', 'Type', tacker_osc_utils.LIST_BOTH),
|
||||
('is_default', 'Is Default',
|
||||
tacker_osc_utils.LIST_BOTH),
|
||||
('placement_attr', 'Placement attribution',
|
||||
tacker_osc_utils.LIST_LONG_ONLY),
|
||||
('status', 'Status', tacker_osc_utils.LIST_BOTH),
|
||||
)
|
||||
|
||||
_VIM = 'vim'
|
||||
|
||||
|
||||
class ListVIM(command.Lister):
|
||||
_description = _("List VIMs that belong to a given tenant.")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListVIM, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'--long',
|
||||
action='store_true',
|
||||
help=_("List additional fields in output")
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
data = client.list_vims()
|
||||
headers, columns = tacker_osc_utils.get_column_definitions(
|
||||
_attr_map, long_listing=parsed_args.long)
|
||||
return (headers,
|
||||
(utils.get_dict_properties(
|
||||
s, columns,
|
||||
) for s in data[_VIM + 's']))
|
||||
|
||||
|
||||
class ShowVIM(command.ShowOne):
|
||||
_description = _("Display VIM details")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ShowVIM, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
_VIM,
|
||||
metavar="<VIM>",
|
||||
help=_("VIM to display (name or ID)")
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
obj_id = tackerV10.find_resourceid_by_name_or_id(
|
||||
client, _VIM, parsed_args.vim)
|
||||
obj = client.show_vim(obj_id)
|
||||
display_columns, columns = _get_columns(obj[_VIM])
|
||||
data = utils.get_item_properties(
|
||||
sdk_utils.DictModel(obj[_VIM]),
|
||||
columns,
|
||||
formatters=_formatters)
|
||||
return (display_columns, data)
|
||||
|
||||
|
||||
class CreateVIM(command.ShowOne):
|
||||
_description = _("Register a new VIM")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CreateVIM, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'name', metavar='NAME',
|
||||
help=_('Set a name for the VIM'))
|
||||
parser.add_argument(
|
||||
'--tenant-id', metavar='TENANT_ID',
|
||||
help=_('The owner tenant ID or project ID'))
|
||||
parser.add_argument(
|
||||
'--config-file',
|
||||
required=True,
|
||||
help=_('YAML file with VIM configuration parameters'))
|
||||
parser.add_argument(
|
||||
'--description',
|
||||
help=_('Set a description for the VIM'))
|
||||
parser.add_argument(
|
||||
'--is-default',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help=_('Set as default VIM'))
|
||||
return parser
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
body = {_VIM: {}}
|
||||
if parsed_args.config_file:
|
||||
with open(parsed_args.config_file) as f:
|
||||
vim_config = f.read()
|
||||
try:
|
||||
config_param = yaml.load(vim_config,
|
||||
Loader=yaml.SafeLoader)
|
||||
except yaml.YAMLError as e:
|
||||
raise exceptions.InvalidInput(e)
|
||||
vim_obj = body[_VIM]
|
||||
try:
|
||||
auth_url = config_param.pop('auth_url')
|
||||
except KeyError:
|
||||
raise exceptions.TackerClientException(message='Auth URL must be '
|
||||
'specified',
|
||||
status_code=404)
|
||||
vim_obj['auth_url'] = vim_utils.validate_auth_url(auth_url).geturl()
|
||||
vim_utils.args2body_vim(config_param, vim_obj)
|
||||
tackerV10.update_dict(parsed_args, body[_VIM],
|
||||
['tenant_id', 'name', 'description',
|
||||
'is_default'])
|
||||
return body
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
vim = client.create_vim(self.args2body(parsed_args))
|
||||
display_columns, columns = _get_columns(vim[_VIM])
|
||||
data = utils.get_item_properties(
|
||||
sdk_utils.DictModel(vim[_VIM]),
|
||||
columns, formatters=_formatters)
|
||||
return (display_columns, data)
|
||||
|
||||
|
||||
class DeleteVIM(command.Command):
|
||||
_description = _("Delete VIM(s).")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(DeleteVIM, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
_VIM,
|
||||
metavar="<VIM>",
|
||||
nargs="+",
|
||||
help=_("VIM(s) to delete (name or ID)")
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
failure = False
|
||||
deleted_ids = []
|
||||
failed_items = {}
|
||||
for resource_id in parsed_args.vim:
|
||||
try:
|
||||
obj = tackerV10.find_resourceid_by_name_or_id(
|
||||
client, _VIM, resource_id)
|
||||
client.delete_vim(obj)
|
||||
deleted_ids.append(resource_id)
|
||||
except Exception as e:
|
||||
failure = True
|
||||
failed_items[resource_id] = e
|
||||
if failure:
|
||||
msg = ''
|
||||
if deleted_ids:
|
||||
msg = (_('Successfully deleted %(resource)s(s):'
|
||||
' %(deleted_list)s') % {'deleted_list':
|
||||
', '.join(deleted_ids),
|
||||
'resource': _VIM})
|
||||
err_msg = _("\n\nUnable to delete the below"
|
||||
" %s(s):") % _VIM
|
||||
for failed_id, error in failed_items.iteritems():
|
||||
err_msg += (_('\n Cannot delete %(failed_id)s: %(error)s')
|
||||
% {'failed_id': failed_id,
|
||||
'error': error})
|
||||
msg += err_msg
|
||||
raise exceptions.CommandError(msg)
|
||||
else:
|
||||
print((_('All specified %(resource)s(s) deleted successfully')
|
||||
% {'resource': _VIM}))
|
||||
return
|
||||
|
||||
|
||||
class UpdateVIM(command.ShowOne):
|
||||
_description = _("Update VIM.")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(UpdateVIM, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'id', metavar="VIM",
|
||||
help=_('ID or name of %s to update') % _VIM)
|
||||
parser.add_argument(
|
||||
'--config-file',
|
||||
required=False,
|
||||
help=_('YAML file with VIM configuration parameters'))
|
||||
parser.add_argument(
|
||||
'--name',
|
||||
help=_('New name for the VIM'))
|
||||
parser.add_argument(
|
||||
'--description',
|
||||
help=_('New description for the VIM'))
|
||||
parser.add_argument(
|
||||
'--is-default',
|
||||
type=strutils.bool_from_string,
|
||||
metavar='{True,False}',
|
||||
help=_('Indicate whether the VIM is used as default'))
|
||||
return parser
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
body = {_VIM: {}}
|
||||
config_param = None
|
||||
# config arg passed as data overrides config yaml when both args passed
|
||||
if parsed_args.config_file:
|
||||
with open(parsed_args.config_file) as f:
|
||||
config_yaml = f.read()
|
||||
try:
|
||||
config_param = yaml.load(config_yaml)
|
||||
except yaml.YAMLError as e:
|
||||
raise exceptions.InvalidInput(e)
|
||||
vim_obj = body[_VIM]
|
||||
if config_param is not None:
|
||||
vim_utils.args2body_vim(config_param, vim_obj)
|
||||
tackerV10.update_dict(parsed_args, body[_VIM],
|
||||
['tenant_id', 'name', 'description',
|
||||
'is_default'])
|
||||
# type attribute is read-only, it can't be updated, so remove it
|
||||
# in update method
|
||||
body[_VIM].pop('type', None)
|
||||
return body
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
obj_id = tackerV10.find_resourceid_by_name_or_id(
|
||||
client, _VIM, parsed_args.id)
|
||||
vim = client.update_vim(obj_id, self.args2body(parsed_args))
|
||||
display_columns, columns = _get_columns(vim[_VIM])
|
||||
data = utils.get_item_properties(
|
||||
sdk_utils.DictModel(vim[_VIM]), columns,
|
||||
formatters=_formatters)
|
||||
return (display_columns, data)
|
||||
|
||||
|
||||
_formatters = {
|
||||
'auth_cred': tacker_osc_utils.format_dict_with_indention,
|
||||
'placement_attr': tacker_osc_utils.format_dict_with_indention,
|
||||
'vim_project': tacker_osc_utils.format_dict_with_indention,
|
||||
}
|
||||
|
||||
|
||||
def _get_columns(item):
|
||||
column_map = {
|
||||
'tenant_id': 'project_id',
|
||||
}
|
||||
return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map)
|
||||
@@ -1,541 +0,0 @@
|
||||
# Copyright 2018 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 yaml
|
||||
|
||||
from osc_lib.command import command
|
||||
from osc_lib import utils
|
||||
|
||||
from tackerclient.common import exceptions
|
||||
from tackerclient.i18n import _
|
||||
from tackerclient.osc import sdk_utils
|
||||
from tackerclient.osc import utils as tacker_osc_utils
|
||||
from tackerclient.tacker import v1_0 as tackerV10
|
||||
|
||||
_VNFFG = 'vnffg' # VNF Forwarding Graph
|
||||
_NFP = 'nfp' # Network Forwarding Path
|
||||
_SFC = 'sfc' # Service Function Chain
|
||||
_FC = 'classifier' # Flow Classifier
|
||||
|
||||
nfps_path = '/nfps'
|
||||
fcs_path = '/classifiers'
|
||||
sfcs_path = '/sfcs'
|
||||
|
||||
DEFAULT_ERROR_REASON_LENGTH = 100
|
||||
|
||||
_attr_map_vnffg = (
|
||||
('id', 'ID', tacker_osc_utils.LIST_BOTH),
|
||||
('name', 'Name', tacker_osc_utils.LIST_BOTH),
|
||||
('ns_id', 'NS ID', tacker_osc_utils.LIST_BOTH),
|
||||
('vnffgd_id', 'VNFFGD ID', tacker_osc_utils.LIST_BOTH),
|
||||
('status', 'Status', tacker_osc_utils.LIST_BOTH),
|
||||
('description', 'Description', tacker_osc_utils.LIST_LONG_ONLY),
|
||||
)
|
||||
|
||||
_attr_map_nfp = (
|
||||
('id', 'ID', tacker_osc_utils.LIST_BOTH),
|
||||
('name', 'Name', tacker_osc_utils.LIST_BOTH),
|
||||
('status', 'Status', tacker_osc_utils.LIST_BOTH),
|
||||
('vnffg_id', 'VNFFG ID', tacker_osc_utils.LIST_BOTH),
|
||||
('path_id', 'Path ID', tacker_osc_utils.LIST_BOTH),
|
||||
)
|
||||
|
||||
_attr_map_sfc = (
|
||||
('id', 'ID', tacker_osc_utils.LIST_BOTH),
|
||||
('status', 'Status', tacker_osc_utils.LIST_BOTH),
|
||||
('nfp_id', 'NFP ID', tacker_osc_utils.LIST_BOTH),
|
||||
)
|
||||
|
||||
_attr_map_fc = (
|
||||
('id', 'ID', tacker_osc_utils.LIST_BOTH),
|
||||
('name', 'Name', tacker_osc_utils.LIST_BOTH),
|
||||
('status', 'Status', tacker_osc_utils.LIST_BOTH),
|
||||
('nfp_id', 'NFP ID', tacker_osc_utils.LIST_BOTH),
|
||||
('chain_id', 'Chain ID', tacker_osc_utils.LIST_BOTH),
|
||||
)
|
||||
|
||||
_formatters = {
|
||||
'attributes': tacker_osc_utils.format_dict_with_indention,
|
||||
'match': tacker_osc_utils.format_dict_with_indention,
|
||||
'chain': tacker_osc_utils.format_dict_with_indention,
|
||||
}
|
||||
|
||||
|
||||
def _get_columns(item):
|
||||
column_map = {
|
||||
'tenant_id': 'project_id',
|
||||
}
|
||||
return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map)
|
||||
|
||||
|
||||
class CreateVNFFG(command.ShowOne):
|
||||
_description = _("Create a new VNFFG.")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CreateVNFFG, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'name', metavar='NAME',
|
||||
help=_('Set a name for the VNFFG'))
|
||||
parser.add_argument(
|
||||
'--tenant-id', metavar='TENANT_ID',
|
||||
help=_('The owner tenant ID'))
|
||||
vnffgd_group = parser.add_mutually_exclusive_group(required=True)
|
||||
vnffgd_group.add_argument(
|
||||
'--vnffgd-id',
|
||||
help=_('VNFFGD ID to use as template to create VNFFG'))
|
||||
vnffgd_group.add_argument(
|
||||
'--vnffgd-name',
|
||||
help=_('VNFFGD Name to use as template to create VNFFG'))
|
||||
vnffgd_group.add_argument(
|
||||
'--vnffgd-template',
|
||||
help=_('VNFFGD file to create VNFFG'))
|
||||
parser.add_argument(
|
||||
'--vnf-mapping',
|
||||
help=_('List of logical VNFD name to VNF instance name mapping. '
|
||||
'Example: VNF1:my_vnf1,VNF2:my_vnf2'))
|
||||
parser.add_argument(
|
||||
'--symmetrical',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help=_('Should a reverse path be created for the NFP '
|
||||
'(True or False)'))
|
||||
parser.add_argument(
|
||||
'--param-file',
|
||||
help=_('YAML file with specific VNFFG parameters'))
|
||||
parser.add_argument(
|
||||
'--description',
|
||||
help=_('Set a description for the VNFFG'))
|
||||
return parser
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
body = {_VNFFG: {}}
|
||||
body[_VNFFG]['attributes'] = {}
|
||||
|
||||
client = self.app.client_manager.tackerclient
|
||||
if parsed_args.vnf_mapping:
|
||||
_vnf_mapping = dict()
|
||||
_vnf_mappings = parsed_args.vnf_mapping.split(",")
|
||||
for mapping in _vnf_mappings:
|
||||
vnfd_name, vnf = mapping.split(":", 1)
|
||||
_vnf_mapping[vnfd_name] = \
|
||||
tackerV10.find_resourceid_by_name_or_id(
|
||||
client, 'vnf', vnf)
|
||||
parsed_args.vnf_mapping = _vnf_mapping
|
||||
|
||||
if parsed_args.vnffgd_name:
|
||||
_id = tackerV10.find_resourceid_by_name_or_id(
|
||||
client, 'vnffgd', parsed_args.vnffgd_name)
|
||||
parsed_args.vnffgd_id = _id
|
||||
elif parsed_args.vnffgd_template:
|
||||
with open(parsed_args.vnffgd_template) as f:
|
||||
template = f.read()
|
||||
try:
|
||||
template = yaml.load(template, Loader=yaml.SafeLoader)
|
||||
except yaml.YAMLError as e:
|
||||
raise exceptions.InvalidInput(e)
|
||||
if not template:
|
||||
raise exceptions.InvalidInput('The vnffgd file is empty')
|
||||
body[_VNFFG]['vnffgd_template'] = template
|
||||
|
||||
if parsed_args.param_file:
|
||||
with open(parsed_args.param_file) as f:
|
||||
param_yaml = f.read()
|
||||
try:
|
||||
param_yaml = yaml.load(
|
||||
param_yaml, Loader=yaml.SafeLoader)
|
||||
except yaml.YAMLError as e:
|
||||
raise exceptions.InvalidInput(e)
|
||||
if not param_yaml:
|
||||
raise exceptions.InvalidInput('The parameter file is empty')
|
||||
body[_VNFFG]['attributes'] = {'param_values': param_yaml}
|
||||
tackerV10.update_dict(parsed_args, body[_VNFFG],
|
||||
['tenant_id', 'name', 'vnffgd_id',
|
||||
'symmetrical', 'vnf_mapping', 'description'])
|
||||
return body
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
vnffg = client.create_vnffg(self.args2body(parsed_args))
|
||||
display_columns, columns = _get_columns(vnffg[_VNFFG])
|
||||
data = utils.get_item_properties(
|
||||
sdk_utils.DictModel(vnffg[_VNFFG]),
|
||||
columns,
|
||||
formatters=_formatters)
|
||||
return (display_columns, data)
|
||||
|
||||
|
||||
class DeleteVNFFG(command.Command):
|
||||
_description = _("Delete VNFFG(s).")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(DeleteVNFFG, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
_VNFFG,
|
||||
metavar="<VNFFG>",
|
||||
nargs="+",
|
||||
help=_("VNFFG(s) to delete (name or ID)")
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
failure = False
|
||||
deleted_ids = []
|
||||
failed_items = {}
|
||||
for resource_id in parsed_args.vnffg:
|
||||
try:
|
||||
obj = tackerV10.find_resourceid_by_name_or_id(
|
||||
client, _VNFFG, resource_id)
|
||||
client.delete_vnffg(obj)
|
||||
deleted_ids.append(resource_id)
|
||||
except Exception as e:
|
||||
failure = True
|
||||
failed_items[resource_id] = e
|
||||
if failure:
|
||||
msg = ''
|
||||
if deleted_ids:
|
||||
msg = (_('Successfully deleted %(resource)s(s):'
|
||||
' %(deleted_list)s') % {'deleted_list':
|
||||
', '.join(deleted_ids),
|
||||
'resource': _VNFFG})
|
||||
err_msg = _("\n\nUnable to delete the below"
|
||||
" %s(s):") % _VNFFG
|
||||
for failed_id, error in failed_items.iteritems():
|
||||
err_msg += (_('\n Cannot delete %(failed_id)s: %(error)s')
|
||||
% {'failed_id': failed_id,
|
||||
'error': error})
|
||||
msg += err_msg
|
||||
raise exceptions.CommandError(msg)
|
||||
else:
|
||||
print((_('All specified %(resource)s(s) deleted successfully')
|
||||
% {'resource': _VNFFG}))
|
||||
return
|
||||
|
||||
|
||||
class UpdateVNFFG(command.ShowOne):
|
||||
_description = _("Update VNFFG.")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(UpdateVNFFG, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
_VNFFG,
|
||||
metavar="<VNFFG>",
|
||||
help=_('VNFFG to update (name or ID)'))
|
||||
parser.add_argument(
|
||||
'--vnffgd-template',
|
||||
help=_('VNFFGD file to update VNFFG'))
|
||||
parser.add_argument(
|
||||
'--vnf-mapping',
|
||||
help=_('List of logical VNFD name to VNF instance name mapping. '
|
||||
'Example: VNF1:my_vnf1,VNF2:my_vnf2'))
|
||||
parser.add_argument(
|
||||
'--symmetrical',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help=_('Should a reverse path be created for the NFP'))
|
||||
parser.add_argument(
|
||||
'--description',
|
||||
help=_('Set a description for the VNFFG'))
|
||||
return parser
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
body = {_VNFFG: {}}
|
||||
body[_VNFFG]['attributes'] = {}
|
||||
|
||||
client = self.app.client_manager.tackerclient
|
||||
|
||||
if parsed_args.vnf_mapping:
|
||||
_vnf_mapping = dict()
|
||||
_vnf_mappings = parsed_args.vnf_mapping.split(",")
|
||||
for mapping in _vnf_mappings:
|
||||
vnfd_name, vnf = mapping.split(":", 1)
|
||||
_vnf_mapping[vnfd_name] = \
|
||||
tackerV10.find_resourceid_by_name_or_id(
|
||||
client, 'vnf', vnf)
|
||||
parsed_args.vnf_mapping = _vnf_mapping
|
||||
|
||||
if parsed_args.vnffgd_template:
|
||||
with open(parsed_args.vnffgd_template) as f:
|
||||
template = f.read()
|
||||
try:
|
||||
template = yaml.load(
|
||||
template, Loader=yaml.SafeLoader)
|
||||
except yaml.YAMLError as e:
|
||||
raise exceptions.InvalidInput(e)
|
||||
if not template:
|
||||
raise exceptions.InvalidInput('The vnffgd file is empty')
|
||||
body[_VNFFG]['vnffgd_template'] = template
|
||||
|
||||
if parsed_args.param_file:
|
||||
with open(parsed_args.param_file) as f:
|
||||
param_yaml = f.read()
|
||||
try:
|
||||
param_yaml = yaml.load(
|
||||
param_yaml, Loader=yaml.SafeLoader)
|
||||
except yaml.YAMLError as e:
|
||||
raise exceptions.InvalidInput(e)
|
||||
if not param_yaml:
|
||||
raise exceptions.InvalidInput('The parameter file is empty')
|
||||
body[_VNFFG]['attributes'] = {'param_values': param_yaml}
|
||||
tackerV10.update_dict(parsed_args, body[self.resource],
|
||||
['vnf_mapping', 'symmetrical', 'description'])
|
||||
return body
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
obj_id = tackerV10.find_resourceid_by_name_or_id(
|
||||
client, _VNFFG, parsed_args.vnffg)
|
||||
vnffg = client.update_vnffg(obj_id, self.args2body(parsed_args))
|
||||
display_columns, columns = _get_columns(vnffg[_VNFFG])
|
||||
data = utils.get_item_properties(
|
||||
sdk_utils.DictModel(vnffg[_VNFFG]),
|
||||
columns,
|
||||
formatters=_formatters)
|
||||
return (display_columns, data)
|
||||
|
||||
|
||||
class ListVNFFG(command.Lister):
|
||||
_description = ("List VNFFG(s) that belong to a given tenant.")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListVNFFG, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'--long',
|
||||
action='store_true',
|
||||
help=_('List additional fields in output')
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
data = client.list_vnffgs()
|
||||
headers, columns = tacker_osc_utils.get_column_definitions(
|
||||
_attr_map_vnffg, long_listing=parsed_args.long)
|
||||
return (headers,
|
||||
(utils.get_dict_properties(
|
||||
s, columns,
|
||||
) for s in data[_VNFFG + 's']))
|
||||
|
||||
|
||||
class ShowVNFFG(command.ShowOne):
|
||||
_description = _("Display VNFFG details")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ShowVNFFG, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
_VNFFG,
|
||||
metavar="<VNFFG>",
|
||||
help=_('VNFFG to display (name or ID)')
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
obj_id = tackerV10.find_resourceid_by_name_or_id(
|
||||
client, _VNFFG, parsed_args.vnffg)
|
||||
obj = client.show_vnffg(obj_id)
|
||||
display_columns, columns = _get_columns(obj[_VNFFG])
|
||||
data = utils.get_item_properties(
|
||||
sdk_utils.DictModel(obj[_VNFFG]),
|
||||
columns,
|
||||
formatters=_formatters)
|
||||
return (display_columns, data)
|
||||
|
||||
|
||||
class ListNFP(command.Lister):
|
||||
_description = ("List NFP(s) that belong to a given tenant.")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListNFP, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'--vnffg-id',
|
||||
help=_('List NFP(s) with specific VNFFG ID'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
_params = {}
|
||||
if parsed_args.vnffg_id:
|
||||
_params['vnffg_id'] = parsed_args.vnffg_id
|
||||
nfps = client.list('nfps', nfps_path, True, **_params)
|
||||
for nfp in nfps['nfps']:
|
||||
error_reason = nfp.get('error_reason', None)
|
||||
if error_reason and \
|
||||
len(error_reason) > DEFAULT_ERROR_REASON_LENGTH:
|
||||
nfp['error_reason'] = error_reason[
|
||||
:DEFAULT_ERROR_REASON_LENGTH]
|
||||
nfp['error_reason'] += '...'
|
||||
data = {}
|
||||
data['nfps'] = nfps['nfps']
|
||||
data = client.list_nfps()
|
||||
headers, columns = tacker_osc_utils.get_column_definitions(
|
||||
_attr_map_nfp, long_listing=None)
|
||||
return (headers,
|
||||
(utils.get_dict_properties(
|
||||
s, columns,
|
||||
) for s in data[_NFP + 's']))
|
||||
|
||||
|
||||
class ShowNFP(command.ShowOne):
|
||||
_description = _("Display NFP details")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ShowNFP, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
_NFP,
|
||||
metavar="<NFP>",
|
||||
help=_('NFP to display (name or ID)')
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
obj_id = tackerV10.find_resourceid_by_name_or_id(
|
||||
client, _NFP, parsed_args.nfp)
|
||||
obj = client.show_nfp(obj_id)
|
||||
display_columns, columns = _get_columns(obj[_NFP])
|
||||
data = utils.get_item_properties(
|
||||
sdk_utils.DictModel(obj[_NFP]),
|
||||
columns)
|
||||
return (display_columns, data)
|
||||
|
||||
|
||||
class ListFC(command.Lister):
|
||||
_description = ("List flow classifier(s) that belong to a given tenant.")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListFC, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'--nfp-id',
|
||||
help=_('List flow classifier(s) with specific nfp id'))
|
||||
parser.add_argument(
|
||||
'--tenant-id', metavar='TENANT_ID',
|
||||
help=_('The owner tenant ID or project ID'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
_params = {}
|
||||
if parsed_args.nfp_id:
|
||||
_params['nfp_id'] = parsed_args.nfp_id
|
||||
if parsed_args.tenant_id:
|
||||
_params['tenant_id'] = parsed_args.tenant_id
|
||||
classifiers = client.list('classifiers', fcs_path, True,
|
||||
**_params)
|
||||
for classifier in classifiers['classifiers']:
|
||||
error_reason = classifier.get('error_reason', None)
|
||||
if error_reason and \
|
||||
len(error_reason) > DEFAULT_ERROR_REASON_LENGTH:
|
||||
classifier['error_reason'] = error_reason[
|
||||
:DEFAULT_ERROR_REASON_LENGTH]
|
||||
classifier['error_reason'] += '...'
|
||||
data = {}
|
||||
data['classifiers'] = classifiers['classifiers']
|
||||
data = client.list_classifiers()
|
||||
headers, columns = tacker_osc_utils.get_column_definitions(
|
||||
_attr_map_fc, long_listing=None)
|
||||
return (headers,
|
||||
(utils.get_dict_properties(
|
||||
s, columns,
|
||||
) for s in data[_FC + 's']))
|
||||
|
||||
|
||||
class ShowFC(command.ShowOne):
|
||||
_description = _("Display flow classifier details")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ShowFC, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
_FC,
|
||||
metavar="<Classifier ID>",
|
||||
help=_('Flow Classifier to display (name or ID)')
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
obj_id = tackerV10.find_resourceid_by_name_or_id(
|
||||
client, _FC, parsed_args.classifier)
|
||||
obj = client.show_classifier(obj_id)
|
||||
display_columns, columns = _get_columns(obj[_FC])
|
||||
data = utils.get_item_properties(
|
||||
sdk_utils.DictModel(obj[_FC]),
|
||||
columns,
|
||||
formatters=_formatters)
|
||||
return (display_columns, data)
|
||||
|
||||
|
||||
class ListSFC(command.Lister):
|
||||
_description = ("List SFC(s) that belong to a given tenant.")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListSFC, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'--nfp-id',
|
||||
help=_('List SFC(s) with specific nfp id'))
|
||||
parser.add_argument(
|
||||
'--tenant-id', metavar='TENANT_ID',
|
||||
help=_('The owner tenant ID or project ID'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
_params = {}
|
||||
if parsed_args.nfp_id:
|
||||
_params['nfp_id'] = parsed_args.nfp_id
|
||||
if parsed_args.tenant_id:
|
||||
_params['tenant_id'] = parsed_args.tenant_id
|
||||
sfcs = client.list('sfcs', sfcs_path, True, **_params)
|
||||
for chain in sfcs['sfcs']:
|
||||
error_reason = chain.get('error_reason', None)
|
||||
if error_reason and \
|
||||
len(error_reason) > DEFAULT_ERROR_REASON_LENGTH:
|
||||
chain['error_reason'] = error_reason[
|
||||
:DEFAULT_ERROR_REASON_LENGTH]
|
||||
chain['error_reason'] += '...'
|
||||
data = {}
|
||||
data['sfcs'] = sfcs['sfcs']
|
||||
headers, columns = tacker_osc_utils.get_column_definitions(
|
||||
_attr_map_sfc, long_listing=None)
|
||||
return (headers,
|
||||
(utils.get_dict_properties(
|
||||
s, columns,
|
||||
) for s in data[_SFC + 's']))
|
||||
|
||||
|
||||
class ShowSFC(command.ShowOne):
|
||||
_description = _("Display SFC details")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ShowSFC, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
_SFC,
|
||||
metavar="<SFC>",
|
||||
help=_('SFC to display (name or ID)')
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
obj_id = tackerV10.find_resourceid_by_name_or_id(
|
||||
client, _SFC, parsed_args.sfc)
|
||||
obj = client.show_sfc(obj_id)
|
||||
display_columns, columns = _get_columns(obj[_SFC])
|
||||
data = utils.get_item_properties(
|
||||
sdk_utils.DictModel(obj[_SFC]),
|
||||
columns,
|
||||
formatters=_formatters)
|
||||
return (display_columns, data)
|
||||
@@ -1,216 +0,0 @@
|
||||
# Copyright 2018 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 yaml
|
||||
|
||||
from osc_lib.command import command
|
||||
from osc_lib import utils
|
||||
|
||||
from tackerclient.common import exceptions
|
||||
from tackerclient.i18n import _
|
||||
from tackerclient.osc import sdk_utils
|
||||
from tackerclient.osc import utils as tacker_osc_utils
|
||||
from tackerclient.tacker import v1_0 as tackerV10
|
||||
|
||||
_attr_map = (
|
||||
('id', 'ID', tacker_osc_utils.LIST_BOTH),
|
||||
('name', 'Name', tacker_osc_utils.LIST_BOTH),
|
||||
('template_source', 'Template_Source',
|
||||
tacker_osc_utils.LIST_BOTH),
|
||||
('description', 'Description', tacker_osc_utils.LIST_BOTH),
|
||||
)
|
||||
|
||||
_VNFFGD = "vnffgd"
|
||||
|
||||
_formatters = {
|
||||
'template': tacker_osc_utils.format_dict_with_indention,
|
||||
}
|
||||
|
||||
|
||||
def _get_columns(item):
|
||||
column_map = {
|
||||
'tenant_id': 'project_id',
|
||||
}
|
||||
return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map)
|
||||
|
||||
|
||||
class CreateVNFFGD(command.ShowOne):
|
||||
_description = _("Create a new VNFFGD")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CreateVNFFGD, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'name', metavar='NAME',
|
||||
help=_('Name for VNFFGD'))
|
||||
parser.add_argument(
|
||||
'--tenant-id', metavar='TENANT_ID',
|
||||
help=_('The owner tenant ID or project ID'))
|
||||
parser.add_argument(
|
||||
'--vnffgd-file',
|
||||
required=True,
|
||||
help=_('YAML file with VNFFGD parameters'))
|
||||
parser.add_argument(
|
||||
'--description',
|
||||
help=_('Set a description for the VNFFGD'))
|
||||
return parser
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
body = {_VNFFGD: {}}
|
||||
vnffgd = None
|
||||
if not parsed_args.vnffgd_file:
|
||||
raise exceptions.InvalidInput("Invalid input for vnffgd file")
|
||||
with open(parsed_args.vnffgd_file) as f:
|
||||
vnffgd = f.read()
|
||||
try:
|
||||
vnffgd = yaml.load(vnffgd, Loader=yaml.SafeLoader)
|
||||
except yaml.YAMLError as e:
|
||||
raise exceptions.InvalidInput(e)
|
||||
if not vnffgd:
|
||||
raise exceptions.InvalidInput("vnffgd file is empty")
|
||||
body[_VNFFGD]['template'] = {'vnffgd': vnffgd}
|
||||
tackerV10.update_dict(parsed_args, body[_VNFFGD],
|
||||
['tenant_id', 'name', 'description'])
|
||||
return body
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
vnffgd = client.create_vnffgd(self.args2body(parsed_args))
|
||||
display_columns, columns = _get_columns(vnffgd[_VNFFGD])
|
||||
data = utils.get_item_properties(
|
||||
sdk_utils.DictModel(vnffgd[_VNFFGD]),
|
||||
columns, formatters=_formatters)
|
||||
return (display_columns, data)
|
||||
|
||||
|
||||
class DeleteVNFFGD(command.Command):
|
||||
_description = _("Delete VNFFGD(s).")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(DeleteVNFFGD, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
_VNFFGD,
|
||||
metavar="<VNFFGD>",
|
||||
nargs="+",
|
||||
help=_("VNFFGD(s) to delete (name or ID)")
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
failure = False
|
||||
deleted_ids = []
|
||||
failed_items = {}
|
||||
for resource_id in parsed_args.vnffgd:
|
||||
try:
|
||||
obj = tackerV10.find_resourceid_by_name_or_id(
|
||||
client, _VNFFGD, resource_id)
|
||||
client.delete_vnffgd(obj)
|
||||
deleted_ids.append(resource_id)
|
||||
except Exception as e:
|
||||
failure = True
|
||||
failed_items[resource_id] = e
|
||||
if failure:
|
||||
msg = ''
|
||||
if deleted_ids:
|
||||
msg = (_('Successfully deleted %(resource)s(s):'
|
||||
' %(deleted_list)s') % {'deleted_list':
|
||||
', '.join(deleted_ids),
|
||||
'resource': _VNFFGD})
|
||||
err_msg = _("\n\nUnable to delete the below"
|
||||
" %s(s):") % _VNFFGD
|
||||
for failed_id, error in failed_items.iteritems():
|
||||
err_msg += (_('\n Cannot delete %(failed_id)s: %(error)s')
|
||||
% {'failed_id': failed_id,
|
||||
'error': error})
|
||||
msg += err_msg
|
||||
raise exceptions.CommandError(msg)
|
||||
else:
|
||||
print((_('All specified %(resource)s(s) deleted successfully')
|
||||
% {'resource': _VNFFGD}))
|
||||
return
|
||||
|
||||
|
||||
class ListVNFFGD(command.Lister):
|
||||
_description = ("List (VNFFGD)s that belong to a given tenant.")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListVNFFGD, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'--template-source',
|
||||
help=_("List VNFFGD with specified template source. Available \
|
||||
options are 'onboarded' (default), 'inline' or 'all'"),
|
||||
action='store',
|
||||
default='onboarded')
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
data = client.list_vnffgds()
|
||||
headers, columns = tacker_osc_utils.get_column_definitions(
|
||||
_attr_map, long_listing=None)
|
||||
return (headers,
|
||||
(utils.get_dict_properties(
|
||||
s, columns,
|
||||
) for s in data[_VNFFGD + 's']))
|
||||
|
||||
|
||||
class ShowVNFFGD(command.ShowOne):
|
||||
_description = _("Display VNFFGD details")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ShowVNFFGD, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
_VNFFGD,
|
||||
metavar="<VNFFGD>",
|
||||
help=_("VNFFGD to display (name or ID)")
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
obj_id = tackerV10.find_resourceid_by_name_or_id(
|
||||
client, _VNFFGD, parsed_args.vnffgd)
|
||||
obj = client.show_vnffgd(obj_id)
|
||||
display_columns, columns = _get_columns(obj[_VNFFGD])
|
||||
data = utils.get_item_properties(
|
||||
sdk_utils.DictModel(obj[_VNFFGD]),
|
||||
columns, formatters=_formatters)
|
||||
return (display_columns, data)
|
||||
|
||||
|
||||
class ShowTemplateVNFFGD(command.ShowOne):
|
||||
_description = _("Display VNFFGD Template details")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ShowTemplateVNFFGD, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
_VNFFGD,
|
||||
metavar="<VNFFGD>",
|
||||
help=_("VNFFGD to display (name or ID)")
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
obj_id = tackerV10.find_resourceid_by_name_or_id(
|
||||
client, _VNFFGD, parsed_args.vnffgd)
|
||||
obj = client.show_vnffgd(obj_id)
|
||||
data = utils.get_item_properties(
|
||||
sdk_utils.DictModel(obj[_VNFFGD]),
|
||||
(u'template',),
|
||||
formatters=_formatters)
|
||||
data = (data or _('Unable to display VNFFGD template!'))
|
||||
return ((u'template',), data)
|
||||
@@ -1,442 +0,0 @@
|
||||
# Copyright 2018 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 yaml
|
||||
|
||||
from osc_lib.command import command
|
||||
from osc_lib import utils
|
||||
|
||||
from tackerclient.common import exceptions
|
||||
from tackerclient.i18n import _
|
||||
from tackerclient.osc import sdk_utils
|
||||
from tackerclient.osc import utils as tacker_osc_utils
|
||||
from tackerclient.tacker import v1_0 as tackerV10
|
||||
|
||||
_attr_map = (
|
||||
('id', 'ID', tacker_osc_utils.LIST_BOTH),
|
||||
('name', 'Name', tacker_osc_utils.LIST_BOTH),
|
||||
('mgmt_ip_address', 'Mgmt Ip Address',
|
||||
tacker_osc_utils.LIST_BOTH),
|
||||
('status', 'Status', tacker_osc_utils.LIST_BOTH),
|
||||
('vim_id', 'VIM ID', tacker_osc_utils.LIST_BOTH),
|
||||
('vnfd_id', 'VNFD ID', tacker_osc_utils.LIST_BOTH),
|
||||
('tenant_id', 'Project ID', tacker_osc_utils.LIST_LONG_ONLY),
|
||||
)
|
||||
|
||||
_attr_map_rsc = (
|
||||
('id', 'ID', tacker_osc_utils.LIST_BOTH),
|
||||
('name', 'Name', tacker_osc_utils.LIST_BOTH),
|
||||
('type', 'Type', tacker_osc_utils.LIST_BOTH),
|
||||
)
|
||||
|
||||
_VNF = "vnf"
|
||||
|
||||
|
||||
def _get_columns(item):
|
||||
column_map = {
|
||||
'tenant_id': 'project_id',
|
||||
}
|
||||
return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map)
|
||||
|
||||
|
||||
def _break_string(vnf_monitoring_policy):
|
||||
count_space = 0
|
||||
monitoring_policy = "\n"
|
||||
for i in range(0, len(vnf_monitoring_policy)):
|
||||
monitoring_policy += vnf_monitoring_policy[i]
|
||||
if vnf_monitoring_policy[i] == ' ':
|
||||
count_space += 1
|
||||
if count_space == 9:
|
||||
monitoring_policy += "\n"
|
||||
count_space = 0
|
||||
return monitoring_policy
|
||||
|
||||
|
||||
class CreateVNF(command.ShowOne):
|
||||
_description = _("Create a new VNF")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CreateVNF, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'name', metavar='NAME',
|
||||
help=_('Set a name for the VNF'))
|
||||
parser.add_argument(
|
||||
'--tenant-id', metavar='TENANT_ID',
|
||||
help=_('The owner tenant ID or project ID'))
|
||||
vnfd_group = parser.add_mutually_exclusive_group(required=True)
|
||||
vnfd_group.add_argument(
|
||||
'--vnfd-id',
|
||||
help=_('VNFD ID to use as template to create VNF'))
|
||||
vnfd_group.add_argument(
|
||||
'--vnfd-name',
|
||||
help=_('VNFD Name to use as template to create VNF'))
|
||||
vnfd_group.add_argument(
|
||||
'--vnfd-template',
|
||||
help=_("VNFD file to create VNF"))
|
||||
vim_group = parser.add_mutually_exclusive_group()
|
||||
vim_group.add_argument(
|
||||
'--vim-id',
|
||||
help=_('VIM ID to deploy VNF on specified VIM'))
|
||||
vim_group.add_argument(
|
||||
'--vim-name',
|
||||
help=_('VIM name to deploy VNF on specified VIM'))
|
||||
parser.add_argument(
|
||||
'--vim-region-name',
|
||||
help=_('VIM Region to deploy VNF on specified VIM'))
|
||||
parser.add_argument(
|
||||
'--config-file',
|
||||
help=_('YAML file with VNF configuration'))
|
||||
parser.add_argument(
|
||||
'--param-file',
|
||||
help=_('Specify parameter yaml file'))
|
||||
parser.add_argument(
|
||||
'--description',
|
||||
help=_('Set description for the VNF'))
|
||||
return parser
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
body = {_VNF: {}}
|
||||
body[_VNF]['attributes'] = {}
|
||||
|
||||
config = None
|
||||
if parsed_args.config_file:
|
||||
with open(parsed_args.config_file) as f:
|
||||
config_yaml = f.read()
|
||||
try:
|
||||
config = yaml.load(
|
||||
config_yaml, Loader=yaml.SafeLoader)
|
||||
except yaml.YAMLError as e:
|
||||
raise exceptions.InvalidInput(e)
|
||||
if config:
|
||||
body[_VNF]['attributes'] = {'config': config}
|
||||
|
||||
if parsed_args.vim_region_name:
|
||||
body[_VNF].setdefault('placement_attr', {})['region_name'] = \
|
||||
parsed_args.vim_region_name
|
||||
|
||||
client = self.app.client_manager.tackerclient
|
||||
if parsed_args.vim_name:
|
||||
_id = tackerV10.find_resourceid_by_name_or_id(client, 'vim',
|
||||
parsed_args.vim_name)
|
||||
parsed_args.vim_id = _id
|
||||
if parsed_args.vnfd_name:
|
||||
_id = tackerV10.find_resourceid_by_name_or_id(
|
||||
client, 'vnfd',
|
||||
parsed_args.vnfd_name
|
||||
)
|
||||
parsed_args.vnfd_id = _id
|
||||
elif parsed_args.vnfd_template:
|
||||
with open(parsed_args.vnfd_template) as f:
|
||||
template = f.read()
|
||||
try:
|
||||
template = yaml.load(
|
||||
template, Loader=yaml.SafeLoader)
|
||||
except yaml.YAMLError as e:
|
||||
raise exceptions.InvalidInput(e)
|
||||
if not template:
|
||||
raise exceptions.InvalidInput('The vnfd file is empty')
|
||||
body[_VNF]['vnfd_template'] = template
|
||||
|
||||
if parsed_args.param_file:
|
||||
with open(parsed_args.param_file) as f:
|
||||
param_yaml = f.read()
|
||||
try:
|
||||
param_yaml = yaml.load(
|
||||
param_yaml, Loader=yaml.SafeLoader)
|
||||
except yaml.YAMLError as e:
|
||||
raise exceptions.InvalidInput(e)
|
||||
if not param_yaml:
|
||||
raise exceptions.InvalidInput('The parameter file is empty')
|
||||
body[_VNF]['attributes'] = {'param_values': param_yaml}
|
||||
tackerV10.update_dict(parsed_args, body[_VNF],
|
||||
['tenant_id', 'name', 'description',
|
||||
'vnfd_id', 'vim_id'])
|
||||
return body
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
vnf = client.create_vnf(self.args2body(parsed_args))
|
||||
display_columns, columns = _get_columns(vnf[_VNF])
|
||||
if vnf[_VNF]['attributes'].get('monitoring_policy'):
|
||||
vnf[_VNF]['attributes']['monitoring_policy'] =\
|
||||
_break_string(vnf[_VNF]['attributes']['monitoring_policy'])
|
||||
data = utils.get_item_properties(
|
||||
sdk_utils.DictModel(vnf[_VNF]),
|
||||
columns)
|
||||
return (display_columns, data)
|
||||
|
||||
|
||||
class DeleteVNF(command.Command):
|
||||
_description = _("Delete VNF(s).")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(DeleteVNF, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
_VNF,
|
||||
metavar="<VNF>",
|
||||
nargs="+",
|
||||
help=_("VNF(s) to delete (name or ID)"))
|
||||
parser.add_argument(
|
||||
'--force',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help=_('Force delete VNF instance'))
|
||||
return parser
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
body = dict()
|
||||
if parsed_args.force:
|
||||
body[_VNF] = dict()
|
||||
body[_VNF]['attributes'] = dict()
|
||||
body[_VNF]['attributes']['force'] = True
|
||||
return body
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
failure = False
|
||||
deleted_ids = []
|
||||
failed_items = {}
|
||||
body = self.args2body(parsed_args)
|
||||
for resource_id in parsed_args.vnf:
|
||||
try:
|
||||
obj = tackerV10.find_resourceid_by_name_or_id(
|
||||
client, _VNF, resource_id)
|
||||
client.delete_vnf(obj, body)
|
||||
deleted_ids.append(resource_id)
|
||||
except Exception as e:
|
||||
failure = True
|
||||
failed_items[resource_id] = e
|
||||
if failure:
|
||||
msg = ''
|
||||
if deleted_ids:
|
||||
msg = (_('Successfully deleted %(resource)s(s):'
|
||||
' %(deleted_list)s') % {'deleted_list':
|
||||
', '.join(deleted_ids),
|
||||
'resource': _VNF})
|
||||
err_msg = _("\n\nUnable to delete the below"
|
||||
" %s(s):") % _VNF
|
||||
for failed_id, error in failed_items.iteritems():
|
||||
err_msg += (_('\n Cannot delete %(failed_id)s: %(error)s')
|
||||
% {'failed_id': failed_id,
|
||||
'error': error})
|
||||
msg += err_msg
|
||||
raise exceptions.CommandError(msg)
|
||||
else:
|
||||
print((_('All specified %(resource)s(s) deleted successfully')
|
||||
% {'resource': _VNF}))
|
||||
return
|
||||
|
||||
|
||||
class ListVNF(command.Lister):
|
||||
_description = _("List VNF(s) that belong to a given tenant.")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListVNF, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'--template-source',
|
||||
help=_("List VNF with specified template source. Available \
|
||||
options are 'onboarded' (default), 'inline' or 'all'"),
|
||||
action='store',
|
||||
default='onboarded')
|
||||
vim_group = parser.add_mutually_exclusive_group()
|
||||
vim_group.add_argument(
|
||||
'--vim-id',
|
||||
help=_('List VNF(s) that belong to a given VIM ID'))
|
||||
vim_group.add_argument(
|
||||
'--vim-name',
|
||||
help=_('List VNF(s) that belong to a given VIM Name'))
|
||||
vnfd_group = parser.add_mutually_exclusive_group()
|
||||
vnfd_group.add_argument(
|
||||
'--vnfd-id',
|
||||
help=_('List VNF(s) that belong to a given VNFD ID'))
|
||||
vnfd_group.add_argument(
|
||||
'--vnfd-name',
|
||||
help=_('List VNF(s) that belong to a given VNFD Name'))
|
||||
parser.add_argument(
|
||||
'--tenant-id', metavar='TENANT_ID',
|
||||
help=_('The owner tenant ID or project ID'))
|
||||
parser.add_argument(
|
||||
'--long',
|
||||
action='store_true',
|
||||
help=_('List additional fields in output'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
_params = {}
|
||||
if parsed_args.vim_id:
|
||||
_params['vim_id'] = parsed_args.vim_id
|
||||
if parsed_args.vim_name:
|
||||
vim_id = tackerV10.find_resourceid_by_name_or_id(
|
||||
client, 'vim', parsed_args.vim_name
|
||||
)
|
||||
_params['vim_id'] = vim_id
|
||||
if parsed_args.vnfd_id:
|
||||
_params['vnfd_id'] = parsed_args.vnfd_id
|
||||
if parsed_args.vnfd_name:
|
||||
vim_id = tackerV10.find_resourceid_by_name_or_id(
|
||||
client, 'vnfd', parsed_args.vnfd_name
|
||||
)
|
||||
_params['vnfd_id'] = vim_id
|
||||
if parsed_args.tenant_id:
|
||||
_params['tenant_id'] = parsed_args.tenant_id
|
||||
data = client.list_vnfs(**_params)
|
||||
headers, columns = tacker_osc_utils.get_column_definitions(
|
||||
_attr_map, long_listing=parsed_args.long)
|
||||
return (headers,
|
||||
(utils.get_dict_properties(
|
||||
s, columns,
|
||||
) for s in data[_VNF + 's']))
|
||||
|
||||
|
||||
class ShowVNF(command.ShowOne):
|
||||
_description = _("Display VNF details")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ShowVNF, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
_VNF,
|
||||
metavar="<VNF>",
|
||||
help=_("VNF to display (name or ID)"))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
obj_id = tackerV10.find_resourceid_by_name_or_id(
|
||||
client, _VNF, parsed_args.vnf)
|
||||
obj = client.show_vnf(obj_id)
|
||||
if obj[_VNF]['attributes'].get('monitoring_policy'):
|
||||
obj[_VNF]['attributes']['monitoring_policy'] =\
|
||||
_break_string(obj[_VNF]['attributes']['monitoring_policy'])
|
||||
display_columns, columns = _get_columns(obj[_VNF])
|
||||
data = utils.get_item_properties(
|
||||
sdk_utils.DictModel(obj[_VNF]),
|
||||
columns)
|
||||
return (display_columns, data)
|
||||
|
||||
|
||||
class ListVNFResources(command.Lister):
|
||||
_description = _("List resources of a VNF like VDU, CP, etc.")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListVNFResources, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
_VNF,
|
||||
metavar="<VNF>",
|
||||
help=_("VNF to display (name or ID)"))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
obj_id = tackerV10.find_resourceid_by_name_or_id(
|
||||
client, _VNF, parsed_args.vnf)
|
||||
data = client.list_vnf_resources(obj_id)
|
||||
headers, columns = tacker_osc_utils.get_column_definitions(
|
||||
_attr_map_rsc, long_listing=None)
|
||||
return (headers,
|
||||
(utils.get_dict_properties(
|
||||
s, columns,
|
||||
) for s in data['resources']))
|
||||
|
||||
|
||||
class UpdateVNF(command.ShowOne):
|
||||
_description = _("Update a given VNF.")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(UpdateVNF, self).get_parser(prog_name)
|
||||
config_group = parser.add_mutually_exclusive_group(required=True)
|
||||
config_group.add_argument(
|
||||
'--config-file',
|
||||
help=_('YAML file with VNF configuration'))
|
||||
config_group.add_argument(
|
||||
'--config',
|
||||
help=_('Specify config YAML data'))
|
||||
parser.add_argument(
|
||||
_VNF,
|
||||
metavar="<VNF>",
|
||||
help=_("VNF to update (name or ID)"))
|
||||
return parser
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
body = {_VNF: {}}
|
||||
body[_VNF]['attributes'] = {}
|
||||
|
||||
config = None
|
||||
if parsed_args.config_file:
|
||||
with open(parsed_args.config_file) as f:
|
||||
config_yaml = f.read()
|
||||
try:
|
||||
config = yaml.load(config_yaml, Loader=yaml.SafeLoader)
|
||||
except yaml.YAMLError as e:
|
||||
raise exceptions.InvalidInput(e)
|
||||
if parsed_args.config:
|
||||
config = parsed_args.config
|
||||
if isinstance(config, str) or isinstance(config, unicode):
|
||||
config_str = parsed_args.config.decode('unicode_escape')
|
||||
try:
|
||||
config = yaml.load(config_str, Loader=yaml.SafeLoader)
|
||||
except yaml.YAMLError as e:
|
||||
raise exceptions.InvalidInput(e)
|
||||
if config:
|
||||
body[_VNF]['attributes'] = {'config': config}
|
||||
tackerV10.update_dict(parsed_args, body[_VNF], ['tenant_id'])
|
||||
return body
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
obj_id = tackerV10.find_resourceid_by_name_or_id(
|
||||
client, _VNF, parsed_args.vnf)
|
||||
vnf = client.update_vnf(obj_id, self.args2body(parsed_args))
|
||||
if vnf[_VNF]['attributes'].get('monitoring_policy'):
|
||||
vnf[_VNF]['attributes']['monitoring_policy'] =\
|
||||
_break_string(vnf[_VNF]['attributes']['monitoring_policy'])
|
||||
display_columns, columns = _get_columns(vnf[_VNF])
|
||||
data = utils.get_item_properties(
|
||||
sdk_utils.DictModel(vnf[_VNF]),
|
||||
columns)
|
||||
return (display_columns, data)
|
||||
|
||||
|
||||
class ScaleVNF(command.Command):
|
||||
_description = _("Scale a VNF.")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ScaleVNF, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'--scaling-policy-name',
|
||||
help=_('VNF policy name used to scale'))
|
||||
parser.add_argument(
|
||||
'--scaling-type',
|
||||
help=_('VNF scaling type, it could be either "out" or "in"'))
|
||||
parser.add_argument(
|
||||
_VNF,
|
||||
metavar="<VNF>",
|
||||
help=_("VNF to scale (name or ID)"))
|
||||
return parser
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
args = {}
|
||||
body = {"scale": args}
|
||||
args['type'] = parsed_args.scaling_type
|
||||
args['policy'] = parsed_args.scaling_policy_name
|
||||
return body
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
obj_id = tackerV10.find_resourceid_by_name_or_id(
|
||||
client, _VNF, parsed_args.vnf)
|
||||
client.scale_vnf(obj_id, self.args2body(parsed_args))
|
||||
return
|
||||
@@ -1,224 +0,0 @@
|
||||
# Copyright 2016 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 yaml
|
||||
|
||||
from osc_lib.command import command
|
||||
from osc_lib import utils
|
||||
|
||||
from tackerclient.common import exceptions
|
||||
from tackerclient.i18n import _
|
||||
from tackerclient.osc import sdk_utils
|
||||
from tackerclient.osc import utils as tacker_osc_utils
|
||||
from tackerclient.tacker import v1_0 as tackerV10
|
||||
|
||||
_attr_map = (
|
||||
('id', 'ID', tacker_osc_utils.LIST_BOTH),
|
||||
('name', 'Name', tacker_osc_utils.LIST_BOTH),
|
||||
('template_source', 'Template_Source',
|
||||
tacker_osc_utils.LIST_BOTH),
|
||||
('description', 'Description', tacker_osc_utils.LIST_BOTH),
|
||||
)
|
||||
|
||||
_VNFD = "vnfd"
|
||||
|
||||
_formatters = {
|
||||
'attributes': tacker_osc_utils.format_dict_with_indention,
|
||||
}
|
||||
|
||||
|
||||
def _get_columns(item):
|
||||
column_map = {
|
||||
'tenant_id': 'project_id',
|
||||
}
|
||||
return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map)
|
||||
|
||||
|
||||
class CreateVNFD(command.ShowOne):
|
||||
_description = _("Create a new VNFD")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CreateVNFD, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'name', metavar='NAME',
|
||||
help=_('Name for VNFD'))
|
||||
parser.add_argument(
|
||||
'--tenant-id', metavar='TENANT_ID',
|
||||
help=_('The owner tenant ID or project ID'))
|
||||
parser.add_argument(
|
||||
'--vnfd-file',
|
||||
required=True,
|
||||
help=_('YAML file with VNFD parameters'))
|
||||
parser.add_argument(
|
||||
'--description',
|
||||
help=_('Set a description for the VNFD'))
|
||||
return parser
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
body = {_VNFD: {}}
|
||||
vnfd = None
|
||||
if not parsed_args.vnfd_file:
|
||||
raise exceptions.InvalidInput("Invalid input for vnfd file")
|
||||
with open(parsed_args.vnfd_file) as f:
|
||||
vnfd = f.read()
|
||||
try:
|
||||
vnfd = yaml.load(vnfd, Loader=yaml.SafeLoader)
|
||||
except yaml.YAMLError as e:
|
||||
msg = _("yaml failed to load vnfd file. %s") % e
|
||||
raise exceptions.InvalidInput(msg)
|
||||
if not vnfd:
|
||||
raise exceptions.InvalidInput("vnfd file is empty")
|
||||
body[_VNFD]['attributes'] = {'vnfd': vnfd}
|
||||
tackerV10.update_dict(parsed_args, body[_VNFD],
|
||||
['tenant_id', 'name', 'description'])
|
||||
return body
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
vnfd = client.create_vnfd(self.args2body(parsed_args))
|
||||
display_columns, columns = _get_columns(vnfd[_VNFD])
|
||||
vnfd[_VNFD]['attributes']['vnfd'] = yaml.load(
|
||||
vnfd[_VNFD]['attributes']['vnfd'])
|
||||
data = utils.get_item_properties(
|
||||
sdk_utils.DictModel(vnfd[_VNFD]),
|
||||
columns, formatters=_formatters)
|
||||
return (display_columns, data)
|
||||
|
||||
|
||||
class DeleteVNFD(command.Command):
|
||||
_description = _("Delete VNFD(s).")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(DeleteVNFD, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
_VNFD,
|
||||
metavar="<VNFD>",
|
||||
nargs="+",
|
||||
help=_("VNFD(s) to delete (name or ID)")
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
failure = False
|
||||
deleted_ids = []
|
||||
failed_items = {}
|
||||
for resource_id in parsed_args.vnfd:
|
||||
try:
|
||||
obj = tackerV10.find_resourceid_by_name_or_id(
|
||||
client, _VNFD, resource_id)
|
||||
client.delete_vnfd(obj)
|
||||
deleted_ids.append(resource_id)
|
||||
except Exception as e:
|
||||
failure = True
|
||||
failed_items[resource_id] = e
|
||||
if failure:
|
||||
msg = ''
|
||||
if deleted_ids:
|
||||
msg = (_('Successfully deleted %(resource)s(s):'
|
||||
' %(deleted_list)s') % {'deleted_list':
|
||||
', '.join(deleted_ids),
|
||||
'resource': _VNFD})
|
||||
err_msg = _("\n\nUnable to delete the below"
|
||||
" %s(s):") % _VNFD
|
||||
for failed_id, error in failed_items.iteritems():
|
||||
err_msg += (_('\n Cannot delete %(failed_id)s: %(error)s')
|
||||
% {'failed_id': failed_id,
|
||||
'error': error})
|
||||
msg += err_msg
|
||||
raise exceptions.CommandError(msg)
|
||||
else:
|
||||
print((_('All specified %(resource)s(s) deleted successfully')
|
||||
% {'resource': _VNFD}))
|
||||
return
|
||||
|
||||
|
||||
class ListVNFD(command.Lister):
|
||||
_description = ("List (VNFD)s that belong to a given tenant.")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListVNFD, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'--template-source',
|
||||
help=_("List VNFD with specified template source. Available \
|
||||
options are 'onboarded' (default), 'inline' or 'all'"),
|
||||
action='store',
|
||||
default='onboarded')
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
data = client.list_vnfds()
|
||||
headers, columns = tacker_osc_utils.get_column_definitions(
|
||||
_attr_map, long_listing=None)
|
||||
return (headers,
|
||||
(utils.get_dict_properties(
|
||||
s, columns,
|
||||
) for s in data[_VNFD + 's']))
|
||||
|
||||
|
||||
class ShowVNFD(command.ShowOne):
|
||||
_description = _("Display VNFD details")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ShowVNFD, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
_VNFD,
|
||||
metavar="<VNFD>",
|
||||
help=_("VNFD to display (name or ID)")
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
obj_id = tackerV10.find_resourceid_by_name_or_id(
|
||||
client, _VNFD, parsed_args.vnfd)
|
||||
obj = client.show_vnfd(obj_id)
|
||||
obj[_VNFD]['attributes']['vnfd'] = yaml.load(
|
||||
obj[_VNFD]['attributes']['vnfd'])
|
||||
display_columns, columns = _get_columns(obj[_VNFD])
|
||||
data = utils.get_item_properties(
|
||||
sdk_utils.DictModel(obj[_VNFD]),
|
||||
columns,
|
||||
formatters=_formatters)
|
||||
return (display_columns, data)
|
||||
|
||||
|
||||
class ShowTemplateVNFD(command.ShowOne):
|
||||
_description = _("Display VNFD Template details")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ShowTemplateVNFD, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
_VNFD,
|
||||
metavar="<VNFD>",
|
||||
help=_("VNFD to display (name or ID)")
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
obj_id = tackerV10.find_resourceid_by_name_or_id(
|
||||
client, _VNFD, parsed_args.vnfd)
|
||||
obj = client.show_vnfd(obj_id)
|
||||
obj[_VNFD]['attributes']['vnfd'] = yaml.load(
|
||||
obj[_VNFD]['attributes']['vnfd'])
|
||||
data = utils.get_item_properties(
|
||||
sdk_utils.DictModel(obj[_VNFD]),
|
||||
(u'attributes',),
|
||||
formatters=_formatters)
|
||||
data = (data or _('Unable to display VNFD template!'))
|
||||
return ((u'attributes',), data)
|
||||
@@ -1,258 +0,0 @@
|
||||
# Copyright (C) 2019 NTT DATA
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import logging
|
||||
|
||||
from osc_lib.cli import parseractions
|
||||
from osc_lib.command import command
|
||||
from osc_lib import utils
|
||||
|
||||
from tackerclient.common import exceptions
|
||||
from tackerclient.i18n import _
|
||||
from tackerclient.osc import sdk_utils
|
||||
from tackerclient.osc import utils as tacker_osc_utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
_attr_map = (
|
||||
('id', 'ID', tacker_osc_utils.LIST_BOTH),
|
||||
('vnfProductName', 'VNF Product Name', tacker_osc_utils.LIST_BOTH),
|
||||
('onboardingState', 'Onboarding State', tacker_osc_utils.LIST_BOTH),
|
||||
('usageState', 'Usage State', tacker_osc_utils.LIST_BOTH),
|
||||
('operationalState', 'Operational State', tacker_osc_utils.LIST_BOTH),
|
||||
('userDefinedData', 'User Defined Data', tacker_osc_utils.LIST_BOTH)
|
||||
)
|
||||
|
||||
|
||||
_mixed_case_fields = ('usageState', 'onboardingState', 'operationalState',
|
||||
'vnfProductName', 'softwareImages', 'userDefinedData',
|
||||
'vnfdId', 'vnfdVersion', 'vnfSoftwareVersion',
|
||||
'vnfProvider', 'artifactPath', 'imagePath',
|
||||
'diskFormat', 'userMetadata')
|
||||
|
||||
|
||||
def _get_columns(vnf_package_obj):
|
||||
column_map = {
|
||||
'_links': 'Links',
|
||||
'onboardingState': 'Onboarding State',
|
||||
'operationalState': 'Operational State',
|
||||
'usageState': 'Usage State',
|
||||
'userDefinedData': 'User Defined Data',
|
||||
'id': 'ID'
|
||||
}
|
||||
|
||||
if vnf_package_obj['onboardingState'] == 'ONBOARDED':
|
||||
column_map.update({
|
||||
'softwareImages': 'Software Images',
|
||||
'vnfProvider': 'VNF Provider',
|
||||
'vnfSoftwareVersion': 'VNF Software Version',
|
||||
'vnfProductName': 'VNF Product Name',
|
||||
'vnfdId': 'VNFD ID',
|
||||
'vnfdVersion': 'VNFD Version'
|
||||
})
|
||||
|
||||
return sdk_utils.get_osc_show_columns_for_sdk_resource(vnf_package_obj,
|
||||
column_map)
|
||||
|
||||
|
||||
class CreateVnfPackage(command.ShowOne):
|
||||
_description = _("Create a new VNF Package")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
LOG.debug('get_parser(%s)', prog_name)
|
||||
parser = super(CreateVnfPackage, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'--user-data',
|
||||
metavar='<key=value>',
|
||||
action=parseractions.KeyValueAction,
|
||||
help=_('User defined data for the VNF package '
|
||||
'(repeat option to set multiple user defined data)'),
|
||||
)
|
||||
return parser
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
body = {}
|
||||
if parsed_args.user_data:
|
||||
body["userDefinedData"] = parsed_args.user_data
|
||||
return body
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
vnf_package = client.create_vnf_package(self.args2body(parsed_args))
|
||||
display_columns, columns = _get_columns(vnf_package)
|
||||
data = utils.get_item_properties(
|
||||
sdk_utils.DictModel(vnf_package),
|
||||
columns, mixed_case_fields=_mixed_case_fields)
|
||||
return (display_columns, data)
|
||||
|
||||
|
||||
class ListVnfPackage(command.Lister):
|
||||
_description = _("List VNF Package")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
LOG.debug('get_parser(%s)', prog_name)
|
||||
parser = super(ListVnfPackage, self).get_parser(prog_name)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
_params = {}
|
||||
client = self.app.client_manager.tackerclient
|
||||
data = client.list_vnf_packages(**_params)
|
||||
headers, columns = tacker_osc_utils.get_column_definitions(
|
||||
_attr_map, long_listing=True)
|
||||
return (headers,
|
||||
(utils.get_dict_properties(
|
||||
s, columns, mixed_case_fields=_mixed_case_fields,
|
||||
) for s in data['vnf_packages']))
|
||||
|
||||
|
||||
class ShowVnfPackage(command.ShowOne):
|
||||
_description = _("Show VNF Package Details")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
LOG.debug('get_parser(%s)', prog_name)
|
||||
parser = super(ShowVnfPackage, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'vnf_package',
|
||||
metavar="<vnf-package>",
|
||||
help=_("VNF package ID")
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
vnf_package = client.show_vnf_package(parsed_args.vnf_package)
|
||||
display_columns, columns = _get_columns(vnf_package)
|
||||
data = utils.get_item_properties(
|
||||
sdk_utils.DictModel(vnf_package),
|
||||
columns, mixed_case_fields=_mixed_case_fields)
|
||||
return (display_columns, data)
|
||||
|
||||
|
||||
class UploadVnfPackage(command.Command):
|
||||
_description = _("Upload VNF Package")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
LOG.debug('get_parser(%s)', prog_name)
|
||||
parser = super(UploadVnfPackage, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'vnf_package',
|
||||
metavar="<vnf-package>",
|
||||
help=_("VNF package ID")
|
||||
)
|
||||
file_source = parser.add_mutually_exclusive_group(required=True)
|
||||
file_source.add_argument(
|
||||
"--path",
|
||||
metavar="<file>",
|
||||
help=_("Upload VNF CSAR package from local file"),
|
||||
)
|
||||
file_source.add_argument(
|
||||
"--url",
|
||||
metavar="<Uri>",
|
||||
help=_("Uri of the VNF package content"),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--user-name",
|
||||
metavar="<user-name>",
|
||||
help=_("User name for authentication"),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--password",
|
||||
metavar="<password>",
|
||||
help=_("Password for authentication"),
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
attrs = {}
|
||||
if parsed_args.user_name:
|
||||
attrs['userName'] = parsed_args.user_name
|
||||
|
||||
if parsed_args.password:
|
||||
attrs['password'] = parsed_args.password
|
||||
|
||||
if parsed_args.url:
|
||||
attrs['url'] = parsed_args.url
|
||||
|
||||
file_data = None
|
||||
try:
|
||||
if parsed_args.path:
|
||||
file_data = open(parsed_args.path, 'rb')
|
||||
result = client.upload_vnf_package(parsed_args.vnf_package,
|
||||
file_data, **attrs)
|
||||
if not result:
|
||||
print((_('Upload request for VNF package %(id)s has been'
|
||||
' accepted.') % {'id': parsed_args.vnf_package}))
|
||||
finally:
|
||||
if file_data:
|
||||
file_data.close()
|
||||
|
||||
|
||||
class DeleteVnfPackage(command.Command):
|
||||
"""Vnf package delete
|
||||
|
||||
Delete class supports bulk deletion of vnf packages, and error
|
||||
handling.
|
||||
"""
|
||||
|
||||
_description = _("Delete VNF Package")
|
||||
|
||||
resource = 'vnf-package'
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
LOG.debug('get_parser(%s)', prog_name)
|
||||
parser = super(DeleteVnfPackage, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'vnf-package',
|
||||
metavar="<vnf-package>",
|
||||
nargs="+",
|
||||
help=_("Vnf package(s) ID to delete")
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.tackerclient
|
||||
failure = False
|
||||
deleted_ids = []
|
||||
failed_items = {}
|
||||
resources = getattr(parsed_args, self.resource, [])
|
||||
for resource_id in resources:
|
||||
try:
|
||||
vnf_package = client.show_vnf_package(resource_id)
|
||||
client.delete_vnf_package(vnf_package['id'])
|
||||
deleted_ids.append(resource_id)
|
||||
except Exception as e:
|
||||
failure = True
|
||||
failed_items[resource_id] = e
|
||||
if failure:
|
||||
msg = ''
|
||||
if deleted_ids:
|
||||
msg = (_('Successfully deleted %(resource)s(s):'
|
||||
' %(deleted_list)s') % {'deleted_list':
|
||||
', '.join(deleted_ids),
|
||||
'resource': self.resource})
|
||||
err_msg = _("\n\nUnable to delete the below"
|
||||
" 'vnf_package'(s):")
|
||||
for failed_id, error in failed_items.items():
|
||||
err_msg += (_('\n Cannot delete %(failed_id)s: %(error)s')
|
||||
% {'failed_id': failed_id,
|
||||
'error': error})
|
||||
msg += err_msg
|
||||
raise exceptions.CommandError(msg)
|
||||
else:
|
||||
print((_('All specified %(resource)s(s) deleted successfully')
|
||||
% {'resource': self.resource}))
|
||||
return
|
||||
@@ -31,7 +31,7 @@ import sys
|
||||
from keystoneclient.auth.identity import v2 as v2_auth
|
||||
from keystoneclient.auth.identity import v3 as v3_auth
|
||||
from keystoneclient import discover
|
||||
from keystoneclient import exceptions as ks_exc
|
||||
from keystoneclient.openstack.common.apiclient import exceptions as ks_exc
|
||||
from keystoneclient import session
|
||||
from oslo_utils import encodeutils
|
||||
import six.moves.urllib.parse as urlparse
|
||||
@@ -45,16 +45,11 @@ from tackerclient.common import exceptions as exc
|
||||
from tackerclient.common import extension as client_extension
|
||||
from tackerclient.common import utils
|
||||
from tackerclient.i18n import _
|
||||
from tackerclient.tacker.v1_0.events import events
|
||||
from tackerclient.tacker.v1_0 import extension
|
||||
from tackerclient.tacker.v1_0.nfvo import ns
|
||||
from tackerclient.tacker.v1_0.nfvo import nsd
|
||||
from tackerclient.tacker.v1_0.nfvo import vim
|
||||
from tackerclient.tacker.v1_0.nfvo import vnfcluster
|
||||
from tackerclient.tacker.v1_0.nfvo import vnffg
|
||||
from tackerclient.tacker.v1_0.nfvo import vnffgd
|
||||
from tackerclient.tacker.v1_0.vnfm import vnf
|
||||
from tackerclient.tacker.v1_0.vnfm import vnfd
|
||||
from tackerclient.tacker.v1_0.vm import device
|
||||
from tackerclient.tacker.v1_0.vm import device_template
|
||||
from tackerclient.tacker.v1_0.vm import vnf
|
||||
from tackerclient.tacker.v1_0.vm import vnfd
|
||||
from tackerclient.version import __version__
|
||||
|
||||
|
||||
@@ -108,89 +103,43 @@ COMMAND_V1 = {
|
||||
'bash-completion': BashCompletionCommand,
|
||||
'ext-list': extension.ListExt,
|
||||
'ext-show': extension.ShowExt,
|
||||
'device-template-create': device_template.CreateDeviceTemplate,
|
||||
'device-template-list': device_template.ListDeviceTemplate,
|
||||
'device-template-show': device_template.ShowDeviceTemplate,
|
||||
'device-template-update': device_template.UpdateDeviceTemplate,
|
||||
'device-template-delete': device_template.DeleteDeviceTemplate,
|
||||
'device-create': device.CreateDevice,
|
||||
'device-list': device.ListDevice,
|
||||
'device-show': device.ShowDevice,
|
||||
'device-update': device.UpdateDevice,
|
||||
'device-delete': device.DeleteDevice,
|
||||
'interface-attach': device.AttachInterface,
|
||||
'interface-detach': device.DetachInterface,
|
||||
|
||||
# MANO lingo
|
||||
'vnfd-create': vnfd.CreateVNFD,
|
||||
'vnfd-delete': vnfd.DeleteVNFD,
|
||||
'vnfd-list': vnfd.ListVNFD,
|
||||
'vnfd-show': vnfd.ShowVNFD,
|
||||
'vnfd-template-show': vnfd.ShowTemplateVNFD,
|
||||
|
||||
'vnf-create': vnf.CreateVNF,
|
||||
'vnf-update': vnf.UpdateVNF,
|
||||
'vnf-delete': vnf.DeleteVNF,
|
||||
'vnf-list': vnf.ListVNF,
|
||||
'vnf-show': vnf.ShowVNF,
|
||||
'vnf-scale': vnf.ScaleVNF,
|
||||
'vnf-resource-list': vnf.ListVNFResources,
|
||||
# 'vnf-config-create'
|
||||
# 'vnf-config-push'
|
||||
|
||||
'vim-register': vim.CreateVIM,
|
||||
'vim-update': vim.UpdateVIM,
|
||||
'vim-delete': vim.DeleteVIM,
|
||||
'vim-list': vim.ListVIM,
|
||||
'vim-show': vim.ShowVIM,
|
||||
|
||||
'events-list': events.ListResourceEvents,
|
||||
'event-show': events.ShowEvent,
|
||||
'vnf-events-list': events.ListVNFEvents,
|
||||
'vim-events-list': events.ListVIMEvents,
|
||||
'vnfd-events-list': events.ListVNFDEvents,
|
||||
|
||||
'vnffgd-create': vnffgd.CreateVNFFGD,
|
||||
'vnffgd-delete': vnffgd.DeleteVNFFGD,
|
||||
'vnffgd-list': vnffgd.ListVNFFGD,
|
||||
'vnffgd-show': vnffgd.ShowVNFFGD,
|
||||
'vnffgd-template-show': vnffgd.ShowTemplateVNFFGD,
|
||||
|
||||
'vnffg-create': vnffg.CreateVNFFG,
|
||||
'vnffg-delete': vnffg.DeleteVNFFG,
|
||||
'vnffg-list': vnffg.ListVNFFG,
|
||||
'vnffg-show': vnffg.ShowVNFFG,
|
||||
'vnffg-update': vnffg.UpdateVNFFG,
|
||||
|
||||
'nfp-list': vnffg.ListNFP,
|
||||
'nfp-show': vnffg.ShowNFP,
|
||||
|
||||
'chain-list': vnffg.ListSFC,
|
||||
'chain-show': vnffg.ShowSFC,
|
||||
|
||||
'classifier-list': vnffg.ListFC,
|
||||
'classifier-show': vnffg.ShowFC,
|
||||
|
||||
'nsd-create': nsd.CreateNSD,
|
||||
'nsd-list': nsd.ListNSD,
|
||||
'nsd-delete': nsd.DeleteNSD,
|
||||
'nsd-show': nsd.ShowNSD,
|
||||
'nsd-template-show': nsd.ShowTemplateNSD,
|
||||
|
||||
'ns-create': ns.CreateNS,
|
||||
'ns-list': ns.ListNS,
|
||||
'ns-delete': ns.DeleteNS,
|
||||
'ns-show': ns.ShowNS,
|
||||
|
||||
'cluster-create': vnfcluster.CreateCluster,
|
||||
'cluster-delete': vnfcluster.DeleteCluster,
|
||||
'cluster-list': vnfcluster.ListCluster,
|
||||
'cluster-show': vnfcluster.ShowCluster,
|
||||
|
||||
'cluster-member-add': vnfcluster.AddClusterMember,
|
||||
'cluster-member-show': vnfcluster.ShowClusterMember,
|
||||
'cluster-member-list': vnfcluster.ListClusterMember,
|
||||
'cluster-member-delete': vnfcluster.DeleteClusterMember,
|
||||
}
|
||||
|
||||
COMMANDS = {'1.0': COMMAND_V1}
|
||||
|
||||
|
||||
class HelpAction(argparse.Action):
|
||||
"""Provides a custom action for the -h and --help options.
|
||||
"""Provide a custom action so the -h and --help options
|
||||
to the main app will print a list of the commands.
|
||||
|
||||
The commands are determined by checking the CommandManager
|
||||
instance, passed in as the "default" value for the action.
|
||||
|
||||
:returns: a list of the commands
|
||||
"""
|
||||
def __call__(self, parser, namespace, values, option_string=None):
|
||||
outputs = []
|
||||
@@ -300,10 +249,8 @@ class TackerShell(app.App):
|
||||
|
||||
parser.add_argument(
|
||||
'--os-service-type', metavar='<os-service-type>',
|
||||
default=env('OS_TACKER_SERVICE_TYPE',
|
||||
default='nfv-orchestration'),
|
||||
help=_('Defaults to env[OS_TACKER_SERVICE_TYPE] or \
|
||||
nfv-orchestration.'))
|
||||
default=env('OS_SERVICEVM_SERVICE_TYPE', default='servicevm'),
|
||||
help=_('Defaults to env[OS_SERVICEVM_SERVICE_TYPE] or servicevm.'))
|
||||
|
||||
parser.add_argument(
|
||||
'--os-endpoint-type', metavar='<os-endpoint-type>',
|
||||
@@ -314,8 +261,7 @@ class TackerShell(app.App):
|
||||
# backward compatibility.
|
||||
parser.add_argument(
|
||||
'--service-type', metavar='<service-type>',
|
||||
default=env('OS_TACKER_SERVICE_TYPE',
|
||||
default='nfv-orchestration'),
|
||||
default=env('OS_SERVICEVM_SERVICE_TYPE', default='servicevm'),
|
||||
help=_('DEPRECATED! Use --os-service-type.'))
|
||||
|
||||
# FIXME(bklei): --endpoint-type is deprecated but kept in for
|
||||
@@ -352,10 +298,10 @@ class TackerShell(app.App):
|
||||
'--os-project-name',
|
||||
metavar='<auth-project-name>',
|
||||
default=utils.env('OS_PROJECT_NAME'),
|
||||
help=_('Another way to specify tenant name. '
|
||||
'This option is mutually exclusive with '
|
||||
' --os-tenant-name. '
|
||||
'Defaults to env[OS_PROJECT_NAME].'))
|
||||
help='Another way to specify tenant name. '
|
||||
'This option is mutually exclusive with '
|
||||
' --os-tenant-name. '
|
||||
'Defaults to env[OS_PROJECT_NAME].')
|
||||
|
||||
parser.add_argument(
|
||||
'--os_tenant_name',
|
||||
@@ -371,10 +317,10 @@ class TackerShell(app.App):
|
||||
'--os-project-id',
|
||||
metavar='<auth-project-id>',
|
||||
default=utils.env('OS_PROJECT_ID'),
|
||||
help=_('Another way to specify tenant ID. '
|
||||
'This option is mutually exclusive with '
|
||||
' --os-tenant-id. '
|
||||
'Defaults to env[OS_PROJECT_ID].'))
|
||||
help='Another way to specify tenant ID. '
|
||||
'This option is mutually exclusive with '
|
||||
' --os-tenant-id. '
|
||||
'Defaults to env[OS_PROJECT_ID].')
|
||||
|
||||
parser.add_argument(
|
||||
'--os-username', metavar='<auth-username>',
|
||||
@@ -397,8 +343,8 @@ class TackerShell(app.App):
|
||||
'--os-user-domain-id',
|
||||
metavar='<auth-user-domain-id>',
|
||||
default=utils.env('OS_USER_DOMAIN_ID'),
|
||||
help=_('OpenStack user domain ID. '
|
||||
'Defaults to env[OS_USER_DOMAIN_ID].'))
|
||||
help='OpenStack user domain ID. '
|
||||
'Defaults to env[OS_USER_DOMAIN_ID].')
|
||||
|
||||
parser.add_argument(
|
||||
'--os_user_domain_id',
|
||||
@@ -408,8 +354,8 @@ class TackerShell(app.App):
|
||||
'--os-user-domain-name',
|
||||
metavar='<auth-user-domain-name>',
|
||||
default=utils.env('OS_USER_DOMAIN_NAME'),
|
||||
help=_('OpenStack user domain name. '
|
||||
'Defaults to env[OS_USER_DOMAIN_NAME].'))
|
||||
help='OpenStack user domain name. '
|
||||
'Defaults to env[OS_USER_DOMAIN_NAME].')
|
||||
|
||||
parser.add_argument(
|
||||
'--os_user_domain_name',
|
||||
@@ -427,13 +373,13 @@ class TackerShell(app.App):
|
||||
'--os-project-domain-id',
|
||||
metavar='<auth-project-domain-id>',
|
||||
default=utils.env('OS_PROJECT_DOMAIN_ID'),
|
||||
help=_('Defaults to env[OS_PROJECT_DOMAIN_ID].'))
|
||||
help='Defaults to env[OS_PROJECT_DOMAIN_ID].')
|
||||
|
||||
parser.add_argument(
|
||||
'--os-project-domain-name',
|
||||
metavar='<auth-project-domain-name>',
|
||||
default=utils.env('OS_PROJECT_DOMAIN_NAME'),
|
||||
help=_('Defaults to env[OS_PROJECT_DOMAIN_NAME].'))
|
||||
help='Defaults to env[OS_PROJECT_DOMAIN_NAME].')
|
||||
|
||||
parser.add_argument(
|
||||
'--os-cert',
|
||||
@@ -610,9 +556,7 @@ class TackerShell(app.App):
|
||||
return 1
|
||||
|
||||
def authenticate_user(self):
|
||||
"""Authentication validation.
|
||||
|
||||
Make sure the user has provided all of the authentication
|
||||
"""Make sure the user has provided all of the authentication
|
||||
info we need.
|
||||
"""
|
||||
if self.options.os_auth_strategy == 'keystone':
|
||||
@@ -639,8 +583,8 @@ class TackerShell(app.App):
|
||||
self.options.os_project_domain_id)) or
|
||||
self.options.os_project_id)
|
||||
|
||||
if (not self.options.os_username and
|
||||
not self.options.os_user_id):
|
||||
if (not self.options.os_username
|
||||
and not self.options.os_user_id):
|
||||
raise exc.CommandError(
|
||||
_("You must provide a username or user ID via"
|
||||
" --os-username, env[OS_USERNAME] or"
|
||||
@@ -726,8 +670,7 @@ class TackerShell(app.App):
|
||||
|
||||
super(TackerShell, self).initialize_app(argv)
|
||||
|
||||
self.api_version = {'nfv-orchestration':
|
||||
self.api_version}
|
||||
self.api_version = {'servicevm': self.api_version}
|
||||
|
||||
# If the user is not asking for help, make sure they
|
||||
# have given us auth.
|
||||
|
||||
@@ -14,20 +14,20 @@
|
||||
# under the License.
|
||||
#
|
||||
|
||||
from tackerclient.common._i18n import _
|
||||
from tackerclient.common import exceptions
|
||||
from tackerclient.common import utils
|
||||
from tackerclient.openstack.common.gettextutils import _
|
||||
|
||||
|
||||
API_NAME = 'nfv-orchestration'
|
||||
API_NAME = 'servicevm'
|
||||
API_VERSIONS = {
|
||||
'1.0': 'tackerclient.v1_0.client.Client',
|
||||
}
|
||||
|
||||
|
||||
def make_client(instance):
|
||||
"""Returns an tacker client."""
|
||||
|
||||
"""Returns an tacker client.
|
||||
"""
|
||||
tacker_client = utils.get_client_class(
|
||||
API_NAME,
|
||||
instance._api_version[API_NAME],
|
||||
@@ -61,8 +61,7 @@ def make_client(instance):
|
||||
|
||||
def Client(api_version, *args, **kwargs):
|
||||
"""Return an tacker client.
|
||||
|
||||
:param api_version: only 1.0 is supported now
|
||||
@param api_version: only 1.0 is supported now
|
||||
"""
|
||||
tacker_client = utils.get_client_class(
|
||||
API_NAME,
|
||||
|
||||
@@ -27,10 +27,10 @@ from cliff import show
|
||||
from oslo_serialization import jsonutils
|
||||
import six
|
||||
|
||||
from tackerclient.common._i18n import _
|
||||
from tackerclient.common import command
|
||||
from tackerclient.common import exceptions
|
||||
from tackerclient.common import utils
|
||||
from tackerclient.openstack.common.gettextutils import _
|
||||
|
||||
HEX_ELEM = '[0-9A-Fa-f]'
|
||||
UUID_PATTERN = '-'.join([HEX_ELEM + '{8}', HEX_ELEM + '{4}',
|
||||
@@ -49,10 +49,8 @@ def _get_resource_plural(resource, client):
|
||||
def find_resourceid_by_id(client, resource, resource_id):
|
||||
resource_plural = _get_resource_plural(resource, client)
|
||||
obj_lister = getattr(client, "list_%s" % resource_plural)
|
||||
if resource == 'event':
|
||||
match = resource_id.isdigit() and resource_id != 0
|
||||
else:
|
||||
match = re.match(UUID_PATTERN, resource_id)
|
||||
# perform search by id only if we are passing a valid UUID
|
||||
match = re.match(UUID_PATTERN, resource_id)
|
||||
collection = resource_plural
|
||||
if match:
|
||||
data = obj_lister(id=resource_id, fields='id')
|
||||
@@ -98,7 +96,7 @@ def add_show_list_common_argument(parser):
|
||||
'-D', '--show-details',
|
||||
help=_('Show detailed info'),
|
||||
action='store_true',
|
||||
default=False,)
|
||||
default=False, )
|
||||
parser.add_argument(
|
||||
'--show_details',
|
||||
action='store_true',
|
||||
@@ -265,7 +263,7 @@ def parse_args_to_dict(values_specs):
|
||||
# All others are value items
|
||||
# Make sure '--' occurs first and allow minus value
|
||||
if (not current_item or '=' in current_item or
|
||||
_item.startswith('-') and not is_number(_item)):
|
||||
_item.startswith('-') and not is_number(_item)):
|
||||
raise exceptions.CommandError(
|
||||
_("Invalid values_specs %s") % ' '.join(values_specs))
|
||||
_value_number += 1
|
||||
@@ -279,12 +277,12 @@ def parse_args_to_dict(values_specs):
|
||||
|
||||
# populate the parser with arguments
|
||||
_parser = argparse.ArgumentParser(add_help=False)
|
||||
for opt, optspec in _options.items():
|
||||
for opt, optspec in _options.iteritems():
|
||||
_parser.add_argument(opt, **optspec)
|
||||
_args = _parser.parse_args(_values_specs)
|
||||
|
||||
result_dict = {}
|
||||
for opt in _options.keys():
|
||||
for opt in _options.iterkeys():
|
||||
_opt = opt.split('--', 2)[1]
|
||||
_opt = _opt.replace('-', '_')
|
||||
_value = getattr(_args, _opt)
|
||||
@@ -303,14 +301,14 @@ def _merge_args(qCmd, parsed_args, _extra_values, value_specs):
|
||||
@param values_specs: the unparsed unknown parts
|
||||
"""
|
||||
temp_values = _extra_values.copy()
|
||||
for key, value in temp_values.items():
|
||||
for key, value in temp_values.iteritems():
|
||||
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]))):
|
||||
type(arg_value[0]) == type(value[0])):
|
||||
arg_value.extend(value)
|
||||
_extra_values.pop(key)
|
||||
|
||||
@@ -355,7 +353,7 @@ class TackerCommandMeta(abc.ABCMeta):
|
||||
@six.add_metaclass(TackerCommandMeta)
|
||||
class TackerCommand(command.OpenStackCommand):
|
||||
|
||||
api = 'nfv-orchestration'
|
||||
api = 'servicevm'
|
||||
values_specs = []
|
||||
json_indent = None
|
||||
|
||||
@@ -364,8 +362,8 @@ class TackerCommand(command.OpenStackCommand):
|
||||
# NOTE(markmcclain): This is no longer supported in cliff version 1.5.2
|
||||
# see https://bugs.launchpad.net/python-tackerclient/+bug/1265926
|
||||
|
||||
# if hasattr(self, 'formatters'):
|
||||
# self.formatters['table'] = TableFormater()
|
||||
#if hasattr(self, 'formatters'):
|
||||
#self.formatters['table'] = TableFormater()
|
||||
|
||||
def get_client(self):
|
||||
return self.app.client_manager.tacker
|
||||
@@ -387,7 +385,7 @@ class TackerCommand(command.OpenStackCommand):
|
||||
def format_output_data(self, data):
|
||||
# Modify data to make it more readable
|
||||
if self.resource in data:
|
||||
for k, v in data[self.resource].items():
|
||||
for k, v in data[self.resource].iteritems():
|
||||
if isinstance(v, list):
|
||||
value = '\n'.join(jsonutils.dumps(
|
||||
i, indent=self.json_indent) if isinstance(i, dict)
|
||||
@@ -411,7 +409,7 @@ class CreateCommand(TackerCommand, show.ShowOne):
|
||||
|
||||
"""
|
||||
|
||||
api = 'nfv-orchestration'
|
||||
api = 'servicevm'
|
||||
resource = None
|
||||
log = None
|
||||
remove_output_fields = []
|
||||
@@ -428,7 +426,7 @@ class CreateCommand(TackerCommand, show.ShowOne):
|
||||
return parser
|
||||
|
||||
def get_data(self, parsed_args):
|
||||
self.log.debug('get_data(%s)', parsed_args)
|
||||
self.log.debug('get_data(%s)' % parsed_args)
|
||||
tacker_client = self.get_client()
|
||||
tacker_client.format = parsed_args.request_format
|
||||
_extra_values = parse_args_to_dict(self.values_specs)
|
||||
@@ -450,13 +448,14 @@ class CreateCommand(TackerCommand, show.ShowOne):
|
||||
info.pop(f)
|
||||
else:
|
||||
info = {'': ''}
|
||||
return zip(*sorted(info.items()))
|
||||
return zip(*sorted(info.iteritems()))
|
||||
|
||||
|
||||
class UpdateCommand(TackerCommand):
|
||||
"""Update resource's information."""
|
||||
"""Update resource's information
|
||||
"""
|
||||
|
||||
api = 'nfv-orchestration'
|
||||
api = 'servicevm'
|
||||
resource = None
|
||||
log = None
|
||||
allow_names = True
|
||||
@@ -500,75 +499,42 @@ class UpdateCommand(TackerCommand):
|
||||
|
||||
|
||||
class DeleteCommand(TackerCommand):
|
||||
"""Delete given resource(s)
|
||||
"""Delete a given resource
|
||||
|
||||
"""
|
||||
|
||||
api = 'nfv-orchestration'
|
||||
api = 'servicevm'
|
||||
resource = None
|
||||
log = None
|
||||
allow_names = True
|
||||
deleted_msg = {}
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(DeleteCommand, self).get_parser(prog_name)
|
||||
if self.allow_names:
|
||||
help_str = _('IDs or names of %s to delete')
|
||||
help_str = _('ID or name of %s to delete')
|
||||
else:
|
||||
help_str = _('IDs of %s to delete')
|
||||
help_str = _('ID of %s to delete')
|
||||
parser.add_argument(
|
||||
'ids', nargs='+',
|
||||
metavar=self.resource.upper(),
|
||||
'id', metavar=self.resource.upper(),
|
||||
help=help_str % self.resource)
|
||||
self.add_known_arguments(parser)
|
||||
return parser
|
||||
|
||||
def run(self, parsed_args):
|
||||
failure = False
|
||||
deleted_ids = []
|
||||
failed_items = {}
|
||||
self.log.debug('run(%s)', parsed_args)
|
||||
tacker_client = self.get_client()
|
||||
tacker_client.format = parsed_args.request_format
|
||||
obj_deleter = getattr(tacker_client,
|
||||
"delete_%s" % self.resource)
|
||||
body = self.args2body(parsed_args)
|
||||
|
||||
for resource_id in parsed_args.ids:
|
||||
try:
|
||||
if self.allow_names:
|
||||
_id = find_resourceid_by_name_or_id(
|
||||
tacker_client, self.resource, resource_id)
|
||||
else:
|
||||
_id = resource_id
|
||||
if body:
|
||||
obj_deleter(_id, body)
|
||||
else:
|
||||
obj_deleter(_id)
|
||||
deleted_ids.append(resource_id)
|
||||
except Exception as e:
|
||||
failure = True
|
||||
failed_items[resource_id] = e
|
||||
if failure:
|
||||
msg = ''
|
||||
if deleted_ids:
|
||||
status_msg = self.deleted_msg.get(self.resource, 'deleted')
|
||||
msg = (_('Successfully %(status_msg)s %(resource)s(s):'
|
||||
' %(deleted_list)s') % {'status_msg': status_msg,
|
||||
'deleted_list':
|
||||
', '.join(deleted_ids),
|
||||
'resource': self.resource})
|
||||
err_msg = _("\n\nUnable to delete the below"
|
||||
" %s(s):") % self.resource
|
||||
for failed_id, error in failed_items.iteritems():
|
||||
err_msg += (_('\n Cannot delete %(failed_id)s: %(error)s')
|
||||
% {'failed_id': failed_id,
|
||||
'error': error})
|
||||
msg += err_msg
|
||||
raise exceptions.CommandError(msg)
|
||||
if self.allow_names:
|
||||
_id = find_resourceid_by_name_or_id(tacker_client, self.resource,
|
||||
parsed_args.id)
|
||||
else:
|
||||
print((_('All specified %(resource)s(s) %(msg)s successfully')
|
||||
% {'msg': self.deleted_msg.get(self.resource, 'deleted'),
|
||||
'resource': self.resource}))
|
||||
_id = parsed_args.id
|
||||
obj_deleter(_id)
|
||||
print((_('Deleted %(resource)s: %(id)s')
|
||||
% {'id': parsed_args.id,
|
||||
'resource': self.resource}),
|
||||
file=self.app.stdout)
|
||||
return
|
||||
|
||||
|
||||
@@ -577,7 +543,7 @@ class ListCommand(TackerCommand, lister.Lister):
|
||||
|
||||
"""
|
||||
|
||||
api = 'nfv-orchestration'
|
||||
api = 'servicevm'
|
||||
resource = None
|
||||
log = None
|
||||
_formatters = {}
|
||||
@@ -676,15 +642,11 @@ class ShowCommand(TackerCommand, show.ShowOne):
|
||||
|
||||
"""
|
||||
|
||||
api = 'nfv-orchestration'
|
||||
api = 'servicevm'
|
||||
resource = None
|
||||
log = None
|
||||
allow_names = True
|
||||
|
||||
def get_id(self):
|
||||
if self.resource:
|
||||
return self.resource.upper()
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ShowCommand, self).get_parser(prog_name)
|
||||
add_show_list_common_argument(parser)
|
||||
@@ -693,7 +655,7 @@ class ShowCommand(TackerCommand, show.ShowOne):
|
||||
else:
|
||||
help_str = _('ID of %s to look up')
|
||||
parser.add_argument(
|
||||
'id', metavar=self.get_id(),
|
||||
'id', metavar=self.resource.upper(),
|
||||
help=help_str % self.resource)
|
||||
return parser
|
||||
|
||||
@@ -718,6 +680,6 @@ class ShowCommand(TackerCommand, show.ShowOne):
|
||||
self.format_output_data(data)
|
||||
resource = data[self.resource]
|
||||
if self.resource in data:
|
||||
return zip(*sorted(resource.items()))
|
||||
return zip(*sorted(resource.iteritems()))
|
||||
else:
|
||||
return None
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
# Copyright 2016 Brocade Communications 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 tackerclient.tacker import v1_0 as tackerV10
|
||||
|
||||
_EVENT = "event"
|
||||
|
||||
|
||||
class ListEventsBase(tackerV10.ListCommand):
|
||||
"""Base class for list command."""
|
||||
|
||||
list_columns = ['id', 'resource_type', 'resource_id',
|
||||
'resource_state', 'event_type',
|
||||
'timestamp', 'event_details']
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListEventsBase, self).get_parser(prog_name)
|
||||
parser.add_argument('--id',
|
||||
help='id of the event to look up.')
|
||||
parser.add_argument('--resource-id',
|
||||
help='resource id of the events to look up.')
|
||||
parser.add_argument('--resource-state',
|
||||
help='resource state of the events to look up.')
|
||||
parser.add_argument('--event-type',
|
||||
help='event type of the events to look up.')
|
||||
return parser
|
||||
|
||||
def args2search_opts(self, parsed_args):
|
||||
search_opts = super(ListEventsBase, self).args2search_opts(
|
||||
parsed_args)
|
||||
if parsed_args.id:
|
||||
search_opts.update({'id': parsed_args.id})
|
||||
if parsed_args.resource_id:
|
||||
search_opts.update({'resource_id': parsed_args.resource_id})
|
||||
if parsed_args.resource_state:
|
||||
search_opts.update({'resource_state': parsed_args.resource_state})
|
||||
if parsed_args.event_type:
|
||||
search_opts.update({'event_type': parsed_args.event_type})
|
||||
return search_opts
|
||||
|
||||
|
||||
class ListResourceEvents(ListEventsBase):
|
||||
"""List events of resources."""
|
||||
|
||||
resource = _EVENT
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListResourceEvents, self).get_parser(prog_name)
|
||||
parser.add_argument('--resource-type',
|
||||
help='resource type of the events to look up.')
|
||||
return parser
|
||||
|
||||
def args2search_opts(self, parsed_args):
|
||||
search_opts = super(ListResourceEvents, self).args2search_opts(
|
||||
parsed_args)
|
||||
if parsed_args.resource_type:
|
||||
search_opts.update({'resource_type': parsed_args.resource_type})
|
||||
return search_opts
|
||||
|
||||
|
||||
class ListVNFEvents(ListEventsBase):
|
||||
"""List events of VNFs."""
|
||||
|
||||
resource = "vnf_event"
|
||||
|
||||
|
||||
class ListVNFDEvents(ListEventsBase):
|
||||
"""List events of VNFDs."""
|
||||
|
||||
resource = "vnfd_event"
|
||||
|
||||
|
||||
class ListVIMEvents(ListEventsBase):
|
||||
"""List events of VIMs."""
|
||||
|
||||
resource = "vim_event"
|
||||
|
||||
|
||||
class ShowEvent(tackerV10.ShowCommand):
|
||||
"""Show event given the event id."""
|
||||
|
||||
resource = _EVENT
|
||||
@@ -14,6 +14,7 @@
|
||||
# under the License.
|
||||
#
|
||||
|
||||
from tackerclient.openstack.common.gettextutils import _
|
||||
from tackerclient.tacker import v1_0 as cmd_base
|
||||
|
||||
|
||||
@@ -30,5 +31,10 @@ class ShowExt(cmd_base.ShowCommand):
|
||||
resource = "extension"
|
||||
allow_names = False
|
||||
|
||||
def get_id(self):
|
||||
return 'EXT-ALIAS'
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(cmd_base.ShowCommand, self).get_parser(prog_name)
|
||||
cmd_base.add_show_list_common_argument(parser)
|
||||
parser.add_argument(
|
||||
'id', metavar='EXT-ALIAS',
|
||||
help=_('The extension alias'))
|
||||
return parser
|
||||
|
||||
@@ -1,139 +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 yaml
|
||||
|
||||
from tackerclient.common import exceptions
|
||||
from tackerclient.i18n import _
|
||||
from tackerclient.tacker import v1_0 as tackerV10
|
||||
|
||||
|
||||
_NS = 'ns'
|
||||
_RESOURCE = 'resource'
|
||||
|
||||
|
||||
class ListNS(tackerV10.ListCommand):
|
||||
"""List NS that belong to a given tenant."""
|
||||
|
||||
resource = _NS
|
||||
list_columns = ['id', 'name', 'nsd_id', 'vnf_ids', 'vnffg_ids',
|
||||
'mgmt_ip_addresses', 'status']
|
||||
|
||||
|
||||
class ShowNS(tackerV10.ShowCommand):
|
||||
"""Show information of a given NS."""
|
||||
|
||||
resource = _NS
|
||||
|
||||
|
||||
class CreateNS(tackerV10.CreateCommand):
|
||||
"""Create a NS."""
|
||||
|
||||
resource = _NS
|
||||
remove_output_fields = ["attributes"]
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'name', metavar='NAME',
|
||||
help=_('Set a name for the NS'))
|
||||
parser.add_argument(
|
||||
'--description',
|
||||
help=_('Set description for the NS'))
|
||||
nsd_group = parser.add_mutually_exclusive_group(required=True)
|
||||
nsd_group.add_argument(
|
||||
'--nsd-id',
|
||||
help=_('NSD ID to use as template to create NS'))
|
||||
nsd_group.add_argument(
|
||||
'--nsd-template',
|
||||
help=_('NSD file to create NS'))
|
||||
nsd_group.add_argument(
|
||||
'--nsd-name',
|
||||
help=_('NSD name to use as template to create NS'))
|
||||
vim_group = parser.add_mutually_exclusive_group()
|
||||
vim_group.add_argument(
|
||||
'--vim-id',
|
||||
help=_('VIM ID to use to create NS on the specified VIM'))
|
||||
vim_group.add_argument(
|
||||
'--vim-name',
|
||||
help=_('VIM name to use to create NS on the specified VIM'))
|
||||
parser.add_argument(
|
||||
'--vim-region-name',
|
||||
help=_('VIM Region to use to create NS on the specified VIM'))
|
||||
parser.add_argument(
|
||||
'--param-file',
|
||||
help=_('Specify parameter yaml file'))
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
args = {'attributes': {}}
|
||||
body = {self.resource: args}
|
||||
if parsed_args.vim_region_name:
|
||||
args.setdefault('placement_attr', {})['region_name'] = \
|
||||
parsed_args.vim_region_name
|
||||
|
||||
tacker_client = self.get_client()
|
||||
tacker_client.format = parsed_args.request_format
|
||||
if parsed_args.vim_name:
|
||||
_id = tackerV10.find_resourceid_by_name_or_id(tacker_client,
|
||||
'vim',
|
||||
parsed_args.
|
||||
vim_name)
|
||||
parsed_args.vim_id = _id
|
||||
if parsed_args.nsd_name:
|
||||
_id = tackerV10.find_resourceid_by_name_or_id(tacker_client,
|
||||
'nsd',
|
||||
parsed_args.
|
||||
nsd_name)
|
||||
parsed_args.nsd_id = _id
|
||||
elif parsed_args.nsd_template:
|
||||
with open(parsed_args.nsd_template) as f:
|
||||
template = f.read()
|
||||
try:
|
||||
args['nsd_template'] = yaml.load(
|
||||
template, Loader=yaml.SafeLoader)
|
||||
except yaml.YAMLError as e:
|
||||
raise exceptions.InvalidInput(e)
|
||||
if not args['nsd_template']:
|
||||
raise exceptions.InvalidInput('The nsd file is empty')
|
||||
|
||||
if parsed_args.param_file:
|
||||
with open(parsed_args.param_file) as f:
|
||||
param_yaml = f.read()
|
||||
try:
|
||||
args['attributes']['param_values'] = yaml.load(
|
||||
param_yaml, Loader=yaml.SafeLoader)
|
||||
except yaml.YAMLError as e:
|
||||
raise exceptions.InvalidInput(e)
|
||||
tackerV10.update_dict(parsed_args, body[self.resource],
|
||||
['tenant_id', 'name', 'description',
|
||||
'nsd_id', 'vim_id'])
|
||||
return body
|
||||
|
||||
|
||||
class DeleteNS(tackerV10.DeleteCommand):
|
||||
"""Delete given NS(s)."""
|
||||
|
||||
resource = _NS
|
||||
deleted_msg = {'ns': 'delete initiated'}
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'--force',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help=_('Force delete Network Service'))
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
if parsed_args.force:
|
||||
body = {self.resource: {'attributes': {'force': True}}}
|
||||
else:
|
||||
body = dict()
|
||||
return body
|
||||
@@ -1,102 +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 __future__ import print_function
|
||||
|
||||
import yaml
|
||||
|
||||
from oslo_serialization import jsonutils
|
||||
|
||||
from tackerclient.i18n import _
|
||||
from tackerclient.tacker import v1_0 as tackerV10
|
||||
|
||||
_NSD = "nsd"
|
||||
|
||||
|
||||
class ListNSD(tackerV10.ListCommand):
|
||||
"""List NSDs that belong to a given tenant."""
|
||||
|
||||
resource = _NSD
|
||||
list_columns = ['id', 'name', 'template_source', 'description']
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListNSD, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'--template-source',
|
||||
help=_("List NSD with specified template source. Available \
|
||||
options are 'onboared' (default), 'inline' or 'all'"),
|
||||
action='store',
|
||||
default='onboarded')
|
||||
return parser
|
||||
|
||||
def args2search_opts(self, parsed_args):
|
||||
search_opts = super(ListNSD, self).args2search_opts(parsed_args)
|
||||
template_source = parsed_args.template_source
|
||||
if parsed_args.template_source:
|
||||
search_opts.update({'template_source': template_source})
|
||||
return search_opts
|
||||
|
||||
|
||||
class ShowNSD(tackerV10.ShowCommand):
|
||||
"""Show information of a given NSD."""
|
||||
|
||||
resource = _NSD
|
||||
|
||||
|
||||
class CreateNSD(tackerV10.CreateCommand):
|
||||
"""Create a NSD."""
|
||||
resource = _NSD
|
||||
remove_output_fields = ["attributes"]
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
parser.add_argument('--nsd-file', help='Specify NSD file',
|
||||
required=True)
|
||||
parser.add_argument(
|
||||
'name', metavar='NAME',
|
||||
help='Set a name for the NSD')
|
||||
parser.add_argument(
|
||||
'--description',
|
||||
help='Set a description for the NSD')
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
body = {self.resource: {}}
|
||||
nsd = None
|
||||
with open(parsed_args.nsd_file) as f:
|
||||
nsd = yaml.safe_load(f.read())
|
||||
tackerV10.update_dict(parsed_args, body[self.resource],
|
||||
['tenant_id', 'name', 'description'])
|
||||
if nsd:
|
||||
body[self.resource]['attributes'] = {'nsd': nsd}
|
||||
|
||||
return body
|
||||
|
||||
|
||||
class DeleteNSD(tackerV10.DeleteCommand):
|
||||
"""Delete a given NSD."""
|
||||
resource = _NSD
|
||||
|
||||
|
||||
class ShowTemplateNSD(tackerV10.ShowCommand):
|
||||
"""Show template of a given NSD."""
|
||||
resource = _NSD
|
||||
|
||||
def run(self, parsed_args):
|
||||
self.log.debug('run(%s)', parsed_args)
|
||||
template = None
|
||||
data = self.get_data(parsed_args)
|
||||
try:
|
||||
attributes_index = data[0].index('attributes')
|
||||
attributes_json = data[1][attributes_index]
|
||||
template = jsonutils.loads(attributes_json).get('nsd', None)
|
||||
except (IndexError, TypeError, ValueError) as e:
|
||||
self.log.debug('Data handling error: %s', str(e))
|
||||
print(template or _('Unable to display NSD template!'))
|
||||
@@ -1,137 +0,0 @@
|
||||
# Copyright 2016 Brocade Communications Systems Inc
|
||||
# All Rights Reserved.
|
||||
#
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import yaml
|
||||
|
||||
from oslo_utils import strutils
|
||||
|
||||
from tackerclient.common import exceptions
|
||||
from tackerclient.i18n import _
|
||||
from tackerclient.tacker import v1_0 as tackerV10
|
||||
from tackerclient.tacker.v1_0.nfvo import vim_utils
|
||||
|
||||
_VIM = "vim"
|
||||
|
||||
|
||||
class ListVIM(tackerV10.ListCommand):
|
||||
"""List VIMs that belong to a given tenant."""
|
||||
|
||||
resource = _VIM
|
||||
list_columns = ['id', 'tenant_id', 'name', 'type', 'is_default',
|
||||
'placement_attr', 'status']
|
||||
|
||||
|
||||
class ShowVIM(tackerV10.ShowCommand):
|
||||
"""Show information of a given VIM."""
|
||||
|
||||
resource = _VIM
|
||||
|
||||
|
||||
class CreateVIM(tackerV10.CreateCommand):
|
||||
"""Create a VIM."""
|
||||
|
||||
resource = _VIM
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'--config-file',
|
||||
required=True,
|
||||
help=_('YAML file with VIM configuration parameters'))
|
||||
parser.add_argument(
|
||||
'name', metavar='NAME',
|
||||
help=_('Set a name for the VIM'))
|
||||
parser.add_argument(
|
||||
'--description',
|
||||
help=_('Set a description for the VIM'))
|
||||
parser.add_argument(
|
||||
'--is-default',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help=_('Set as default VIM'))
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
body = {self.resource: {}}
|
||||
if parsed_args.config_file:
|
||||
with open(parsed_args.config_file) as f:
|
||||
vim_config = f.read()
|
||||
try:
|
||||
config_param = yaml.load(vim_config,
|
||||
Loader=yaml.SafeLoader)
|
||||
except yaml.YAMLError as e:
|
||||
raise exceptions.InvalidInput(e)
|
||||
vim_obj = body[self.resource]
|
||||
try:
|
||||
auth_url = config_param.pop('auth_url')
|
||||
except KeyError:
|
||||
raise exceptions.TackerClientException(message='Auth URL must be '
|
||||
'specified',
|
||||
status_code=404)
|
||||
vim_obj['auth_url'] = vim_utils.validate_auth_url(auth_url).geturl()
|
||||
vim_utils.args2body_vim(config_param, vim_obj)
|
||||
tackerV10.update_dict(parsed_args, body[self.resource],
|
||||
['tenant_id', 'name', 'description',
|
||||
'is_default'])
|
||||
return body
|
||||
|
||||
|
||||
class UpdateVIM(tackerV10.UpdateCommand):
|
||||
"""Update a given VIM."""
|
||||
|
||||
resource = _VIM
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'--config-file',
|
||||
required=False,
|
||||
help=_('YAML file with VIM configuration parameters'))
|
||||
parser.add_argument(
|
||||
'--name',
|
||||
help=_('New name for the VIM'))
|
||||
parser.add_argument(
|
||||
'--description',
|
||||
help=_('New description for the VIM'))
|
||||
parser.add_argument(
|
||||
'--is-default',
|
||||
type=strutils.bool_from_string,
|
||||
metavar='{True,False}',
|
||||
help=_('Indicate whether the VIM is used as default'))
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
body = {self.resource: {}}
|
||||
config_param = None
|
||||
# config arg passed as data overrides config yaml when both args passed
|
||||
if parsed_args.config_file:
|
||||
with open(parsed_args.config_file) as f:
|
||||
config_yaml = f.read()
|
||||
try:
|
||||
config_param = yaml.load(config_yaml)
|
||||
except yaml.YAMLError as e:
|
||||
raise exceptions.InvalidInput(e)
|
||||
vim_obj = body[self.resource]
|
||||
if config_param is not None:
|
||||
vim_utils.args2body_vim(config_param, vim_obj)
|
||||
tackerV10.update_dict(parsed_args, body[self.resource],
|
||||
['tenant_id', 'name', 'description',
|
||||
'is_default'])
|
||||
# type attribute is read-only, it can't be updated, so remove it
|
||||
# in update method
|
||||
body['vim'].pop('type', None)
|
||||
return body
|
||||
|
||||
|
||||
class DeleteVIM(tackerV10.DeleteCommand):
|
||||
"""Delete given VIM(s)."""
|
||||
resource = _VIM
|
||||
@@ -1,86 +0,0 @@
|
||||
# Copyright 2016 Brocade Communications Systems Inc
|
||||
# All Rights Reserved.
|
||||
#
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
import six.moves.urllib.parse as urlparse
|
||||
|
||||
from tackerclient.common import exceptions
|
||||
|
||||
|
||||
def args2body_vim(config_param, vim):
|
||||
"""Create additional args to vim body
|
||||
|
||||
:param vim: vim request object
|
||||
:return: vim body with args populated
|
||||
"""
|
||||
vim_type = ['openstack', 'kubernetes']
|
||||
cert_verify_type = ['True', 'False']
|
||||
|
||||
if 'type' in config_param:
|
||||
vim['type'] = config_param.pop('type', '')
|
||||
if not vim['type'] in vim_type:
|
||||
raise exceptions.TackerClientException(
|
||||
message='Supported VIM types: openstack, kubernetes',
|
||||
status_code=400)
|
||||
else:
|
||||
vim['type'] = 'openstack'
|
||||
if vim['type'] == 'openstack':
|
||||
vim['vim_project'] = {
|
||||
'name': config_param.pop('project_name', ''),
|
||||
'project_domain_name':
|
||||
config_param.pop('project_domain_name', '')}
|
||||
if not vim['vim_project']['name']:
|
||||
raise exceptions.TackerClientException(
|
||||
message='Project name must be specified',
|
||||
status_code=404)
|
||||
cert_verify = config_param.pop('cert_verify', 'True')
|
||||
if cert_verify not in cert_verify_type:
|
||||
raise exceptions.TackerClientException(
|
||||
message='Supported cert_verify types: True, False',
|
||||
status_code=400)
|
||||
vim['auth_cred'] = {'username': config_param.pop('username', ''),
|
||||
'password': config_param.pop('password', ''),
|
||||
'user_domain_name':
|
||||
config_param.pop('user_domain_name', ''),
|
||||
'cert_verify': cert_verify}
|
||||
elif vim['type'] == 'kubernetes':
|
||||
vim['vim_project'] = {
|
||||
'name': config_param.pop('project_name', '')}
|
||||
if not vim['vim_project']['name']:
|
||||
raise exceptions.TackerClientException(
|
||||
message='Project name must be specified in Kubernetes VIM,'
|
||||
'it is namespace in Kubernetes environment',
|
||||
status_code=404)
|
||||
if ('username' in config_param) and ('password' in config_param):
|
||||
vim['auth_cred'] = {
|
||||
'username': config_param.pop('username', ''),
|
||||
'password': config_param.pop('password', '')}
|
||||
elif 'bearer_token' in config_param:
|
||||
vim['auth_cred'] = {
|
||||
'bearer_token': config_param.pop('bearer_token', '')}
|
||||
else:
|
||||
raise exceptions.TackerClientException(
|
||||
message='username and password or bearer_token must be'
|
||||
'provided',
|
||||
status_code=404)
|
||||
ssl_ca_cert = config_param.pop('ssl_ca_cert', '')
|
||||
if ssl_ca_cert:
|
||||
vim['auth_cred']['ssl_ca_cert'] = ssl_ca_cert
|
||||
|
||||
|
||||
def validate_auth_url(url):
|
||||
url_parts = urlparse.urlparse(url)
|
||||
if not url_parts.scheme or not url_parts.netloc:
|
||||
raise exceptions.TackerClientException(message='Invalid auth URL')
|
||||
return url_parts
|
||||
@@ -1,191 +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 tackerclient.tacker import v1_0 as tackerV10
|
||||
import yaml
|
||||
|
||||
_CLUSTER = 'cluster'
|
||||
_CLUSTER_MEMBER = 'clustermember'
|
||||
|
||||
|
||||
class ListCluster(tackerV10.ListCommand):
|
||||
"""List Clusters that belong to a given tenant."""
|
||||
|
||||
resource = _CLUSTER
|
||||
list_columns = ['id', 'name', 'vnfd_id', 'status', 'vip_endpoint']
|
||||
|
||||
|
||||
class ShowCluster(tackerV10.ShowCommand):
|
||||
"""Show information of a given Cluster."""
|
||||
|
||||
resource = _CLUSTER
|
||||
|
||||
|
||||
class DeleteCluster(tackerV10.DeleteCommand):
|
||||
"""Delete a given Cluster."""
|
||||
|
||||
resource = _CLUSTER
|
||||
|
||||
|
||||
class CreateCluster(tackerV10.CreateCommand):
|
||||
"""Create a Cluster."""
|
||||
|
||||
resource = _CLUSTER
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'name', metavar='NAME',
|
||||
help='Set a name for the VNF cluster')
|
||||
vnfd_group = parser.add_mutually_exclusive_group(required=True)
|
||||
vnfd_group.add_argument(
|
||||
'--vnfd-id',
|
||||
help='VNFD ID to use as template to create member VNF')
|
||||
vnfd_group.add_argument(
|
||||
'--vnfd-name',
|
||||
help='VNFD name to use as template to create member VNF')
|
||||
parser.add_argument('--policy-file',
|
||||
help='Specify policy file for cluster',
|
||||
required=True)
|
||||
parser.add_argument(
|
||||
'--description',
|
||||
help='Set a description for the created VNF cluster')
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
body = {self.resource: {}}
|
||||
tacker_client = self.get_client()
|
||||
tacker_client.format = parsed_args.request_format
|
||||
|
||||
if parsed_args.vnfd_name:
|
||||
_id = tackerV10.find_resourceid_by_name_or_id(tacker_client,
|
||||
'vnfd',
|
||||
parsed_args.
|
||||
vnfd_name)
|
||||
parsed_args.vnfd_id = _id
|
||||
policy_info = None
|
||||
with open(parsed_args.policy_file) as f:
|
||||
policy_info = yaml.safe_load(f.read())
|
||||
tackerV10.update_dict(parsed_args, body[self.resource],
|
||||
['tenant_id', 'name', 'vnfd_id', 'description'])
|
||||
if policy_info:
|
||||
body[self.resource]['policy_info'] = policy_info
|
||||
return body
|
||||
|
||||
|
||||
class AddClusterMember(tackerV10.CreateCommand):
|
||||
"""Add a new Cluster Member to given Cluster."""
|
||||
|
||||
resource = _CLUSTER_MEMBER
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'name', metavar='NAME',
|
||||
help='Set a name for the VNF cluster member')
|
||||
cluster_group = parser.add_mutually_exclusive_group()
|
||||
cluster_group.add_argument(
|
||||
'--cluster-id',
|
||||
help='VNFD ID to use as template to create member VNF')
|
||||
cluster_group.add_argument(
|
||||
'--cluster-name',
|
||||
help='VNFD name to use as template to create member VNF')
|
||||
vnfd_group = parser.add_mutually_exclusive_group()
|
||||
vnfd_group.add_argument(
|
||||
'--vnfd-id',
|
||||
help='Set a id for the VNFD')
|
||||
vnfd_group.add_argument(
|
||||
'--vnfd-name',
|
||||
help='Set a name for the VNFD')
|
||||
parser.add_argument(
|
||||
'--role',
|
||||
help='Set a [Active/Standby] role to cluster member',
|
||||
required=True)
|
||||
vim_group = parser.add_mutually_exclusive_group()
|
||||
vim_group.add_argument(
|
||||
'--vim-id',
|
||||
help='Set a VIM ID to deploy cluster member')
|
||||
vim_group.add_argument(
|
||||
'--vim-name',
|
||||
help='Set a VIM name to deploy cluster member')
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
body = {self.resource: {}}
|
||||
|
||||
tacker_client = self.get_client()
|
||||
tacker_client.format = parsed_args.request_format
|
||||
if parsed_args.cluster_name:
|
||||
_id = tackerV10.find_resourceid_by_name_or_id(tacker_client,
|
||||
'cluster',
|
||||
parsed_args.
|
||||
cluster_name)
|
||||
parsed_args.cluster_id = _id
|
||||
if parsed_args.vnfd_name:
|
||||
_id = tackerV10.find_resourceid_by_name_or_id(tacker_client,
|
||||
'vnfd',
|
||||
parsed_args.
|
||||
vnfd_name)
|
||||
parsed_args.vnfd_id = _id
|
||||
parsed_args.role = parsed_args.role.upper()
|
||||
if parsed_args.vim_name:
|
||||
_id = tackerV10.find_resourceid_by_name_or_id(tacker_client,
|
||||
'vim',
|
||||
parsed_args.
|
||||
vim_name)
|
||||
parsed_args.vim_id = _id
|
||||
tackerV10.update_dict(parsed_args, body[self.resource],
|
||||
['tenant_id', 'name', 'cluster_id', 'vnfd_id',
|
||||
'role', 'vim_id'])
|
||||
return body
|
||||
|
||||
|
||||
class ListClusterMember(tackerV10.ListCommand):
|
||||
"""List Cluster Members that belong to a given tenant."""
|
||||
|
||||
resource = _CLUSTER_MEMBER
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
cluster_group = parser.add_mutually_exclusive_group(required=True)
|
||||
cluster_group.add_argument(
|
||||
'--cluster-id',
|
||||
help='Set a ID for the queried cluster')
|
||||
cluster_group.add_argument(
|
||||
'--cluster-name',
|
||||
help='Set a name for the queried cluster')
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
body = {self.resource: {}}
|
||||
tacker_client = self.get_client()
|
||||
tacker_client.format = parsed_args.request_format
|
||||
|
||||
if parsed_args.cluster_name:
|
||||
_id = tackerV10.find_resourceid_by_name_or_id(tacker_client,
|
||||
'cluster',
|
||||
parsed_args.
|
||||
cluster_name)
|
||||
parsed_args.cluster_id = _id
|
||||
|
||||
tackerV10.update_dict(parsed_args, body[self.resource],
|
||||
['tenant_id', 'cluster_id'])
|
||||
return body
|
||||
|
||||
list_columns = ['id', 'name', 'cluster_id', 'role', 'vnf_id',
|
||||
'vim_id', 'mgmt_ip_address', 'lb_member_id']
|
||||
|
||||
|
||||
class DeleteClusterMember(tackerV10.DeleteCommand):
|
||||
"""Delete a given Cluster Member."""
|
||||
|
||||
resource = _CLUSTER_MEMBER
|
||||
|
||||
|
||||
class ShowClusterMember(tackerV10.ShowCommand):
|
||||
"""Show information of a given Cluster Member."""
|
||||
|
||||
resource = _CLUSTER_MEMBER
|
||||
@@ -1,232 +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 yaml
|
||||
|
||||
from tackerclient.common import exceptions
|
||||
from tackerclient.i18n import _
|
||||
from tackerclient.tacker import v1_0 as tackerV10
|
||||
|
||||
|
||||
_VNFFG = 'vnffg'
|
||||
_NFP = 'nfp'
|
||||
_SFC = 'sfc'
|
||||
_FC = 'classifier'
|
||||
|
||||
|
||||
class ListFC(tackerV10.ListCommand):
|
||||
"""List FCs that belong to a given tenant."""
|
||||
|
||||
resource = _FC
|
||||
list_columns = ['id', 'status', 'nfp_id', 'chain_id']
|
||||
|
||||
def extend_list(self, data, parsed_args):
|
||||
"""Update the list_columns list.
|
||||
|
||||
This method update the list_columns list by adding the
|
||||
'name' column in case the retrieved FC list from the tacker
|
||||
server side contains the names of the FCs.
|
||||
"""
|
||||
for item in data:
|
||||
if 'name' in item:
|
||||
self.list_columns.insert(1, 'name')
|
||||
break
|
||||
|
||||
|
||||
class ShowFC(tackerV10.ShowCommand):
|
||||
"""Show information of a given FC."""
|
||||
|
||||
resource = _FC
|
||||
|
||||
|
||||
class ListSFC(tackerV10.ListCommand):
|
||||
"""List SFCs that belong to a given tenant."""
|
||||
|
||||
resource = _SFC
|
||||
list_columns = ['id', 'status', 'nfp_id']
|
||||
|
||||
|
||||
class ShowSFC(tackerV10.ShowCommand):
|
||||
"""Show information of a given SFC."""
|
||||
|
||||
resource = _SFC
|
||||
|
||||
|
||||
class ListNFP(tackerV10.ListCommand):
|
||||
"""List NFPs that belong to a given tenant."""
|
||||
|
||||
resource = _NFP
|
||||
list_columns = ['id', 'name', 'status', 'vnffg_id', 'path_id']
|
||||
|
||||
|
||||
class ShowNFP(tackerV10.ShowCommand):
|
||||
"""Show information of a given NFP."""
|
||||
|
||||
resource = _NFP
|
||||
|
||||
|
||||
class ListVNFFG(tackerV10.ListCommand):
|
||||
"""List VNFFGs that belong to a given tenant."""
|
||||
|
||||
resource = _VNFFG
|
||||
list_columns = ['id', 'name', 'ns_id',
|
||||
'description', 'status', 'vnffgd_id']
|
||||
|
||||
|
||||
class ShowVNFFG(tackerV10.ShowCommand):
|
||||
"""Show information of a given VNFFG."""
|
||||
|
||||
resource = _VNFFG
|
||||
|
||||
|
||||
class CreateVNFFG(tackerV10.CreateCommand):
|
||||
"""Create a VNFFG."""
|
||||
|
||||
resource = _VNFFG
|
||||
remove_output_fields = ["attributes"]
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'name', metavar='NAME',
|
||||
help=_('Set a name for the VNFFG'))
|
||||
vnffgd_group = parser.add_mutually_exclusive_group(required=True)
|
||||
vnffgd_group.add_argument(
|
||||
'--vnffgd-id',
|
||||
help=_('VNFFGD ID to use as template to create VNFFG'))
|
||||
vnffgd_group.add_argument(
|
||||
'--vnffgd-name',
|
||||
help=_('VNFFGD Name to use as template to create VNFFG'))
|
||||
vnffgd_group.add_argument(
|
||||
'--vnffgd-template',
|
||||
help=_('VNFFGD file to create VNFFG'))
|
||||
parser.add_argument(
|
||||
'--vnf-mapping',
|
||||
help=_('List of logical VNFD name to VNF instance name mapping. '
|
||||
'Example: VNF1:my_vnf1,VNF2:my_vnf2'))
|
||||
parser.add_argument(
|
||||
'--symmetrical',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help=_('Should a reverse path be created for the NFP'))
|
||||
parser.add_argument(
|
||||
'--param-file',
|
||||
help='Specify parameter yaml file'
|
||||
)
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
args = {'attributes': {}}
|
||||
body = {self.resource: args}
|
||||
|
||||
tacker_client = self.get_client()
|
||||
tacker_client.format = parsed_args.request_format
|
||||
|
||||
if parsed_args.vnf_mapping:
|
||||
_vnf_mapping = dict()
|
||||
_vnf_mappings = parsed_args.vnf_mapping.split(",")
|
||||
for mapping in _vnf_mappings:
|
||||
vnfd_name, vnf = mapping.split(":", 1)
|
||||
_vnf_mapping[vnfd_name] = \
|
||||
tackerV10.find_resourceid_by_name_or_id(
|
||||
tacker_client, 'vnf', vnf)
|
||||
|
||||
parsed_args.vnf_mapping = _vnf_mapping
|
||||
|
||||
if parsed_args.vnffgd_name:
|
||||
_id = tackerV10.find_resourceid_by_name_or_id(tacker_client,
|
||||
'vnffgd',
|
||||
parsed_args.
|
||||
vnffgd_name)
|
||||
parsed_args.vnffgd_id = _id
|
||||
elif parsed_args.vnffgd_template:
|
||||
with open(parsed_args.vnffgd_template) as f:
|
||||
template = f.read()
|
||||
try:
|
||||
args['vnffgd_template'] = yaml.load(
|
||||
template, Loader=yaml.SafeLoader)
|
||||
except yaml.YAMLError as e:
|
||||
raise exceptions.InvalidInput(e)
|
||||
if not args['vnffgd_template']:
|
||||
raise exceptions.InvalidInput('The vnffgd file is empty')
|
||||
|
||||
if parsed_args.param_file:
|
||||
with open(parsed_args.param_file) as f:
|
||||
param_yaml = f.read()
|
||||
try:
|
||||
args['attributes']['param_values'] = yaml.load(
|
||||
param_yaml, Loader=yaml.SafeLoader)
|
||||
except yaml.YAMLError as e:
|
||||
raise exceptions.InvalidInput(e)
|
||||
|
||||
tackerV10.update_dict(parsed_args, body[self.resource],
|
||||
['tenant_id', 'name', 'vnffgd_id',
|
||||
'symmetrical', 'vnf_mapping'])
|
||||
return body
|
||||
|
||||
|
||||
class UpdateVNFFG(tackerV10.UpdateCommand):
|
||||
"""Update a given VNFFG."""
|
||||
|
||||
resource = _VNFFG
|
||||
|
||||
def add_known_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'--vnffgd-template',
|
||||
help=_('VNFFGD file to update VNFFG')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--vnf-mapping',
|
||||
help=_('List of logical VNFD name to VNF instance name mapping. '
|
||||
'Example: VNF1:my_vnf1,VNF2:my_vnf2'))
|
||||
parser.add_argument(
|
||||
'--symmetrical',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help=_('Should a reverse path be created for the NFP'))
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
args = {}
|
||||
body = {self.resource: args}
|
||||
|
||||
tacker_client = self.get_client()
|
||||
tacker_client.format = parsed_args.request_format
|
||||
|
||||
if parsed_args.vnf_mapping:
|
||||
_vnf_mapping = dict()
|
||||
_vnf_mappings = parsed_args.vnf_mapping.split(",")
|
||||
for mapping in _vnf_mappings:
|
||||
vnfd_name, vnf = mapping.split(":", 1)
|
||||
_vnf_mapping[vnfd_name] = \
|
||||
tackerV10.find_resourceid_by_name_or_id(
|
||||
tacker_client, 'vnf', vnf)
|
||||
|
||||
parsed_args.vnf_mapping = _vnf_mapping
|
||||
|
||||
if parsed_args.vnffgd_template:
|
||||
with open(parsed_args.vnffgd_template) as f:
|
||||
template = f.read()
|
||||
try:
|
||||
args['vnffgd_template'] = yaml.load(
|
||||
template, Loader=yaml.SafeLoader)
|
||||
except yaml.YAMLError as e:
|
||||
raise exceptions.InvalidInput(e)
|
||||
if not args['vnffgd_template']:
|
||||
raise exceptions.InvalidInput('The vnffgd template is empty')
|
||||
|
||||
tackerV10.update_dict(parsed_args, body[self.resource],
|
||||
['tenant_id', 'vnf_mapping', 'symmetrical'])
|
||||
return body
|
||||
|
||||
|
||||
class DeleteVNFFG(tackerV10.DeleteCommand):
|
||||
"""Delete a given VNFFG."""
|
||||
|
||||
resource = _VNFFG
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user