Compare commits

..

11 Commits

Author SHA1 Message Date
gong yong sheng
f8bb518893 Add domain information into auth cred.
If we use v3 keystone, these information should be passed
into tacker server.

Change-Id: Ieb5612c966b2110930faac0401f5dabde064ffc1
Partial-Bug: #1603851
(cherry picked from commit 0aa7efd292)
2016-07-29 19:07:16 +00:00
OpenStack Proposal Bot
56e3f5f5bb Updated from global requirements
Change-Id: I07c61da636c8fd3a6520e6861f33736bd6b46c28
2016-05-15 18:06:36 +00:00
OpenStack Proposal Bot
aa5d500024 Updated from global requirements
Change-Id: I691efc4f7c6129f5e64820077e84cfdf10bddb84
2016-04-29 22:41:22 +00:00
Sripriya
c0f8feca4b Do not display password in VIM command outputs
This fix masks the password attr in vim response for vim-list,
vim-show and vim-create commands.

Change-Id: I9b38b45f292a11f4615f3dc8eff672a4206c4a4a
Closes-Bug: #1567585
(cherry picked from commit 07444203ec)
2016-04-22 01:01:36 +00:00
Jenkins
1bccdcb084 Merge "Help message correction" into stable/mitaka 2016-04-22 00:25:56 +00:00
dharmendra
84fb385cd2 Help message correction
Help message correction

Change-Id: Iaf29d6c24968c28b1d7271d6363272565efbddb5
(cherry picked from commit 23dc390b4f)
2016-04-19 18:39:56 +00:00
dharmendra
240b8d434c Clean unused code.
Removing unused class.

Change-Id: Id134e24564cbd2f2e977cd823121e67d9183fc8e
(cherry picked from commit 6a2df0a918)
2016-04-19 18:37:54 +00:00
OpenStack Proposal Bot
3af84f735f Updated from global requirements
Change-Id: I0c12cc6fc25bf2a59fbf74482b0052b973b2c3bb
2016-04-18 15:07:04 +00:00
Jenkins
cb3607784d Merge "Updated from global requirements" into stable/mitaka 2016-03-24 21:16:06 +00:00
OpenStack Proposal Bot
91b9345f33 Updated from global requirements
Change-Id: I55fc9fb353d382d0f884634b011f9e7262f51618
2016-03-24 13:50:55 +00:00
Thierry Carrez
ea5212cb2a Update .gitreview for stable/mitaka
Change-Id: Ic2f7b386808979921371fb3d2bf2ee3c47bf3ec0
2016-03-23 17:45:51 +01:00
119 changed files with 1222 additions and 7328 deletions

5
.gitignore vendored
View File

@@ -17,10 +17,5 @@ run_tests.log
.autogenerated
.coverage
.testrepository/
.idea/
.tox/
.venv/
.stestr/
# Files created by releasenotes build
releasenotes/build

View File

@@ -1,5 +1,5 @@
[gerrit]
host=review.opendev.org
host=review.openstack.org
port=29418
project=openstack/python-tackerclient.git
defaultbranch=stable/stein
defaultbranch=stable/mitaka

View File

@@ -1,3 +0,0 @@
[DEFAULT]
test_path=./tackerclient/tests/unit
top_path=./

4
.testr.conf Normal file
View 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

View File

@@ -1,9 +0,0 @@
- project:
templates:
- check-requirements
- openstack-lower-constraints-jobs
- openstack-python-jobs
- openstack-python36-jobs
- openstack-python35-jobs-nonvoting
- publish-openstack-docs-pti
- release-notes-jobs-python3

View File

@@ -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

View File

@@ -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

View File

@@ -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/
* Tacker Documentation: https://docs.openstack.org/tacker/
* 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.

View File

@@ -1,7 +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 # BSD
openstackdocstheme>=1.18.1 # Apache-2.0
reno>=2.5.0 # Apache-2.0

View File

@@ -1,79 +0,0 @@
..
Licensed under the Apache License, Version 2.0 (the "License"); you may
not use this file except in compliance with the License. You may obtain
a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
Convention for heading levels in Neutron devref:
======= Heading 0 (reserved for the title in a document)
------- Heading 1
~~~~~~~ Heading 2
+++++++ Heading 3
''''''' Heading 4
(Avoid deeper levels because they do not render well.)
=========
CLI Usage
=========
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.

View File

@@ -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,28 +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'
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {'http://docs.python.org/': None}

View File

@@ -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

View File

@@ -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:** http://git.openstack.org/cgit/openstack/tacker
* **Tacker Client Library:** http://git.openstack.org/cgit/openstack/python-tackerclient
* **Tacker Service Bugs:** http://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

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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)

View File

@@ -1,61 +0,0 @@
appdirs==1.3.0
Babel==2.3.4
cliff==2.8.0
cmd2==0.8.0
coverage==4.0
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
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

View File

@@ -1,5 +0,0 @@
---
features:
- |
Support to create directly VNFFG and NS from its descriptor template
without creating VNFFGD and NSD.

View File

@@ -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.

View File

@@ -1,4 +0,0 @@
---
fixes:
- |
The VNFFGD CLI cannot show the VNFFGD template.

View File

@@ -1,4 +0,0 @@
---
fixes:
- |
Add documentation for python-tackerclient.

View File

@@ -1,4 +0,0 @@
---
fixes:
- |
Fix the VNFFG update osc command misusing create_vnffg function.

View File

@@ -1,4 +0,0 @@
---
fixes:
- |
Fix local test fail with pypy.

View File

@@ -1,4 +0,0 @@
---
features:
- |
Enable CLI to support clustering service in Tacker Server.

View File

@@ -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.

View File

@@ -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.

View File

@@ -1,4 +0,0 @@
---
deprecations:
- infra_driver and mgmt_driver attributes in VNFD client
attribute is deprecated and will be removed in Ocata.

View File

@@ -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

View File

@@ -1,3 +0,0 @@
---
features:
- Add support for multi delete feature for resources.

View File

@@ -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.

View File

@@ -1,4 +0,0 @@
---
features:
- Adds new CLI command 'vnf-resource-list' to view VNF
resources, such as VDU, CP, etc.

View File

@@ -1,3 +0,0 @@
---
fixes:
- Remove passing mgmt and infra driver from client.

View File

@@ -1,4 +0,0 @@
---
features:
- |
Add python-vnfd, vnf, nsd, ns, vnffgd, vnffg, event commands support.

View File

@@ -1,3 +0,0 @@
---
features:
- VIM can be updated without config-file argument in tacker vim-update command.

View File

@@ -1,3 +0,0 @@
---
features:
- Support to create VNF with direct VNFD template input from CLI.

View File

@@ -1,3 +0,0 @@
---
features:
- Made VNFD/VNF/VIM names mandatory in tacker CLI.

View File

@@ -1,3 +0,0 @@
---
features:
- Add client support for VNFFG.

View File

@@ -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 = ''

View File

@@ -1,14 +0,0 @@
Python-TackerClient Release Notes
=================================
Contents:
.. toctree::
:maxdepth: 2
unreleased
rocky
queens
pike
ocata
newton

View File

@@ -1,6 +0,0 @@
===================================
Newton Series Release Notes
===================================
.. release-notes::
:branch: origin/stable/newton

View File

@@ -1,6 +0,0 @@
===================================
Ocata Series Release Notes
===================================
.. release-notes::
:branch: origin/stable/ocata

View File

@@ -1,6 +0,0 @@
===================================
Pike Series Release Notes
===================================
.. release-notes::
:branch: stable/pike

View File

@@ -1,6 +0,0 @@
===================================
Queens Series Release Notes
===================================
.. release-notes::
:branch: stable/queens

View File

@@ -1,6 +0,0 @@
===================================
Rocky Series Release Notes
===================================
.. release-notes::
:branch: stable/rocky

View File

@@ -1,5 +0,0 @@
============================
Current Series Release Notes
============================
.. release-notes::

View File

@@ -1,18 +1,15 @@
# 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 # Apache-2.0
cliff!=1.16.0,!=1.17.0,>=1.15.0 # Apache-2.0
iso8601>=0.1.9 # MIT
netaddr!=0.7.16,>=0.7.12 # BSD
requests!=2.9.0,>=2.8.1 # Apache-2.0
python-keystoneclient!=1.8.0,!=2.1.0,<3.0.0,>=1.6.0 # Apache-2.0
simplejson>=2.2.0 # MIT
six>=1.9.0 # MIT
Babel!=2.3.0,!=2.3.1,!=2.3.2,!=2.3.3,>=1.3 # BSD
oslo.i18n>=2.1.0 # Apache-2.0
oslo.utils>=3.5.0 # Apache-2.0

View File

@@ -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
@@ -16,9 +16,6 @@ classifier =
Programming Language :: Python
Programming Language :: Python :: 2
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
Programming Language :: Python :: 3.5
Programming Language :: Python :: 3.6
[files]
packages =
@@ -32,60 +29,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
[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

View File

@@ -25,5 +25,5 @@ except ImportError:
pass
setuptools.setup(
setup_requires=['pbr>=2.0.0'],
setup_requires=['pbr>=1.8'],
pbr=True)

View File

@@ -185,7 +185,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)

View File

@@ -14,7 +14,7 @@
"""oslo.i18n integration module.
See https://docs.openstack.org/oslo.i18n/latest/user/index.html.
See http://docs.openstack.org/developer/oslo.i18n/usage.html .
"""
@@ -35,6 +35,16 @@ _C = _translators.contextual_form
# requires oslo.i18n >=2.1.0
_P = _translators.plural_form
# Translators for log levels.
#
# The abbreviated names are meant to reflect the usual use of a short
# name like '_'. The "L" is for "log" and the other letter comes from
# the level.
_LI = _translators.log_info
_LW = _translators.log_warning
_LE = _translators.log_error
_LC = _translators.log_critical
def get_available_languages():
return oslo_i18n.get_available_languages(DOMAIN)

View File

@@ -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."""

View File

@@ -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:

View File

@@ -203,10 +203,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):

View File

@@ -21,10 +21,10 @@ import argparse
import logging
import os
from oslo_log import versionutils
from oslo_utils import encodeutils
from oslo_utils import importutils
import six
import six.moves.urllib.parse as urlparse
from tackerclient.common import exceptions
from tackerclient.i18n import _
@@ -174,13 +174,8 @@ 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 validate_url(url):
url_parts = urlparse.urlparse(url)
if not url_parts.scheme or not url_parts.netloc or not url_parts.port:
raise exceptions.TackerClientException(message='Invalid URL')
return url_parts

View File

@@ -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

View File

@@ -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

View File

@@ -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))

View File

@@ -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.

View File

@@ -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']))

View File

@@ -1,240 +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)")
)
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.ns:
try:
obj = tackerV10.find_resourceid_by_name_or_id(
client, _NS, resource_id)
client.delete_ns(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': _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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -1,456 +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])
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.ShowOne):
_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}
client = self.app.client_manager.tackerclient
client.format = parsed_args.request_format
_id = tackerV10.find_resourceid_by_name_or_id(
client, 'vnf',
parsed_args.vnf)
parsed_args.vnf_id = _id
args['vnf_id'] = parsed_args.vnf_id
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)
vnf = client.scale_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)

View File

@@ -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)

View File

@@ -45,16 +45,10 @@ 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 vnf
from tackerclient.tacker.v1_0.vm import vnfd
from tackerclient.version import __version__
@@ -121,8 +115,6 @@ COMMAND_V1 = {
'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'
@@ -131,54 +123,6 @@ COMMAND_V1 = {
'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}
@@ -352,10 +296,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 +315,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 +341,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 +352,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 +371,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',

View File

@@ -25,6 +25,7 @@ from cliff.formatters import table
from cliff import lister
from cliff import show
from oslo_serialization import jsonutils
from oslo_utils import strutils
import six
from tackerclient.common._i18n import _
@@ -49,10 +50,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 +97,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',
@@ -279,12 +278,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 +302,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)
@@ -387,7 +386,9 @@ 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():
data[self.resource] = strutils.mask_dict_password(
data[self.resource])
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)
@@ -428,7 +429,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,7 +451,7 @@ class CreateCommand(TackerCommand, show.ShowOne):
info.pop(f)
else:
info = {'': ''}
return zip(*sorted(info.items()))
return zip(*sorted(info.iteritems()))
class UpdateCommand(TackerCommand):
@@ -500,7 +501,7 @@ class UpdateCommand(TackerCommand):
class DeleteCommand(TackerCommand):
"""Delete given resource(s)
"""Delete a given resource
"""
@@ -508,67 +509,34 @@ class DeleteCommand(TackerCommand):
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
@@ -681,10 +649,6 @@ class ShowCommand(TackerCommand, show.ShowOne):
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 +657,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 +682,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

View File

@@ -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

View File

@@ -14,6 +14,7 @@
# under the License.
#
from tackerclient.common._i18n 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(ShowExt, 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

View File

@@ -1,125 +0,0 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import 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'}

View File

@@ -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!'))

View File

@@ -14,12 +14,11 @@
# License for the specific language governing permissions and limitations
# under the License.
from oslo_utils import strutils
import yaml
from oslo_utils import strutils
from tackerclient.common import exceptions
from tackerclient.i18n import _
from tackerclient.common import utils
from tackerclient.tacker import v1_0 as tackerV10
from tackerclient.tacker.v1_0.nfvo import vim_utils
@@ -30,8 +29,12 @@ 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']
list_columns = ['id', 'tenant_id', 'name', 'type', 'description',
'auth_url', 'placement_attr', 'auth_cred']
def extend_list(self, data, parsed_args):
for index, value in enumerate(data):
data[index] = strutils.mask_dict_password(value)
class ShowVIM(tackerV10.ShowCommand):
@@ -46,32 +49,27 @@ class CreateVIM(tackerV10.CreateCommand):
resource = _VIM
def add_known_arguments(self, parser):
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('--config-file', help='specify VIM specific '
'config parameters in a file')
group.add_argument('--config', help='specify VIM config parameters '
'as a direct input')
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'))
'--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'))
help='Set a description for the 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)
config_param = yaml.load(vim_config)
if parsed_args.config:
parsed_args.config = parsed_args.config.decode('unicode_escape')
config_param = yaml.load(parsed_args.config)
vim_obj = body[self.resource]
try:
auth_url = config_param.pop('auth_url')
@@ -79,11 +77,11 @@ class CreateVIM(tackerV10.CreateCommand):
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_obj['auth_url'] = utils.validate_url(auth_url).geturl()
vim_obj['type'] = config_param.pop('type', 'openstack')
vim_utils.args2body_vim(config_param, vim_obj)
tackerV10.update_dict(parsed_args, body[self.resource],
['tenant_id', 'name', 'description',
'is_default'])
['tenant_id', 'name', 'description'])
return body
@@ -93,45 +91,34 @@ class UpdateVIM(tackerV10.UpdateCommand):
resource = _VIM
def add_known_arguments(self, parser):
parser.add_argument(
group = parser.add_mutually_exclusive_group(required=True)
group.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'))
help='specify VIM specific config parameters in a file')
group.add_argument(
'--config',
help='specify VIM config parameters as a direct input')
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)
config_param = yaml.load(config_yaml)
if parsed_args.config:
parsed_args.config = parsed_args.config.decode('unicode_escape')
config_param = yaml.load(parsed_args.config)
if 'auth_url' in config_param:
raise exceptions.TackerClientException(message='Auth URL cannot '
'be updated',
status_code=404)
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)
vim_utils.args2body_vim(config_param, vim_obj)
tackerV10.update_dict(parsed_args, body[self.resource], ['tenant_id'])
return body
class DeleteVIM(tackerV10.DeleteCommand):
"""Delete given VIM(s)."""
"""Delete a given VIM."""
resource = _VIM

View File

@@ -13,7 +13,7 @@
# 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
@@ -24,63 +24,16 @@ def args2body_vim(config_param, vim):
: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
vim['vim_project'] = {'id': config_param.pop('project_id', ''),
'name': config_param.pop('project_name', ''),
'project_domain_name':
config_param.pop('project_domain_name', '')}
if not vim['vim_project']['id'] and not vim['vim_project']['name']:
raise exceptions.TackerClientException(message='Project Id or name '
'must be specified',
status_code=404)
vim['auth_cred'] = {'username': config_param.pop('username', ''),
'password': config_param.pop('password', ''),
'user_id': config_param.pop('user_id', ''),
'user_domain_name':
config_param.pop('user_domain_name', '')}

View File

@@ -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

View File

@@ -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

View File

@@ -1,99 +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
_VNFFGD = "vnffgd"
class ListVNFFGD(tackerV10.ListCommand):
"""List VNFFGDs that belong to a given tenant."""
resource = _VNFFGD
list_columns = ['id', 'name', 'template_source', 'description']
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 args2search_opts(self, parsed_args):
search_opts = super(ListVNFFGD, 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 ShowVNFFGD(tackerV10.ShowCommand):
"""Show information of a given VNFFGD."""
resource = _VNFFGD
class CreateVNFFGD(tackerV10.CreateCommand):
"""Create a VNFFGD."""
resource = _VNFFGD
remove_output_fields = ["attributes"]
def add_known_arguments(self, parser):
parser.add_argument('--vnffgd-file', help=_('Specify VNFFGD file'))
parser.add_argument(
'name', metavar='NAME',
help=_('Set a name for the VNFFGD'))
parser.add_argument(
'--description',
help=_('Set a description for the VNFFGD'))
def args2body(self, parsed_args):
body = {self.resource: {}}
if parsed_args.vnffgd_file:
with open(parsed_args.vnffgd_file) as f:
vnffgd = yaml.safe_load(f.read())
body[self.resource]['template'] = {'vnffgd': vnffgd}
tackerV10.update_dict(parsed_args, body[self.resource],
['tenant_id', 'name', 'description'])
return body
class DeleteVNFFGD(tackerV10.DeleteCommand):
"""Delete a given VNFFGD."""
resource = _VNFFGD
class ShowTemplateVNFFGD(tackerV10.ShowCommand):
"""Show template of a given VNFFGD."""
resource = _VNFFGD
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('template')
attributes_json = data[1][attributes_index]
template = jsonutils.loads(attributes_json).get('vnffgd', None)
except (IndexError, TypeError, ValueError) as e:
self.log.debug('Data handling error: %s', str(e))
print(template or _('Unable to display VNFFGD template!'))

View File

@@ -0,0 +1,147 @@
#
# Copyright 2013 Intel
# Copyright 2013 Isaku Yamahata <isaku.yamahata at intel com>
# <isaku.yamahata at gmail com>
# All Rights Reserved.
#
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# 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
_VNF = 'vnf'
class ListVNF(tackerV10.ListCommand):
"""List device that belong to a given tenant."""
resource = _VNF
list_columns = ['id', 'name', 'description', 'mgmt_url', 'status',
'vim_id', 'placement_attr']
class ShowVNF(tackerV10.ShowCommand):
"""show information of a given VNF."""
resource = _VNF
class CreateVNF(tackerV10.CreateCommand):
"""create a VNF."""
resource = _VNF
remove_output_fields = ["attributes"]
def add_known_arguments(self, parser):
parser.add_argument(
'--name',
help='Set a name for the vnf')
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')
vim_group = parser.add_mutually_exclusive_group()
vim_group.add_argument(
'--vim-id',
help='VIM ID to use to create VNF on the specified VIM')
vim_group.add_argument(
'--vim-name',
help='VIM name to use to create VNF on the specified VIM')
parser.add_argument(
'--vim-region-name',
help='VIM Region to use to create VNF on the specified VIM')
parser.add_argument(
'--config-file',
help='specify config yaml file')
parser.add_argument(
'--config',
help='specify config yaml data')
parser.add_argument(
'--param-file',
help='specify parameter yaml file'
)
def args2body(self, parsed_args):
args = {'attributes': {}}
body = {self.resource: args}
# 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()
args['attributes']['config'] = config_yaml
if parsed_args.config:
parsed_args.config = parsed_args.config.decode('unicode_escape')
args['attributes']['config'] = parsed_args.config
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.vnfd_name:
_id = tackerV10.find_resourceid_by_name_or_id(tacker_client,
'vnfd',
parsed_args.
vnfd_name)
parsed_args.vnfd_id = _id
if parsed_args.param_file:
with open(parsed_args.param_file) as f:
param_yaml = f.read()
args['attributes']['param_values'] = param_yaml
tackerV10.update_dict(parsed_args, body[self.resource],
['tenant_id', 'name', 'vnfd_id', 'vim_id'])
return body
class UpdateVNF(tackerV10.UpdateCommand):
"""Update a given VNF."""
resource = _VNF
def add_known_arguments(self, parser):
parser.add_argument(
'--config-file',
help='specify config yaml file')
parser.add_argument(
'--config',
help='specify config yaml data')
def args2body(self, parsed_args):
body = {self.resource: {}}
# 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()
body[self.resource]['attributes'] = {'config': config_yaml}
if parsed_args.config:
parsed_args.config = parsed_args.config.decode('unicode_escape')
body[self.resource]['attributes'] = {'config': parsed_args.config}
tackerV10.update_dict(parsed_args, body[self.resource], ['tenant_id'])
return body
class DeleteVNF(tackerV10.DeleteCommand):
"""Delete a given VNF."""
resource = _VNF

View File

@@ -1,5 +1,7 @@
#
# Copyright 2013 Intel Corporation
# Copyright 2013 Intel
# Copyright 2013 Isaku Yamahata <isaku.yamahata at intel com>
# <isaku.yamahata at gmail com>
# All Rights Reserved.
#
#
@@ -18,9 +20,7 @@
from __future__ import print_function
from oslo_serialization import jsonutils
import yaml
from tackerclient.common import exceptions
from tackerclient.i18n import _
from tackerclient.tacker import v1_0 as tackerV10
@@ -32,68 +32,48 @@ class ListVNFD(tackerV10.ListCommand):
"""List VNFD that belong to a given tenant."""
resource = _VNFD
list_columns = ['id', 'name', 'template_source', 'description']
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 args2search_opts(self, parsed_args):
search_opts = super(ListVNFD, 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
list_columns = ['id', 'name', 'description', 'infra_driver', 'mgmt_driver']
class ShowVNFD(tackerV10.ShowCommand):
"""Show information of a given VNFD."""
"""show information of a given VNFD."""
resource = _VNFD
class CreateVNFD(tackerV10.CreateCommand):
"""Create a VNFD."""
"""create a VNFD."""
resource = _VNFD
remove_output_fields = ["attributes"]
def add_known_arguments(self, parser):
parser.add_argument('--vnfd-file', help=_('Specify VNFD file'))
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('--vnfd-file', help='specify vnfd file')
group.add_argument('--vnfd', help='specify vnfd')
parser.add_argument(
'name', metavar='NAME',
help=_('Set a name for the VNFD'))
'--name',
help='Set a name for the vnfd')
parser.add_argument(
'--description',
help=_('Set a description for the VNFD'))
help='Set a description for the vnfd')
def args2body(self, parsed_args):
body = {self.resource: {}}
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:
raise exceptions.InvalidInput(e)
if not vnfd:
raise exceptions.InvalidInput("vnfd file is empty")
body[self.resource]['attributes'] = {'vnfd': vnfd}
if parsed_args.vnfd_file:
with open(parsed_args.vnfd_file) as f:
vnfd = f.read()
body[self.resource]['attributes'] = {'vnfd': vnfd}
if parsed_args.vnfd:
body[self.resource]['attributes'] = {'vnfd': parsed_args.vnfd}
tackerV10.update_dict(parsed_args, body[self.resource],
['tenant_id', 'name', 'description'])
return body
class DeleteVNFD(tackerV10.DeleteCommand):
"""Delete given VNFD(s)."""
"""Delete a given VNFD."""
resource = _VNFD

View File

@@ -1,319 +0,0 @@
#
# Copyright 2013 Intel Corporation
# All Rights Reserved.
#
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import yaml
from tackerclient.common import exceptions
from tackerclient.i18n import _
from tackerclient.tacker import v1_0 as tackerV10
_VNF = 'vnf'
_RESOURCE = 'resource'
class ListVNF(tackerV10.ListCommand):
"""List VNF that belong to a given tenant."""
resource = _VNF
list_columns = ['id', 'name', 'mgmt_ip_address', 'status',
'vim_id', 'vnfd_id']
class ShowVNF(tackerV10.ShowCommand):
"""Show information of a given VNF."""
resource = _VNF
class CreateVNF(tackerV10.CreateCommand):
"""Create a VNF."""
resource = _VNF
remove_output_fields = ["attributes"]
def add_known_arguments(self, parser):
parser.add_argument(
'name', metavar='NAME',
help=_('Set a name for the VNF'))
parser.add_argument(
'--description',
help=_('Set description for the VNF'))
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 use to create VNF on the specified VIM'))
vim_group.add_argument(
'--vim-name',
help=_('VIM name to use to create VNF on the specified VIM'))
parser.add_argument(
'--vim-region-name',
help=_('VIM Region to use to create VNF on the specified VIM'))
parser.add_argument(
'--config-file',
help=_('YAML file with VNF configuration'))
parser.add_argument(
'--param-file',
help=_('Specify parameter yaml file'))
def args2body(self, parsed_args):
args = {'attributes': {}}
body = {self.resource: args}
# config arg passed as data overrides config yaml when both args passed
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:
args['attributes']['config'] = config
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.vnfd_name:
_id = tackerV10.find_resourceid_by_name_or_id(tacker_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:
args['vnfd_template'] = yaml.load(
template, Loader=yaml.SafeLoader)
except yaml.YAMLError as e:
raise exceptions.InvalidInput(e)
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',
'vnfd_id', 'vim_id'])
return body
class UpdateVNF(tackerV10.UpdateCommand):
"""Update a given VNF."""
resource = _VNF
def add_known_arguments(self, parser):
parser.add_argument(
'--config-file',
help=_('YAML file with VNF configuration'))
parser.add_argument(
'--config',
help=_('Specify config yaml data'))
def args2body(self, parsed_args):
body = {self.resource: {}}
# config arg passed as data overrides config yaml when both args passed
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[self.resource]['attributes'] = {'config': config}
tackerV10.update_dict(parsed_args, body[self.resource], ['tenant_id'])
return body
class DeleteVNF(tackerV10.DeleteCommand):
"""Delete given VNF(s)."""
resource = _VNF
remove_output_fields = ["attributes"]
deleted_msg = {'vnf': 'delete initiated'}
def add_known_arguments(self, parser):
parser.add_argument(
'--force',
default=False,
action='store_true',
help=_('Force delete VNF instance'))
def args2body(self, parsed_args):
body = dict()
if parsed_args.force:
body[self.resource] = dict()
body[self.resource]['attributes'] = {'force': True}
return body
class ListVNFResources(tackerV10.ListCommand):
"""List resources of a VNF like VDU, CP, etc."""
list_columns = ['name', 'id', 'type']
allow_names = True
resource = _VNF
def get_id(self):
if self.resource:
return self.resource.upper()
def get_parser(self, prog_name):
parser = super(ListVNFResources, self).get_parser(prog_name)
if self.allow_names:
help_str = _('ID or name of %s to look up')
else:
help_str = _('ID of %s to look up')
parser.add_argument(
'id', metavar=self.get_id(),
help=help_str % self.resource)
return parser
def get_data(self, parsed_args):
self.log.debug('get_data(%s)', parsed_args)
tacker_client = self.get_client()
tacker_client.format = parsed_args.request_format
if self.allow_names:
_id = tackerV10.find_resourceid_by_name_or_id(tacker_client,
self.resource,
parsed_args.id)
else:
_id = parsed_args.id
data = self.retrieve_list_by_id(_id, parsed_args)
self.extend_list(data, parsed_args)
return self.setup_columns(data, parsed_args)
def retrieve_list_by_id(self, id, parsed_args):
"""Retrieve a list of sub resources from Tacker server"""
tacker_client = self.get_client()
tacker_client.format = parsed_args.request_format
_extra_values = tackerV10.parse_args_to_dict(self.values_specs)
tackerV10._merge_args(self, parsed_args, _extra_values,
self.values_specs)
search_opts = self.args2search_opts(parsed_args)
search_opts.update(_extra_values)
if self.pagination_support:
page_size = parsed_args.page_size
if page_size:
search_opts.update({'limit': page_size})
if self.sorting_support:
keys = parsed_args.sort_key
if keys:
search_opts.update({'sort_key': keys})
dirs = parsed_args.sort_dir
len_diff = len(keys) - len(dirs)
if len_diff > 0:
dirs += ['asc'] * len_diff
elif len_diff < 0:
dirs = dirs[:len(keys)]
if dirs:
search_opts.update({'sort_dir': dirs})
obj_lister = getattr(tacker_client, "list_vnf_resources")
data = obj_lister(id, **search_opts)
return data.get('resources', [])
class ScaleVNF(tackerV10.TackerCommand):
"""Scale a VNF."""
api = 'nfv-orchestration'
resource = None
log = None
def get_parser(self, prog_name):
parser = super(ScaleVNF, self).get_parser(prog_name)
self.add_known_arguments(parser)
return parser
def run(self, parsed_args):
tacker_client = self.get_client()
tacker_client.format = parsed_args.request_format
body = self.args2body(parsed_args)
obj_creator = getattr(tacker_client,
"scale_vnf")
obj_creator(body["scale"].pop('vnf_id'), body)
def add_known_arguments(self, parser):
vnf_group = parser.add_mutually_exclusive_group(required=True)
vnf_group.add_argument(
'--vnf-id',
help=_('VNF ID'))
vnf_group.add_argument(
'--vnf-name',
help=_('VNF 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"'))
def args2body(self, parsed_args):
args = {}
body = {"scale": args}
if parsed_args.vnf_name:
tacker_client = self.get_client()
tacker_client.format = parsed_args.request_format
_id = tackerV10.find_resourceid_by_name_or_id(tacker_client,
'vnf',
parsed_args.
vnf_name)
parsed_args.vnf_id = _id
args['vnf_id'] = parsed_args.vnf_id
args['type'] = parsed_args.scaling_type
args['policy'] = parsed_args.scaling_policy_name
return body

View File

@@ -0,0 +1,52 @@
# Copyright 2016 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 mock import sentinel
import testtools
from tackerclient.common import exceptions
from tackerclient.tacker.v1_0.nfvo import vim_utils
class CLITestAuthNoAuth(testtools.TestCase):
def test_args2body_vim(self):
config_param = {'project_id': sentinel.prj_id1,
'username': sentinel.usrname1,
'password': sentinel.password1,
'project_domain_name': sentinel.prj_domain_name1,
'user_domain_name': sentinel.user_domain.name, }
vim = {}
auth_cred = config_param.copy()
auth_cred.pop('project_id')
auth_cred.pop('project_domain_name')
auth_cred.update({'user_id': ''})
expected_vim = {'auth_cred': auth_cred,
'vim_project':
{'id': sentinel.prj_id1,
'name': '',
'project_domain_name': sentinel.prj_domain_name1}}
vim_utils.args2body_vim(config_param.copy(), vim)
self.assertEqual(expected_vim, vim)
def test_args2body_vim_no_project(self):
config_param = {'username': sentinel.usrname1,
'password': sentinel.password1,
'user_domain_name': sentinel.user_domain.name, }
vim = {}
self.assertRaises(exceptions.TackerClientException,
vim_utils.args2body_vim,
config_param, vim)

View File

@@ -19,12 +19,13 @@ import json
import uuid
from keystoneclient import exceptions as k_exceptions
import mock
import mox
import requests
import testtools
from tackerclient import client
from tackerclient.common import exceptions
from tackerclient.common import utils
USERNAME = 'testuser'
@@ -70,44 +71,39 @@ ENDPOINTS_RESULT = {
def get_response(status_code, headers=None):
response = mock.Mock().CreateMock(requests.Response)
response = mox.Mox().CreateMock(requests.Response)
response.headers = headers or {}
response.status_code = status_code
return response
resp_200 = get_response(200)
resp_401 = get_response(401)
headers = {'X-Auth-Token': '',
'User-Agent': 'python-tackerclient'}
expected_headers = {'X-Auth-Token': TOKEN,
'User-Agent': 'python-tackerclient'}
agent_header = {'User-Agent': 'python-tackerclient'}
class CLITestAuthNoAuth(testtools.TestCase):
def setUp(self):
"""Prepare the test environment."""
super(CLITestAuthNoAuth, self).setUp()
self.mox = mox.Mox()
self.client = client.HTTPClient(username=USERNAME,
tenant_name=TENANT_NAME,
password=PASSWORD,
endpoint_url=ENDPOINT_URL,
auth_strategy=NOAUTH,
region_name=REGION)
self.addCleanup(mock.patch.stopall)
self.addCleanup(self.mox.VerifyAll)
self.addCleanup(self.mox.UnsetStubs)
@mock.patch('tackerclient.client.HTTPClient.request')
def test_get_noauth(self, mock_request):
def test_get_noauth(self):
self.mox.StubOutWithMock(self.client, "request")
mock_request.return_value = (resp_200, '')
self.client.do_request('/resource', 'GET',
headers=headers)
mock_request.assert_called_once_with(
ENDPOINT_URL + '/resource',
'GET',
headers=headers)
res200 = get_response(200)
self.client.request(
mox.StrContains(ENDPOINT_URL + '/resource'), 'GET',
headers=mox.IsA(dict),
).AndReturn((res200, ''))
self.mox.ReplayAll()
self.client.do_request('/resource', 'GET')
self.assertEqual(self.client.endpoint_url, ENDPOINT_URL)
@@ -121,12 +117,14 @@ class CLITestAuthKeystone(testtools.TestCase):
def setUp(self):
"""Prepare the test environment."""
super(CLITestAuthKeystone, self).setUp()
self.mox = mox.Mox()
self.client = client.HTTPClient(username=USERNAME,
tenant_name=TENANT_NAME,
password=PASSWORD,
auth_url=AUTH_URL,
region_name=REGION)
self.addCleanup(mock.patch.stopall)
self.addCleanup(self.mox.VerifyAll)
self.addCleanup(self.mox.UnsetStubs)
def test_reused_token_get_auth_info(self):
"""Test Client.get_auth_info().
@@ -146,53 +144,69 @@ class CLITestAuthKeystone(testtools.TestCase):
'endpoint_url': self.client.endpoint_url}
self.assertEqual(client_.get_auth_info(), expected)
@mock.patch('tackerclient.client.HTTPClient.request')
def test_get_token(self, mock_request):
def test_get_token(self):
self.mox.StubOutWithMock(self.client, "request")
res200 = get_response(200)
self.client.request(
AUTH_URL + '/tokens', 'POST',
body=self.auth_body, headers=mox.IsA(dict)
).AndReturn((res200, json.dumps(KS_TOKEN_RESULT)))
self.client.request(
mox.StrContains(ENDPOINT_URL + '/resource'), 'GET',
headers=mox.ContainsKeyValue('X-Auth-Token', TOKEN)
).AndReturn((res200, ''))
self.mox.ReplayAll()
mock_request.return_value = (resp_200, json.dumps(KS_TOKEN_RESULT))
self.client.do_request('/resource', 'GET')
mock_request.assert_called_with(
ENDPOINT_URL + '/resource', 'GET',
headers=expected_headers)
self.assertEqual(self.client.endpoint_url, ENDPOINT_URL)
self.assertEqual(self.client.auth_token, TOKEN)
@mock.patch('tackerclient.client.HTTPClient.request')
def test_refresh_token(self, mock_request):
def test_refresh_token(self):
self.mox.StubOutWithMock(self.client, "request")
self.client.auth_token = TOKEN
self.client.endpoint_url = ENDPOINT_URL
# If a token is expired, tacker server returns 401
mock_request.return_value = (resp_401, '')
self.assertRaises(exceptions.Unauthorized,
self.client.do_request,
'/resource',
'GET')
res200 = get_response(200)
res401 = get_response(401)
mock_request.return_value = (resp_200, json.dumps(KS_TOKEN_RESULT))
# If a token is expired, tacker server retruns 401
self.client.request(
mox.StrContains(ENDPOINT_URL + '/resource'), 'GET',
headers=mox.ContainsKeyValue('X-Auth-Token', TOKEN)
).AndReturn((res401, ''))
self.client.request(
AUTH_URL + '/tokens', 'POST',
body=mox.IsA(str), headers=mox.IsA(dict)
).AndReturn((res200, json.dumps(KS_TOKEN_RESULT)))
self.client.request(
mox.StrContains(ENDPOINT_URL + '/resource'), 'GET',
headers=mox.ContainsKeyValue('X-Auth-Token', TOKEN)
).AndReturn((res200, ''))
self.mox.ReplayAll()
self.client.do_request('/resource', 'GET')
mock_request.assert_called_with(
ENDPOINT_URL + '/resource', 'GET',
headers=expected_headers)
@mock.patch('tackerclient.client.HTTPClient.request')
def test_refresh_token_no_auth_url(self, mock_request):
def test_refresh_token_no_auth_url(self):
self.mox.StubOutWithMock(self.client, "request")
self.client.auth_url = None
self.client.auth_token = TOKEN
self.client.endpoint_url = ENDPOINT_URL
res401 = get_response(401)
# If a token is expired, tacker server returns 401
mock_request.return_value = (resp_401, '')
self.client.request(
mox.StrContains(ENDPOINT_URL + '/resource'), 'GET',
headers=mox.ContainsKeyValue('X-Auth-Token', TOKEN)
).AndReturn((res401, ''))
self.mox.ReplayAll()
self.assertRaises(exceptions.NoAuthURLProvided,
self.client.do_request,
'/resource',
'GET')
expected_url = ENDPOINT_URL + '/resource'
mock_request.assert_called_with(expected_url, 'GET',
headers=expected_headers)
def test_get_endpoint_url_with_invalid_auth_url(self):
# Handle the case when auth_url is not provided
@@ -200,70 +214,85 @@ class CLITestAuthKeystone(testtools.TestCase):
self.assertRaises(exceptions.NoAuthURLProvided,
self.client._get_endpoint_url)
@mock.patch('tackerclient.client.HTTPClient.request')
def test_get_endpoint_url(self, mock_request):
def test_get_endpoint_url(self):
self.mox.StubOutWithMock(self.client, "request")
self.client.auth_token = TOKEN
mock_request.return_value = (resp_200, json.dumps(ENDPOINTS_RESULT))
res200 = get_response(200)
self.client.request(
mox.StrContains(AUTH_URL + '/tokens/%s/endpoints' % TOKEN), 'GET',
headers=mox.IsA(dict)
).AndReturn((res200, json.dumps(ENDPOINTS_RESULT)))
self.client.request(
mox.StrContains(ENDPOINT_URL + '/resource'), 'GET',
headers=mox.ContainsKeyValue('X-Auth-Token', TOKEN)
).AndReturn((res200, ''))
self.mox.ReplayAll()
self.client.do_request('/resource', 'GET')
mock_request.assert_called_with(
ENDPOINT_URL + '/resource', 'GET',
headers=expected_headers)
mock_request.return_value = (resp_200, '')
self.client.do_request('/resource', 'GET',
headers=headers)
mock_request.assert_called_with(
ENDPOINT_URL + '/resource', 'GET',
headers=headers)
@mock.patch('tackerclient.client.HTTPClient.request')
def test_use_given_endpoint_url(self, mock_request):
def test_use_given_endpoint_url(self):
self.client = client.HTTPClient(
username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD,
auth_url=AUTH_URL, region_name=REGION,
endpoint_url=ENDPOINT_OVERRIDE)
self.assertEqual(self.client.endpoint_url, ENDPOINT_OVERRIDE)
self.client.auth_token = TOKEN
mock_request.return_value = (resp_200, '')
self.mox.StubOutWithMock(self.client, "request")
self.client.do_request('/resource', 'GET',
headers=headers)
mock_request.assert_called_with(
ENDPOINT_OVERRIDE + '/resource', 'GET',
headers=headers)
self.client.auth_token = TOKEN
res200 = get_response(200)
self.client.request(
mox.StrContains(ENDPOINT_OVERRIDE + '/resource'), 'GET',
headers=mox.ContainsKeyValue('X-Auth-Token', TOKEN)
).AndReturn((res200, ''))
self.mox.ReplayAll()
self.client.do_request('/resource', 'GET')
self.assertEqual(self.client.endpoint_url, ENDPOINT_OVERRIDE)
@mock.patch('tackerclient.client.HTTPClient.request')
def test_get_endpoint_url_other(self, mock_request):
def test_get_endpoint_url_other(self):
self.client = client.HTTPClient(
username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD,
auth_url=AUTH_URL, region_name=REGION, endpoint_type='otherURL')
self.mox.StubOutWithMock(self.client, "request")
self.client.auth_token = TOKEN
mock_request.return_value = (resp_200, json.dumps(ENDPOINTS_RESULT))
res200 = get_response(200)
self.client.request(
mox.StrContains(AUTH_URL + '/tokens/%s/endpoints' % TOKEN), 'GET',
headers=mox.IsA(dict)
).AndReturn((res200, json.dumps(ENDPOINTS_RESULT)))
self.mox.ReplayAll()
self.assertRaises(exceptions.EndpointTypeNotFound,
self.client.do_request,
'/resource',
'GET')
expected_url = AUTH_URL + '/tokens/%s/endpoints' % TOKEN
headers = {'User-Agent': 'python-tackerclient'}
mock_request.assert_called_with(expected_url, 'GET',
headers=headers)
@mock.patch('tackerclient.client.HTTPClient.request')
def test_get_endpoint_url_failed(self, mock_request):
def test_get_endpoint_url_failed(self):
self.mox.StubOutWithMock(self.client, "request")
self.client.auth_token = TOKEN
self.client.auth_url = AUTH_URL + '/tokens/%s/endpoints' % TOKEN
mock_request.return_value = (resp_401, '')
self.assertRaises(exceptions.Unauthorized,
self.client.do_request,
'/resource',
'GET')
res200 = get_response(200)
res401 = get_response(401)
self.client.request(
mox.StrContains(AUTH_URL + '/tokens/%s/endpoints' % TOKEN), 'GET',
headers=mox.IsA(dict)
).AndReturn((res401, ''))
self.client.request(
AUTH_URL + '/tokens', 'POST',
body=mox.IsA(str), headers=mox.IsA(dict)
).AndReturn((res200, json.dumps(KS_TOKEN_RESULT)))
self.client.request(
mox.StrContains(ENDPOINT_URL + '/resource'), 'GET',
headers=mox.ContainsKeyValue('X-Auth-Token', TOKEN)
).AndReturn((res200, ''))
self.mox.ReplayAll()
self.client.do_request('/resource', 'GET')
def test_endpoint_type(self):
resources = copy.deepcopy(KS_TOKEN_RESULT)
@@ -313,25 +342,32 @@ class CLITestAuthKeystone(testtools.TestCase):
self.client._extract_service_catalog,
resources)
@mock.patch('tackerclient.client.HTTPClient.request')
@mock.patch('tackerclient.common.utils.http_log_req')
def test_strip_credentials_from_log(self, mock_http_log_req,
mock_request,):
def test_strip_credentials_from_log(self):
def verify_no_credentials(kwargs):
return ('REDACTED' in kwargs['body']) and (
self.client.password not in kwargs['body'])
body = ('{"auth": {"tenantId": "testtenant_id",'
'"passwordCredentials": {"password": "password",'
'"userId": "testuser_id"}}}')
expected_body = ('{"auth": {"tenantId": "testtenant_id",'
'"REDACTEDCredentials": {"REDACTED": "REDACTED",'
'"userId": "testuser_id"}}}')
_headers = {'headers': expected_headers, 'body': expected_body}
def verify_credentials(body):
return 'REDACTED' not in body and self.client.password in body
mock_request.return_value = (resp_200, json.dumps(KS_TOKEN_RESULT))
self.client.do_request('/resource', 'GET', body=body)
self.mox.StubOutWithMock(self.client, "request")
self.mox.StubOutWithMock(utils, "http_log_req")
args, kwargs = mock_http_log_req.call_args
# Check that credentials are stripped while logging.
self.assertEqual(_headers, args[2])
res200 = get_response(200)
utils.http_log_req(mox.IgnoreArg(), mox.IgnoreArg(), mox.Func(
verify_no_credentials))
self.client.request(
mox.IsA(str), mox.IsA(str), body=mox.Func(verify_credentials),
headers=mox.IgnoreArg()
).AndReturn((res200, json.dumps(KS_TOKEN_RESULT)))
utils.http_log_req(mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg())
self.client.request(
mox.IsA(str), mox.IsA(str), headers=mox.IsA(dict)
).AndReturn((res200, ''))
self.mox.ReplayAll()
self.client.do_request('/resource', 'GET')
class CLITestAuthKeystoneWithId(CLITestAuthKeystone):

View File

@@ -17,10 +17,9 @@
import urllib
import contextlib
import cStringIO
import fixtures
import mock
import six
import six.moves.urllib.parse as urlparse
import mox
import sys
import testtools
@@ -28,8 +27,6 @@ from tackerclient.common import constants
from tackerclient.common import exceptions
from tackerclient import shell
from tackerclient.tacker import v1_0 as tackerV1_0
from tackerclient.tacker.v1_0 import TackerCommand
from tackerclient.tests.unit import test_utils
from tackerclient.v1_0 import client
API_VERSION = "1.0"
@@ -40,7 +37,7 @@ ENDURL = 'localurl'
@contextlib.contextmanager
def capture_std_streams():
fake_stdout, fake_stderr = six.StringIO(), six.StringIO()
fake_stdout, fake_stderr = cStringIO.StringIO(), cStringIO.StringIO()
stdout, stderr = sys.stdout, sys.stderr
try:
sys.stdout, sys.stderr = fake_stdout, fake_stderr
@@ -81,23 +78,13 @@ def end_url(path, query=None, format=FORMAT):
return query and _url_str + "?" + query or _url_str
class MyUrlComparator(object):
class MyUrlComparator(mox.Comparator):
def __init__(self, lhs, client):
self.lhs = lhs
self.client = client
def equals(self, rhs):
lhsp = urlparse.urlparse(self.lhs)
rhsp = urlparse.urlparse(rhs)
lhs_qs = urlparse.parse_qsl(lhsp.query)
rhs_qs = urlparse.parse_qsl(rhsp.query)
return (lhsp.scheme == rhsp.scheme and
lhsp.netloc == rhsp.netloc and
lhsp.path == rhsp.path and
len(lhs_qs) == len(rhs_qs) and
set(lhs_qs) == set(rhs_qs))
return str(self) == rhs
def __str__(self):
if self.client and self.client.format != FORMAT:
@@ -115,14 +102,8 @@ class MyUrlComparator(object):
def __repr__(self):
return str(self)
def __eq__(self, rhs):
return self.equals(rhs)
def __ne__(self, rhs):
return not self.__eq__(rhs)
class MyComparator(object):
class MyComparator(mox.Comparator):
def __init__(self, lhs, client):
self.lhs = lhs
self.client = client
@@ -130,7 +111,7 @@ class MyComparator(object):
def _com_dict(self, lhs, rhs):
if len(lhs) != len(rhs):
return False
for key, value in lhs.items():
for key, value in lhs.iteritems():
if key not in rhs:
return False
rhs_value = rhs[key]
@@ -173,12 +154,6 @@ class MyComparator(object):
return self.client.serialize(self.lhs)
return str(self.lhs)
def __eq__(self, rhs):
return self.equals(rhs)
def __ne__(self, rhs):
return not self.__eq__(rhs)
class CLITestV10Base(testtools.TestCase):
@@ -189,6 +164,14 @@ class CLITestV10Base(testtools.TestCase):
def _find_resourceid(self, client, resource, name_or_id):
return name_or_id
def _get_attr_metadata(self):
return self.metadata
client.Client.EXTED_PLURALS.update(constants.PLURALS)
client.Client.EXTED_PLURALS.update({'tags': 'tag'})
return {'plurals': client.Client.EXTED_PLURALS,
'xmlns': constants.XML_NS_V10,
constants.EXT_NS: {'prefix': 'http://xxxx.yy.com'}}
def setUp(self, plurals={}):
"""Prepare the test environment."""
super(CLITestV10Base, self).setUp()
@@ -198,6 +181,7 @@ class CLITestV10Base(testtools.TestCase):
'xmlns': constants.XML_NS_V10,
constants.EXT_NS: {'prefix':
'http://xxxx.yy.com'}}
self.mox = mox.Mox()
self.endurl = ENDURL
self.fake_stdout = FakeStdout()
self.useFixture(fixtures.MonkeyPatch('sys.stdout', self.fake_stdout))
@@ -207,17 +191,20 @@ class CLITestV10Base(testtools.TestCase):
self.useFixture(fixtures.MonkeyPatch(
'tackerclient.tacker.v1_0.find_resourceid_by_id',
self._find_resourceid))
self.useFixture(fixtures.MonkeyPatch(
'tackerclient.v1_0.client.Client.get_attr_metadata',
self._get_attr_metadata))
self.client = client.Client(token=TOKEN, endpoint_url=self.endurl)
@mock.patch.object(TackerCommand, 'get_client')
def _test_create_resource(self, resource, cmd,
name, myid, args,
position_names, position_values, mock_get,
tenant_id=None, get_client_called_count=1,
position_names, position_values, tenant_id=None,
tags=None, admin_state_up=True, extra_body=None,
**kwargs):
mock_get.return_value = self.client
non_admin_status_resources = ['vnfd', 'vnf', 'vim', 'vnffgd', 'vnffg']
self.mox.StubOutWithMock(cmd, "get_client")
self.mox.StubOutWithMock(self.client.httpclient, "request")
cmd.get_client().MultipleTimes().AndReturn(self.client)
non_admin_status_resources = ['vnfd', 'vnf', 'vim']
if (resource in non_admin_status_resources):
body = {resource: {}, }
else:
@@ -245,49 +232,53 @@ class CLITestV10Base(testtools.TestCase):
# Work around for LP #1217791. XML deserializer called from
# MyComparator does not decodes XML string correctly.
if self.format == 'json':
_body = MyComparator(body, self.client)
mox_body = MyComparator(body, self.client)
else:
_body = self.client.serialize(body)
with mock.patch.object(self.client.httpclient, 'request') as mock_req:
mock_req.return_value = (MyResp(200), resstr)
args.extend(['--request-format', self.format])
cmd_parser = cmd.get_parser('create_' + resource)
shell.run_command(cmd, cmd_parser, args)
mock_req.assert_called_once_with(
end_url(path, format=self.format), 'POST',
body=_body,
headers=test_utils.ContainsKeyValue('X-Auth-Token', TOKEN))
self.assertEqual(get_client_called_count, mock_get.call_count)
mox_body = self.client.serialize(body)
self.client.httpclient.request(
end_url(path, format=self.format), 'POST',
body=mox_body,
headers=mox.ContainsKeyValue(
'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr))
args.extend(['--request-format', self.format])
self.mox.ReplayAll()
cmd_parser = cmd.get_parser('create_' + resource)
shell.run_command(cmd, cmd_parser, args)
self.mox.VerifyAll()
self.mox.UnsetStubs()
_str = self.fake_stdout.make_string()
self.assertIn(myid, _str)
if name:
self.assertIn(name, _str)
@mock.patch.object(TackerCommand, 'get_client')
def _test_list_columns(self, cmd, resources_collection,
resources_out, mock_get,
args=['-f', 'json']):
mock_get.return_value = self.client
resources_out, args=['-f', 'json']):
self.mox.StubOutWithMock(cmd, "get_client")
self.mox.StubOutWithMock(self.client.httpclient, "request")
cmd.get_client().MultipleTimes().AndReturn(self.client)
self.client.format = self.format
resstr = self.client.serialize(resources_out)
path = getattr(self.client, resources_collection + "_path")
with mock.patch.object(self.client.httpclient, 'request') as mock_req:
mock_req.return_value = (MyResp(200), resstr)
args.extend(['--request-format', self.format])
cmd_parser = cmd.get_parser("list_" + resources_collection)
shell.run_command(cmd, cmd_parser, args)
mock_req.assert_called_once_with(
end_url(path, format=self.format), 'GET',
body=None,
headers=test_utils.ContainsKeyValue('X-Auth-Token', TOKEN))
mock_get.assert_called_once_with()
self.client.httpclient.request(
end_url(path, format=self.format), 'GET',
body=None,
headers=mox.ContainsKeyValue(
'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr))
args.extend(['--request-format', self.format])
self.mox.ReplayAll()
cmd_parser = cmd.get_parser("list_" + resources_collection)
shell.run_command(cmd, cmd_parser, args)
self.mox.VerifyAll()
self.mox.UnsetStubs()
def _test_list_resources(self, resources, cmd, detail=False, tags=[],
fields_1=[], fields_2=[], page_size=None,
sort_key=[], sort_dir=[], response_contents=None,
base_args=None, path=None,
template_source=None):
base_args=None, path=None):
self.mox.StubOutWithMock(cmd, "get_client")
self.mox.StubOutWithMock(self.client.httpclient, "request")
cmd.get_client().MultipleTimes().AndReturn(self.client)
if response_contents is None:
contents = [{self.id_field: 'myid1', },
{self.id_field: 'myid2', }, ]
@@ -306,17 +297,13 @@ class CLITestV10Base(testtools.TestCase):
for field in fields_1:
args.append('--fields')
args.append(field)
if template_source is not None:
args.append("--template-source")
args.append(template_source)
query += 'template_source=' + template_source
if tags:
args.append('--')
args.append("--tag")
for tag in tags:
args.append(tag)
if isinstance(tag, six.string_types):
if isinstance(tag, unicode):
tag = urllib.quote(tag.encode('utf-8'))
if query:
query += "&tag=" + tag
@@ -364,287 +351,147 @@ class CLITestV10Base(testtools.TestCase):
query += 'sort_dir=%s' % dir
if path is None:
path = getattr(self.client, resources + "_path")
with mock.patch.object(self.client.httpclient, 'request') as mock_req:
mock_req.return_value = (MyResp(200), resstr)
with mock.patch.object(TackerCommand, 'get_client') as mock_get:
mock_get.return_value = self.client
cmd_parser = cmd.get_parser("list_" + resources)
shell.run_command(cmd, cmd_parser, args)
mock_req.assert_called_once_with(
MyUrlComparator(end_url(path, query, format=self.format),
self.client),
'GET',
body=None,
headers=test_utils.ContainsKeyValue('X-Auth-Token', TOKEN))
self.client.httpclient.request(
MyUrlComparator(end_url(path, query, format=self.format),
self.client),
'GET',
body=None,
headers=mox.ContainsKeyValue(
'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr))
self.mox.ReplayAll()
cmd_parser = cmd.get_parser("list_" + resources)
shell.run_command(cmd, cmd_parser, args)
self.mox.VerifyAll()
self.mox.UnsetStubs()
_str = self.fake_stdout.make_string()
if response_contents is None:
self.assertIn('myid1', _str)
return _str
@mock.patch.object(TackerCommand, 'get_client')
def _test_list_sub_resources(self, resources, api_resource, cmd, myid,
mock_get, detail=False,
tags=[], fields_1=[], fields_2=[],
page_size=None, sort_key=[], sort_dir=[],
response_contents=None, base_args=None,
path=None):
mock_get.return_value = self.client
if response_contents is None:
contents = [{self.id_field: 'myid1', },
{self.id_field: 'myid2', }, ]
else:
contents = response_contents
reses = {api_resource: contents}
def _test_list_resources_with_pagination(self, resources, cmd):
self.mox.StubOutWithMock(cmd, "get_client")
self.mox.StubOutWithMock(self.client.httpclient, "request")
cmd.get_client().MultipleTimes().AndReturn(self.client)
path = getattr(self.client, resources + "_path")
fake_query = "marker=myid2&limit=2"
reses1 = {resources: [{'id': 'myid1', },
{'id': 'myid2', }],
'%s_links' % resources: [{'href': end_url(path, fake_query),
'rel': 'next'}]}
reses2 = {resources: [{'id': 'myid3', },
{'id': 'myid4', }]}
self.client.format = self.format
resstr = self.client.serialize(reses)
# url method body
query = ""
args = base_args if base_args is not None else []
if detail:
args.append('-D')
args.extend(['--request-format', self.format])
if fields_1:
for field in fields_1:
args.append('--fields')
args.append(field)
resstr1 = self.client.serialize(reses1)
resstr2 = self.client.serialize(reses2)
self.client.httpclient.request(
end_url(path, "", format=self.format), 'GET',
body=None,
headers=mox.ContainsKeyValue(
'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr1))
self.client.httpclient.request(
end_url(path, fake_query, format=self.format), 'GET',
body=None,
headers=mox.ContainsKeyValue(
'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr2))
self.mox.ReplayAll()
cmd_parser = cmd.get_parser("list_" + resources)
args = ['--request-format', self.format]
shell.run_command(cmd, cmd_parser, args)
self.mox.VerifyAll()
self.mox.UnsetStubs()
if tags:
args.append('--')
args.append("--tag")
for tag in tags:
args.append(tag)
if isinstance(tag, six.string_types):
tag = urllib.quote(tag.encode('utf-8'))
if query:
query += "&tag=" + tag
else:
query = "tag=" + tag
if (not tags) and fields_2:
args.append('--')
if fields_2:
args.append("--fields")
for field in fields_2:
args.append(field)
if detail:
query = query and query + '&verbose=True' or 'verbose=True'
fields_1.extend(fields_2)
for field in fields_1:
if query:
query += "&fields=" + field
else:
query = "fields=" + field
if page_size:
args.append("--page-size")
args.append(str(page_size))
if query:
query += "&limit=%s" % page_size
else:
query = "limit=%s" % page_size
if sort_key:
for key in sort_key:
args.append('--sort-key')
args.append(key)
if query:
query += '&'
query += 'sort_key=%s' % key
if sort_dir:
len_diff = len(sort_key) - len(sort_dir)
if len_diff > 0:
sort_dir += ['asc'] * len_diff
elif len_diff < 0:
sort_dir = sort_dir[:len(sort_key)]
for dir in sort_dir:
args.append('--sort-dir')
args.append(dir)
if query:
query += '&'
query += 'sort_dir=%s' % dir
if path is None:
path = getattr(self.client, resources + "_path")
with mock.patch.object(self.client.httpclient, 'request') as mock_req:
mock_req.return_value = (MyResp(200), resstr)
comparator = MyUrlComparator(
end_url(path % myid, query=query, format=self.format),
self.client)
args.extend(['--request-format', self.format])
cmd_parser = cmd.get_parser("list_" + resources)
shell.run_command(cmd, cmd_parser, args)
mock_req.assert_called_once_with(
comparator, 'GET',
body=None,
headers=test_utils.ContainsKeyValue('X-Auth-Token', TOKEN))
_str = self.fake_stdout.make_string()
if response_contents is None:
self.assertIn('myid1', _str)
return _str
# TODO(gongysh) add pagination unit test BUG 1633255
# def _test_list_sub_resources_with_pagination(
# self, resources, api_resource, cmd, myid):
# self.mox.StubOutWithMock(cmd, "get_client")
# self.mox.StubOutWithMock(self.client.httpclient, "request")
# cmd.get_client().MultipleTimes().AndReturn(self.client)
# path = getattr(self.client, resources + "_path")
# fake_query = "marker=myid2&limit=2"
# reses1 = {api_resource: [{'id': 'myid1', },
# {'id': 'myid2', }],
# '%s_links' % api_resource: [
# {'href': end_url(path % myid, fake_query),
# 'rel': 'next'}]
# }
# reses2 = {api_resource: [{'id': 'myid3', },
# {'id': 'myid4', }]}
# self.client.format = self.format
# resstr1 = self.client.serialize(reses1)
# resstr2 = self.client.serialize(reses2)
# self.client.httpclient.request(
# end_url(path % myid, "", format=self.format), 'GET',
# body=None,
# headers=mox.ContainsKeyValue(
# 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr1))
# self.client.httpclient.request(
# MyUrlComparator(end_url(path % myid, fake_query,
# format=self.format), self.client), 'GET',
# body=None, headers=mox.ContainsKeyValue(
# 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr2))
# self.mox.ReplayAll()
# cmd_parser = cmd.get_parser("list_" + resources)
# args = [myid, '--request-format', self.format]
# shell.run_command(cmd, cmd_parser, args)
# self.mox.VerifyAll()
# self.mox.UnsetStubs()
# def _test_list_resources_with_pagination(self, resources, cmd):
# self.mox.StubOutWithMock(cmd, "get_client")
# self.mox.StubOutWithMock(self.client.httpclient, "request")
# cmd.get_client().MultipleTimes().AndReturn(self.client)
# path = getattr(self.client, resources + "_path")
# fake_query = "marker=myid2&limit=2"
# reses1 = {resources: [{'id': 'myid1', },
# {'id': 'myid2', }],
# '%s_links' % resources: [
# {'href': end_url(path, fake_query),
# 'rel': 'next'}]}
# reses2 = {resources: [{'id': 'myid3', },
# {'id': 'myid4', }]}
# self.client.format = self.format
# resstr1 = self.client.serialize(reses1)
# resstr2 = self.client.serialize(reses2)
# self.client.httpclient.request(
# end_url(path, "", format=self.format), 'GET',
# body=None,
# headers=mox.ContainsKeyValue(
# 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr1))
# self.client.httpclient.request(
# MyUrlComparator(end_url(path, fake_query, format=self.format),
# self.client), 'GET', body=None,
# headers=mox.ContainsKeyValue(
# 'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr2))
# self.mox.ReplayAll()
# cmd_parser = cmd.get_parser("list_" + resources)
# args = ['--request-format', self.format]
# shell.run_command(cmd, cmd_parser, args)
# self.mox.VerifyAll()
# self.mox.UnsetStubs()
@mock.patch.object(TackerCommand, 'get_client')
def _test_update_resource(self, resource, cmd, myid, args, extrafields,
mock_get, get_client_called_count=1):
mock_get.return_value = self.client
def _test_update_resource(self, resource, cmd, myid, args, extrafields):
self.mox.StubOutWithMock(cmd, "get_client")
self.mox.StubOutWithMock(self.client.httpclient, "request")
cmd.get_client().MultipleTimes().AndReturn(self.client)
body = {resource: extrafields}
path = getattr(self.client, resource + "_path")
self.client.format = self.format
# Work around for LP #1217791. XML deserializer called from
# MyComparator does not decodes XML string correctly.
if self.format == 'json':
_body = MyComparator(body, self.client)
mox_body = MyComparator(body, self.client)
else:
_body = self.client.serialize(body)
with mock.patch.object(self.client.httpclient, 'request') as mock_req:
comparator = MyUrlComparator(
end_url(path % myid, format=self.format), self.client)
mock_req.return_value = (MyResp(204), None)
args.extend(['--request-format', self.format])
cmd_parser = cmd.get_parser("update_" + resource)
shell.run_command(cmd, cmd_parser, args)
mock_req.assert_called_once_with(
comparator,
'PUT',
body=_body,
headers=test_utils.ContainsKeyValue('X-Auth-Token', TOKEN))
self.assertEqual(get_client_called_count, mock_get.call_count)
mox_body = self.client.serialize(body)
self.client.httpclient.request(
MyUrlComparator(end_url(path % myid, format=self.format),
self.client),
'PUT',
body=mox_body,
headers=mox.ContainsKeyValue(
'X-Auth-Token', TOKEN)).AndReturn((MyResp(204), None))
args.extend(['--request-format', self.format])
self.mox.ReplayAll()
cmd_parser = cmd.get_parser("update_" + resource)
shell.run_command(cmd, cmd_parser, args)
self.mox.VerifyAll()
self.mox.UnsetStubs()
_str = self.fake_stdout.make_string()
self.assertIn(myid, _str)
def _test_show_resource(self, resource, cmd, myid, args, fields=[]):
with mock.patch.object(cmd, 'get_client') as mock_get:
mock_get.return_value = self.client
query = "&".join(["fields=%s" % field for field in fields])
expected_res = {resource:
{self.id_field: myid,
'name': 'myname', }, }
self.client.format = self.format
resstr = self.client.serialize(expected_res)
path = getattr(self.client, resource + "_path")
with mock.patch.object(self.client.httpclient, 'request') as\
mock_req:
mock_req.return_value = (MyResp(200), resstr)
args.extend(['--request-format', self.format])
cmd_parser = cmd.get_parser("show_" + resource)
shell.run_command(cmd, cmd_parser, args)
mock_req.assert_called_once_with(
end_url(path % myid, query, format=self.format), 'GET',
body=None,
headers=test_utils.ContainsKeyValue('X-Auth-Token', TOKEN))
_str = self.fake_stdout.make_string()
mock_get.assert_called_once_with()
self.assertIn(myid, _str)
self.assertIn('myname', _str)
@mock.patch.object(TackerCommand, 'get_client')
def _test_delete_resource(self, resource, cmd, myid, args, mock_get):
deleted_msg = {'vnf': 'delete initiated'}
mock_get.return_value = self.client
self.mox.StubOutWithMock(cmd, "get_client")
self.mox.StubOutWithMock(self.client.httpclient, "request")
cmd.get_client().MultipleTimes().AndReturn(self.client)
query = "&".join(["fields=%s" % field for field in fields])
expected_res = {resource:
{self.id_field: myid,
'name': 'myname', }, }
self.client.format = self.format
resstr = self.client.serialize(expected_res)
path = getattr(self.client, resource + "_path")
with mock.patch.object(self.client.httpclient, 'request') as mock_req:
mock_req.return_value = (MyResp(204), None)
args.extend(['--request-format', self.format])
cmd_parser = cmd.get_parser("delete_" + resource)
shell.run_command(cmd, cmd_parser, args)
if '--force' in args:
body_str = '{"' + resource + \
'": {"attributes": {"force": true}}}'
mock_req.assert_called_once_with(
end_url(path % myid, format=self.format), 'DELETE',
body=body_str,
headers=test_utils.ContainsKeyValue('X-Auth-Token', TOKEN))
else:
mock_req.assert_called_once_with(
end_url(path % myid, format=self.format), 'DELETE',
body=None,
headers=test_utils.ContainsKeyValue('X-Auth-Token', TOKEN))
mock_get.assert_called_once_with()
self.client.httpclient.request(
end_url(path % myid, query, format=self.format), 'GET',
body=None,
headers=mox.ContainsKeyValue(
'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr))
args.extend(['--request-format', self.format])
self.mox.ReplayAll()
cmd_parser = cmd.get_parser("show_" + resource)
shell.run_command(cmd, cmd_parser, args)
self.mox.VerifyAll()
self.mox.UnsetStubs()
_str = self.fake_stdout.make_string()
msg = 'All specified %(resource)s(s) %(msg)s successfully\n' % {
'msg': deleted_msg.get(resource, 'deleted'),
'resource': resource}
self.assertEqual(msg, _str)
self.assertIn(myid, _str)
self.assertIn('myname', _str)
def _test_delete_resource(self, resource, cmd, myid, args):
self.mox.StubOutWithMock(cmd, "get_client")
self.mox.StubOutWithMock(self.client.httpclient, "request")
cmd.get_client().MultipleTimes().AndReturn(self.client)
path = getattr(self.client, resource + "_path")
self.client.httpclient.request(
end_url(path % myid, format=self.format), 'DELETE',
body=None,
headers=mox.ContainsKeyValue(
'X-Auth-Token', TOKEN)).AndReturn((MyResp(204), None))
args.extend(['--request-format', self.format])
self.mox.ReplayAll()
cmd_parser = cmd.get_parser("delete_" + resource)
shell.run_command(cmd, cmd_parser, args)
self.mox.VerifyAll()
self.mox.UnsetStubs()
_str = self.fake_stdout.make_string()
self.assertIn(myid, _str)
@mock.patch.object(TackerCommand, 'get_client')
def _test_update_resource_action(self, resource, cmd, myid, action, args,
body, mock_get, retval=None):
mock_get.return_value = self.client
body, retval=None):
self.mox.StubOutWithMock(cmd, "get_client")
self.mox.StubOutWithMock(self.client.httpclient, "request")
cmd.get_client().MultipleTimes().AndReturn(self.client)
path = getattr(self.client, resource + "_path")
path_action = '%s/%s' % (myid, action)
with mock.patch.object(self.client.httpclient, 'request') as mock_req:
mock_req.return_value = (MyResp(204), retval)
args.extend(['--request-format', self.format])
cmd_parser = cmd.get_parser("delete_" + resource)
shell.run_command(cmd, cmd_parser, args)
mock_req.assert_called_once_with(
end_url(path % path_action, format=self.format), 'PUT',
body=MyComparator(body, self.client),
headers=test_utils.ContainsKeyValue('X-Auth-Token', TOKEN))
self.client.httpclient.request(
end_url(path % path_action, format=self.format), 'PUT',
body=MyComparator(body, self.client),
headers=mox.ContainsKeyValue(
'X-Auth-Token', TOKEN)).AndReturn((MyResp(204), retval))
args.extend(['--request-format', self.format])
self.mox.ReplayAll()
cmd_parser = cmd.get_parser("delete_" + resource)
shell.run_command(cmd, cmd_parser, args)
self.mox.VerifyAll()
self.mox.UnsetStubs()
_str = self.fake_stdout.make_string()
self.assertIn(myid, _str)
@@ -652,48 +499,70 @@ class CLITestV10Base(testtools.TestCase):
class ClientV1TestJson(CLITestV10Base):
def test_do_request_unicode(self):
self.client.format = self.format
self.mox.StubOutWithMock(self.client.httpclient, "request")
unicode_text = u'\u7f51\u7edc'
# url with unicode
action = u'/test'
expected_action = action.encode('utf-8')
# query string with unicode
params = {'test': unicode_text}
expect_query = urllib.urlencode({'test':
unicode_text.encode('utf-8')})
# request body with unicode
body = params
expect_body = self.client.serialize(body)
# headers with unicode
self.client.httpclient.auth_token = unicode_text
with mock.patch.object(self.client.httpclient, 'request') as mock_req:
mock_req.return_value = (MyResp(200), expect_body)
res_body = self.client.do_request('PUT', action, body=body,
params=params)
expected_uri = u'localurl/v1.0/test.json?test=%E7%BD%91%E7%BB%9C'
mock_req.assert_called_with(
expected_uri, 'PUT', body=expect_body,
headers={'X-Auth-Token': unicode_text,
'User-Agent': 'python-tackerclient'})
expected_auth_token = unicode_text.encode('utf-8')
self.client.httpclient.request(
end_url(expected_action, query=expect_query, format=self.format),
'PUT', body=expect_body,
headers=mox.ContainsKeyValue(
'X-Auth-Token',
expected_auth_token)).AndReturn((MyResp(200), expect_body))
self.mox.ReplayAll()
res_body = self.client.do_request('PUT', action, body=body,
params=params)
self.mox.VerifyAll()
self.mox.UnsetStubs()
# test response with unicode
self.assertEqual(res_body, body)
def test_do_request_error_without_response_body(self):
self.client.format = self.format
self.mox.StubOutWithMock(self.client.httpclient, "request")
params = {'test': 'value'}
expect_query = urlparse.urlencode(params)
expect_query = urllib.urlencode(params)
self.client.httpclient.auth_token = 'token'
with mock.patch.object(self.client.httpclient, 'request') as mock_req:
mock_req.return_value = (MyResp(400, reason='An error'), '')
self.client.httpclient.request(
end_url('/test', query=expect_query, format=self.format),
'PUT', body='',
headers={'X-Auth-Token': 'token'}
)
error = self.assertRaises(exceptions.TackerClientException,
self.client.do_request, 'PUT', '/test',
body='', params=params)
self.assertEqual("An error", str(error))
self.client.httpclient.request(
end_url('/test', query=expect_query, format=self.format),
'PUT', body='',
headers=mox.ContainsKeyValue('X-Auth-Token', 'token')
).AndReturn((MyResp(400, reason='An error'), ''))
self.mox.ReplayAll()
error = self.assertRaises(exceptions.TackerClientException,
self.client.do_request, 'PUT', '/test',
body='', params=params)
self.assertEqual("An error", str(error))
self.mox.VerifyAll()
self.mox.UnsetStubs()
class ClientV1UnicodeTestXML(ClientV1TestJson):
format = 'xml'
class CLITestV10ExceptionHandler(CLITestV10Base):
def _test_exception_handler_v10(
self, expected_exception, status_code, expected_msg,
error_type=None, error_msg=None, error_detail=None,
error_content=None):
self, expected_exception, status_code, expected_msg,
error_type=None, error_msg=None, error_detail=None,
error_content=None):
if error_content is None:
error_content = {'TackerError': {'type': error_type,
'message': error_msg,
@@ -703,8 +572,6 @@ class CLITestV10ExceptionHandler(CLITestV10Base):
client.exception_handler_v10,
status_code, error_content)
self.assertEqual(status_code, e.status_code)
self.assertEqual(expected_exception.__name__,
e.__class__.__name__)
if expected_msg is None:
if error_detail:

View File

@@ -1,4 +1,6 @@
# Copyright 2013 Intel Corporation
# Copyright 2013 Intel
# Copyright 2013 Isaku Yamahata <isaku.yamahata at intel com>
# <isaku.yamahata at gmail com>
# All Rights Reserved.
#
#

View File

@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import mock
import mox
import testtools
from tackerclient.client import HTTPClient
@@ -25,47 +25,60 @@ AUTH_TOKEN = 'test_token'
END_URL = 'test_url'
METHOD = 'GET'
URL = 'http://test.test:1234/v1.0/test'
headers = {'User-Agent': 'python-tackerclient'}
class TestHTTPClient(testtools.TestCase):
def setUp(self):
super(TestHTTPClient, self).setUp()
self.addCleanup(mock.patch.stopall)
self.mox = mox.Mox()
self.mox.StubOutWithMock(HTTPClient, 'request')
self.addCleanup(self.mox.UnsetStubs)
self.http = HTTPClient(token=AUTH_TOKEN, endpoint_url=END_URL)
@mock.patch('tackerclient.client.HTTPClient.request')
def test_request_error(self, mock_request):
def test_request_error(self):
HTTPClient.request(
URL, METHOD, headers=mox.IgnoreArg()
).AndRaise(Exception('error msg'))
self.mox.ReplayAll()
mock_request.side_effect = Exception('error msg')
self.assertRaises(
exceptions.ConnectionFailed,
self.http._cs_request,
URL, METHOD
)
self.mox.VerifyAll()
@mock.patch('tackerclient.client.HTTPClient.request')
def test_request_success(self, mock_request):
def test_request_success(self):
rv_should_be = MyResp(200), 'test content'
mock_request.return_value = rv_should_be
HTTPClient.request(
URL, METHOD, headers=mox.IgnoreArg()
).AndReturn(rv_should_be)
self.mox.ReplayAll()
self.assertEqual(rv_should_be, self.http._cs_request(URL, METHOD))
self.mox.VerifyAll()
@mock.patch('tackerclient.client.HTTPClient.request')
def test_request_unauthorized(self, mock_request):
mock_request.return_value = MyResp(401), 'unauthorized message'
def test_request_unauthorized(self):
rv_should_be = MyResp(401), 'unauthorized message'
HTTPClient.request(
URL, METHOD, headers=mox.IgnoreArg()
).AndReturn(rv_should_be)
self.mox.ReplayAll()
e = self.assertRaises(exceptions.Unauthorized,
self.http._cs_request, URL, METHOD)
self.assertEqual('unauthorized message', str(e))
mock_request.assert_called_with(URL, METHOD, headers=headers)
@mock.patch('tackerclient.client.HTTPClient.request')
def test_request_forbidden_is_returned_to_caller(self, mock_request):
self.assertEqual('unauthorized message', e.message)
self.mox.VerifyAll()
def test_request_forbidden_is_returned_to_caller(self):
rv_should_be = MyResp(403), 'forbidden message'
mock_request.return_value = rv_should_be
HTTPClient.request(
URL, METHOD, headers=mox.IgnoreArg()
).AndReturn(rv_should_be)
self.mox.ReplayAll()
self.assertEqual(rv_should_be, self.http._cs_request(URL, METHOD))
self.mox.VerifyAll()

View File

@@ -14,15 +14,14 @@
# under the License.
import argparse
import cStringIO
import logging
import os
import re
import six
import sys
import fixtures
from keystoneclient import session
import mock
import mox
import testtools
from testtools import matchers
@@ -36,8 +35,7 @@ DEFAULT_TENANT_ID = 'tenant_id'
DEFAULT_TENANT_NAME = 'tenant_name'
DEFAULT_AUTH_URL = 'http://127.0.0.1:5000/v1.0/'
DEFAULT_TOKEN = '3bcc3d3a03f44e3d8377f9247b0ad155'
DEFAULT_URL = 'http://tacker.example.org:9890/'
DEFAULT_API_VERSION = '1.0'
DEFAULT_URL = 'http://tacker.example.org:8888/'
class ShellTest(testtools.TestCase):
@@ -52,6 +50,7 @@ class ShellTest(testtools.TestCase):
# Patch os.environ to avoid required auth info.
def setUp(self):
super(ShellTest, self).setUp()
self.mox = mox.Mox()
for var in self.FAKE_ENV:
self.useFixture(
fixtures.EnvironmentVariable(
@@ -62,9 +61,9 @@ class ShellTest(testtools.TestCase):
clean_env = {}
_old_env, os.environ = os.environ, clean_env.copy()
try:
sys.stdout = six.StringIO()
sys.stderr = six.StringIO()
_shell = openstack_shell.TackerShell(DEFAULT_API_VERSION)
sys.stdout = cStringIO.StringIO()
sys.stderr = cStringIO.StringIO()
_shell = openstack_shell.TackerShell('1.0')
_shell.run(argstr.split())
except SystemExit:
exc_type, exc_value, exc_traceback = sys.exc_info()
@@ -104,7 +103,7 @@ class ShellTest(testtools.TestCase):
def test_help_command(self):
required = 'usage:'
help_text, stderr = self.shell('help vnfd-create')
help_text, stderr = self.shell('help network-create')
self.assertThat(
help_text,
matchers.MatchesRegex(required))
@@ -115,27 +114,16 @@ class ShellTest(testtools.TestCase):
stdout, stderr = self.shell('--os-auth-strategy fake '
'vnfd-list')
self.assertFalse(stdout)
self.assertEqual('You must provide a service URL via '
'either --os-url or env[OS_URL]', stderr.strip())
def test_auth(self):
with mock.patch.object(openstack_shell.TackerShell,
'run_subcommand'), \
mock.patch.object(session, 'Session'), \
mock.patch.object(clientmanager, 'ClientManager') as mock_cmgr:
shell = openstack_shell.TackerShell(DEFAULT_API_VERSION)
shell.options = mock.Mock()
auth_session = shell._get_keystone_session()
cmdline = ('--os-username test '
'--os-password test '
'--os-tenant-name test '
'--os-auth-url http://127.0.0.1:5000/ '
'--os-auth-strategy keystone vnfd-list')
shell.authenticate_user()
shell.run(cmdline.split())
mock_cmgr.assert_called_with(
raise_errors=False, retries=0, timeout=None,
# import pdb; pdb.set_trace()
tacker_shell = openstack_shell.TackerShell('1.0')
self.addCleanup(self.mox.UnsetStubs)
self.mox.StubOutWithMock(clientmanager.ClientManager, '__init__')
self.mox.StubOutWithMock(tacker_shell, 'run_subcommand')
clientmanager.ClientManager.__init__(
token='', url='', auth_url='http://127.0.0.1:5000/',
tenant_name='test', tenant_id='tenant_id',
username='test', user_id='',
@@ -144,26 +132,38 @@ class ShellTest(testtools.TestCase):
auth_strategy='keystone',
service_type='nfv-orchestration',
endpoint_type='publicURL', insecure=False, ca_cert=None,
log_credentials=True, session=auth_session, auth=auth_session.auth)
log_credentials=True)
tacker_shell.run_subcommand(['vnfd-list'])
self.mox.ReplayAll()
cmdline = ('--os-username test '
'--os-password test '
'--os-tenant-name test '
'--os-auth-url http://127.0.0.1:5000/ '
'--os-auth-strategy keystone vnfd-list')
tacker_shell.run(cmdline.split())
self.mox.VerifyAll()
def test_build_option_parser(self):
tacker_shell = openstack_shell.TackerShell(DEFAULT_API_VERSION)
result = tacker_shell.build_option_parser('descr', DEFAULT_API_VERSION)
self.assertIsInstance(result, argparse.ArgumentParser)
tacker_shell = openstack_shell.TackerShell('1.0')
result = tacker_shell.build_option_parser('descr', '1.0')
self.assertEqual(True, isinstance(result, argparse.ArgumentParser))
@mock.patch.object(openstack_shell.TackerShell, 'run')
def test_main_with_unicode(self, mock_run):
mock_run.return_value = 0
def test_main_with_unicode(self):
self.mox.StubOutClassWithMocks(openstack_shell, 'TackerShell')
qshell_mock = openstack_shell.TackerShell('1.0')
unicode_text = u'\u7f51\u7edc'
argv = ['net-list', unicode_text, unicode_text.encode('utf-8')]
qshell_mock.run([u'net-list', unicode_text,
unicode_text]).AndReturn(0)
self.mox.ReplayAll()
ret = openstack_shell.main(argv=argv)
mock_run.assert_called_once_with([u'net-list', unicode_text,
unicode_text])
self.assertEqual(0, ret)
self.mox.VerifyAll()
self.mox.UnsetStubs()
self.assertEqual(ret, 0)
def test_endpoint_option(self):
shell = openstack_shell.TackerShell(DEFAULT_API_VERSION)
parser = shell.build_option_parser('descr', DEFAULT_API_VERSION)
shell = openstack_shell.TackerShell('1.0')
parser = shell.build_option_parser('descr', '1.0')
# Neither $OS_ENDPOINT_TYPE nor --endpoint-type
namespace = parser.parse_args([])
@@ -178,8 +178,8 @@ class ShellTest(testtools.TestCase):
"public")
self.useFixture(fixture)
shell = openstack_shell.TackerShell(DEFAULT_API_VERSION)
parser = shell.build_option_parser('descr', DEFAULT_API_VERSION)
shell = openstack_shell.TackerShell('1.0')
parser = shell.build_option_parser('descr', '1.0')
# $OS_ENDPOINT_TYPE but not --endpoint-type
namespace = parser.parse_args([])

View File

@@ -14,13 +14,12 @@
# under the License.
import fixtures
from keystoneclient import session
import mock
import mox
import requests
import testtools
from tackerclient import client
from tackerclient.common import clientmanager
from tackerclient.client import HTTPClient
from tackerclient.common.clientmanager import ClientManager
from tackerclient.common import exceptions
from tackerclient import shell as openstack_shell
@@ -30,7 +29,6 @@ END_URL = 'test_url'
METHOD = 'GET'
URL = 'http://test.test:1234/v1.0/'
CA_CERT = '/tmp/test/path'
DEFAULT_API_VERSION = '1.0'
class TestSSL(testtools.TestCase):
@@ -39,44 +37,106 @@ class TestSSL(testtools.TestCase):
self.useFixture(fixtures.EnvironmentVariable('OS_TOKEN', AUTH_TOKEN))
self.useFixture(fixtures.EnvironmentVariable('OS_URL', END_URL))
self.addCleanup(mock.patch.stopall)
def _test_verify_client_manager(self, cacert):
with mock.patch.object(session, 'Session'), \
mock.patch.object(clientmanager, 'ClientManager') as mock_cmgr:
mock_cmgr.return_value = 0
shell = openstack_shell.TackerShell(DEFAULT_API_VERSION)
shell.options = mock.Mock()
auth_session = shell._get_keystone_session()
shell.run(cacert)
mock_cmgr.assert_called_with(
api_version={'nfv-orchestration': '1.0'},
auth=auth_session.auth, auth_strategy='keystone',
auth_url='', ca_cert=CA_CERT, endpoint_type='publicURL',
insecure=False, log_credentials=True, password='',
raise_errors=False, region_name='', retries=0,
service_type='nfv-orchestration', session=auth_session,
tenant_id='', tenant_name='', timeout=None,
token='test_token', url='test_url', user_id='', username='')
self.mox = mox.Mox()
self.addCleanup(self.mox.UnsetStubs)
def test_ca_cert_passed(self):
cacert = ['--os-cacert', CA_CERT]
self._test_verify_client_manager(cacert)
self.mox.StubOutWithMock(ClientManager, '__init__')
self.mox.StubOutWithMock(openstack_shell.TackerShell, 'interact')
ClientManager.__init__(
ca_cert=CA_CERT,
# we are not really interested in other args
api_version=mox.IgnoreArg(),
auth_strategy=mox.IgnoreArg(),
auth_url=mox.IgnoreArg(),
service_type=mox.IgnoreArg(),
endpoint_type=mox.IgnoreArg(),
insecure=mox.IgnoreArg(),
password=mox.IgnoreArg(),
region_name=mox.IgnoreArg(),
tenant_id=mox.IgnoreArg(),
tenant_name=mox.IgnoreArg(),
token=mox.IgnoreArg(),
url=mox.IgnoreArg(),
username=mox.IgnoreArg(),
user_id=mox.IgnoreArg(),
log_credentials=mox.IgnoreArg(),
)
openstack_shell.TackerShell.interact().AndReturn(0)
self.mox.ReplayAll()
openstack_shell.TackerShell('1.0').run(['--os-cacert', CA_CERT])
self.mox.VerifyAll()
def test_ca_cert_passed_as_env_var(self):
self.useFixture(fixtures.EnvironmentVariable('OS_CACERT', CA_CERT))
self._test_verify_client_manager([])
@mock.patch.object(client.HTTPClient, 'request')
def test_proper_exception_is_raised_when_cert_validation_fails(self,
mock_req):
http = client.HTTPClient(token=AUTH_TOKEN, endpoint_url=END_URL)
mock_req.side_effect = requests.exceptions.SSLError()
self.mox.StubOutWithMock(ClientManager, '__init__')
self.mox.StubOutWithMock(openstack_shell.TackerShell, 'interact')
ClientManager.__init__(
ca_cert=CA_CERT,
# we are not really interested in other args
api_version=mox.IgnoreArg(),
auth_strategy=mox.IgnoreArg(),
auth_url=mox.IgnoreArg(),
service_type=mox.IgnoreArg(),
endpoint_type=mox.IgnoreArg(),
insecure=mox.IgnoreArg(),
password=mox.IgnoreArg(),
region_name=mox.IgnoreArg(),
tenant_id=mox.IgnoreArg(),
tenant_name=mox.IgnoreArg(),
token=mox.IgnoreArg(),
url=mox.IgnoreArg(),
username=mox.IgnoreArg(),
user_id=mox.IgnoreArg(),
log_credentials=mox.IgnoreArg(),
)
openstack_shell.TackerShell.interact().AndReturn(0)
self.mox.ReplayAll()
openstack_shell.TackerShell('1.0').run([])
self.mox.VerifyAll()
def test_client_manager_properly_creates_httpclient_instance(self):
self.mox.StubOutWithMock(HTTPClient, '__init__')
HTTPClient.__init__(
ca_cert=CA_CERT,
# we are not really interested in other args
auth_strategy=mox.IgnoreArg(),
auth_url=mox.IgnoreArg(),
endpoint_url=mox.IgnoreArg(),
insecure=mox.IgnoreArg(),
password=mox.IgnoreArg(),
region_name=mox.IgnoreArg(),
tenant_name=mox.IgnoreArg(),
token=mox.IgnoreArg(),
username=mox.IgnoreArg(),
)
self.mox.ReplayAll()
version = {'nfv-orchestration': '1.0'}
ClientManager(ca_cert=CA_CERT,
api_version=version,
url=END_URL,
token=AUTH_TOKEN).tacker
self.mox.VerifyAll()
def test_proper_exception_is_raised_when_cert_validation_fails(self):
http = HTTPClient(token=AUTH_TOKEN, endpoint_url=END_URL)
self.mox.StubOutWithMock(HTTPClient, 'request')
HTTPClient.request(
URL, METHOD, headers=mox.IgnoreArg()
).AndRaise(requests.exceptions.SSLError)
self.mox.ReplayAll()
self.assertRaises(
exceptions.SslCertificateValidationError,
http._cs_request,
URL, METHOD
)
self.mox.VerifyAll()

View File

@@ -13,6 +13,9 @@
# License for the specific language governing permissions and limitations
# under the License.
import datetime
import sys
import testtools
from tackerclient.common import exceptions
@@ -103,47 +106,92 @@ class TestUtils(testtools.TestCase):
self.assertEqual(('test_name', 'test_id', 'test', 'pass'), act)
class JSONUtilsTestCase(testtools.TestCase):
def test_dumps(self):
self.assertEqual(utils.dumps({'a': 'b'}), '{"a": "b"}')
def test_dumps_dict_with_date_value(self):
x = datetime.datetime(1920, 2, 3, 4, 5, 6, 7)
res = utils.dumps({1: 'a', 2: x})
expected = '{"1": "a", "2": "1920-02-03 04:05:06.000007"}'
self.assertEqual(expected, res)
def test_dumps_dict_with_spaces(self):
x = datetime.datetime(1920, 2, 3, 4, 5, 6, 7)
res = utils.dumps({1: 'a ', 2: x})
expected = '{"1": "a ", "2": "1920-02-03 04:05:06.000007"}'
self.assertEqual(expected, res)
def test_loads(self):
self.assertEqual(utils.loads('{"a": "b"}'), {'a': 'b'})
class ToPrimitiveTestCase(testtools.TestCase):
def test_list(self):
self.assertEqual(utils.to_primitive([1, 2, 3]), [1, 2, 3])
def test_empty_list(self):
self.assertEqual(utils.to_primitive([]), [])
def test_tuple(self):
self.assertEqual(utils.to_primitive((1, 2, 3)), [1, 2, 3])
def test_empty_tuple(self):
self.assertEqual(utils.to_primitive(()), [])
def test_dict(self):
self.assertEqual(
utils.to_primitive(dict(a=1, b=2, c=3)),
dict(a=1, b=2, c=3))
def test_empty_dict(self):
self.assertEqual(utils.to_primitive({}), {})
def test_datetime(self):
x = datetime.datetime(1920, 2, 3, 4, 5, 6, 7)
self.assertEqual(
utils.to_primitive(x),
'1920-02-03 04:05:06.000007')
def test_iter(self):
x = range(1, 6)
self.assertEqual(utils.to_primitive(x), [1, 2, 3, 4, 5])
def test_iteritems(self):
d = {'a': 1, 'b': 2, 'c': 3}
class IterItemsClass(object):
def iteritems(self):
return d.iteritems()
x = IterItemsClass()
p = utils.to_primitive(x)
self.assertEqual(p, {'a': 1, 'b': 2, 'c': 3})
def test_nasties(self):
def foo():
pass
x = [datetime, foo, dir]
ret = utils.to_primitive(x)
self.assertEqual(len(ret), 3)
def test_to_primitive_dict_with_date_value(self):
x = datetime.datetime(1920, 2, 3, 4, 5, 6, 7)
res = utils.to_primitive({'a': x})
self.assertEqual({'a': '1920-02-03 04:05:06.000007'}, res)
class ImportClassTestCase(testtools.TestCase):
def test_import_class(self):
dt = utils.import_class('datetime.datetime')
self.assertTrue(sys.modules['datetime'].datetime is dt)
def test_import_bad_class(self):
self.assertRaises(
ImportError, utils.import_class,
'lol.u_mad.brah')
def test_get_client_class_invalid_version(self):
self.assertRaises(
exceptions.UnsupportedVersion,
utils.get_client_class, 'image', '2', {'image': '2'})
class ContainsKeyValue(object):
"""Checks whether a key/value pair is in a dict parameter.
The ContainsKeyValue class is a helper for mock.assert_*()
method. It enables strict check than the built in mock.ANY
helper, and is the equivalent of the mox.ContainsKeyValue()
function from the legacy mox library
Example usage could be:
mock_some_method.assert_called_once_with(
"hello",
ContainsKeyValue('foo', bar),
mock.ANY,
"world",
ContainsKeyValue('hello', world))
"""
def __init__(self, wantkey, wantvalue):
self.wantkey = wantkey
self.wantvalue = wantvalue
def __eq__(self, other):
try:
return other[self.wantkey] == self.wantvalue
except (KeyError, TypeError):
return False
def __ne__(self, other):
try:
return other[self.wantkey] != self.wantvalue
except (KeyError, TypeError):
return True
def __repr__(self):
return "<ContainsKeyValue: key " + str(self.wantkey) + \
" and value " + str(self.wantvalue) + ">"

View File

@@ -1,7 +0,0 @@
auth_url: 'http://1.2.3.4:5000'
username: 'xyz'
password: '12345'
project_name: 'abc'
project_domain_name: 'prj_domain_name'
user_domain_name: 'user_domain_name'
type: 'openstack'

View File

@@ -1,8 +0,0 @@
auth_url: 'http://1.2.3.4:5000'
username: 'xyz'
password: '12345'
project_name: 'abc'
project_domain_name: 'prj_domain_name'
user_domain_name: 'user_domain_name'
cert_verify: 'False'
type: 'openstack'

Some files were not shown because too many files have changed in this diff Show More