Compare commits

..

49 Commits

Author SHA1 Message Date
Zuul
8509a590aa Merge "Show classifier's name" 2018-01-31 15:16:26 +00:00
Dimitrios Markou
99fc9b7bda Show classifier's name
In continuation of this patch [0] we are adding the functionality
of showing the names of the existing classifiers when the classifier-list
command is getting executed.

[0]: https://review.openstack.org/#/c/532203/

Depends-On: Ia586061c578a99662d49ad284d1ff313c350e1f3

Change-Id: If6d76a66ab0d37662d1b28fe1786b6e0ffc8171d
Signed-off-by: Dimitrios Markou <mardim@intracom-telecom.com>
2018-01-31 16:37:42 +02:00
OpenStack Proposal Bot
b9c53e664a Updated from global requirements
Change-Id: Icb596d201b2d15924cb794665408fc72539d432c
2018-01-24 01:33:04 +00:00
Zuul
ed0cdb5460 Merge "Complete VIM osc commands" 2018-01-23 07:26:30 +00:00
gongysh
573de86f7f Complete VIM osc commands
Change-Id: I3cb21b0583020db8a91789e2168d2a51d45d0cf4
Implements: blueprint tacker-support-python-openstackclient
2018-01-23 08:44:12 +08:00
Zuul
8d0e9825ae Merge "fix misspell" 2018-01-22 07:41:56 +00:00
OpenStack Proposal Bot
b472a2d50e Updated from global requirements
Change-Id: I01fb34fe999fb9a694b24afbc5211a69a43102c7
2018-01-18 03:31:52 +00:00
sunqingliang6
1c3cdf58ec fix misspell
Change-Id: I20f48f4cd7b4ed301d6c9df81aa66b1b126105f5
2018-01-17 15:53:57 +08:00
OpenStack Proposal Bot
8ce414c612 Updated from global requirements
Change-Id: I7c3b9efd16d2a6717ec4c9197a907c948f6a781e
2018-01-16 03:50:17 +00:00
Cong Phuoc Hoang
b6ef835d89 Add "cert_verify" in vim_config file to support insecure VIM
Currently, Tacker can not communicate with VIM endpoints that
use SSL. This patch will add cert_verify parameter, user can
set "cert_verify" to False (default value is True) to disable
verifying SSL.

Change-Id: I0af2a0f91ecda2a63cf6233d780e1dd7c064513c
2018-01-13 09:35:13 +00:00
Zuul
4a772e74d8 Merge "Base OpenStackClient(OSC) plugin support" 2018-01-08 07:46:11 +00:00
Cong Phuoc Hoang
e026ae17e5 Add "--vnffgd-template" to vnffg-update command
This patch will add "--vnffgd-template" to vnffg-update command to support
update VNFFG from file. User can run:
    tacker vnffg-update --vnffgd-template <vnffgd-file-name> vnffg_name
to update the existing VNFFG.

Partially Implements: blueprint update-vnffg

Change-Id: Ief0469d870530693798f798ecd2f52782318795b
2018-01-06 11:39:06 +09:00
yong sheng gong
0fcd6c31c3 Base OpenStackClient(OSC) plugin support
This patch enables tackerclient OSC plugin support.

Co-Authored-By: yong sheng gong <gong.yongsheng@99cloud.net>
Co-Authored-By: Trinath Somanchi <trinath.somanchi@nxp.com>

This adds the first list VIM command.

Change-Id: I75d430b00861ee9020d346cfb8ac8b736d36f47d
Implements: blueprint tacker-support-python-openstackclient
2018-01-05 19:11:02 +08:00
Zuul
b7075c91d7 Merge "Add doc migration framework" 2017-12-22 08:15:24 +00:00
yong sheng gong
09bf59d764 Add doc migration framework
Change-Id: Ia175bbe7d83fdc356aa5ecf474d30ba640ab7da4
Partial-Bug: 1706161
2017-12-21 13:42:00 +08:00
Zuul
d4bf57ee3e Merge "Updated from global requirements" 2017-12-21 05:33:58 +00:00
Dirk Mueller
d86e111ddf let hacking install its dependencies
hacking pulls in the right versions of flake8, mccabe, pep8 and
pyflakes already, listing them explicitely is unnecessary and
can cause mismatches.

Change-Id: Id44c90ddd4a8a2204fd7ce600097bafbc9469993
2017-12-20 13:00:27 +05:30
Zuul
67d2cd836b Merge "Update the documentation link for doc migration" 2017-12-19 05:00:58 +00:00
lingyongxu
5881bec6a6 Update the documentation link for doc migration
This patch is proposed according to the Direction 10 of doc
migration(https://etherpad.openstack.org/p/doc-migration-tracking).

Change-Id: Ifdf9aa3a03d268ba13c2d7c2f15ec728b909c37d
2017-12-19 11:13:20 +08:00
OpenStack Proposal Bot
cb9651ef9d Updated from global requirements
Change-Id: If3aa319a8b9254a3b05d413b88b18c2037bb7a76
2017-12-19 01:46:35 +00:00
Zuul
04b5b7c528 Merge "Updated from global requirements" 2017-12-12 09:18:46 +00:00
Andreas Jaeger
1dc4cbef6b Avoid tox_install.sh for constraints support
We do not need tox_install.sh, pip can handle constraints itself
and install the project correctly. Thus update tox.ini and remove
the now obsolete tools/tox_install.sh file.

This follows https://review.openstack.org/#/c/508061 to remove
tools/tox_install.sh.

Change-Id: Id0c51375402e9ee1255d25c685821bace9b6084a
2017-12-02 16:59:28 +00:00
Zuul
73592154f1 Merge "Implement client to support Kubernetes as VIM" 2017-11-22 01:54:54 +00:00
Andreas Jaeger
90c251b84e Remove setting of version/release from releasenotes
Release notes are version independent, so remove version/release
values. We've found that projects now require the service package
to be installed in order to build release notes, and this is entirely
due to the current convention of pulling in the version information.

Release notes should not need installation in order to build, so this
unnecessary version setting needs to be removed.

This is needed for new release notes publishing, see
I56909152975f731a9d2c21b2825b972195e48ee8 and the discussion starting
at
http://lists.openstack.org/pipermail/openstack-dev/2017-November/124480.html
.

Change-Id: I43e6ddc1786a5d71c08457c83dac6f9071aa5b8a
2017-11-16 20:51:01 +01:00
OpenStack Proposal Bot
c74447bf72 Updated from global requirements
Change-Id: I635b0625ceffed9a64989c36c875afcc7f1cc6d1
2017-11-16 11:26:46 +00:00
OpenStack Proposal Bot
42107c3d14 Updated from global requirements
Change-Id: I4f8bc1fd90c903bc1040a134afe0e3e6ca6eb02b
2017-11-14 04:20:22 +00:00
Cong Phuoc Hoang
6f1792b490 Implement client to support Kubernetes as VIM
This patch will add changes to support Kubernetes as VIM feature
in python-tackerclient.

After this change, user have to provide VIM type in VIM config file
when creating or updating VIM. The default VIM type is openstack.

Partially Implements: blueprint kubernetes-as-vim

Change-Id: I1b1610cb89a78a2a8fe134265b83dd469d72fdcd
2017-10-30 17:13:40 +09:00
Zuul
3e31720ed6 Merge "Fixes symmetrical for vnffg update" 2017-10-16 03:10:45 +00:00
OpenStack Proposal Bot
8ea5e29b44 Updated from global requirements
Change-Id: I3d374dd71df18c04a44dc8f5c1d66687e6765e37
2017-10-15 17:09:30 +00:00
Tim Rozet
584b44c8e7 Fixes symmetrical for vnffg update
Missed this with create fix:
Ide2aeab73b1dd88beb6e491e6b07cdee9fb7e48a

Change-Id: Ia983150de322d1ffbd7eeedc2048c19c095afefb
Signed-off-by: Tim Rozet <trozet@redhat.com>
2017-10-10 10:14:32 -04:00
Jenkins
bab4900db2 Merge "Update creating directly VNFFG and NS from descriptor template" 2017-09-28 12:16:36 +00:00
Jenkins
4458d3dce1 Merge "Fixes passing boolean as string for symmetrical" 2017-09-27 02:13:51 +00:00
Tim Rozet
9630f711a8 Fixes passing boolean as string for symmetrical
Bug where 'True'/'False' strings were being passed in REST to Tacker
service which would end up throwing an exception because the DB type for
symmetrical is boolean/small int.  This converts it to boolean in the
client.

Closes-Bug: 1711550

Change-Id: Ide2aeab73b1dd88beb6e491e6b07cdee9fb7e48a
Signed-off-by: Tim Rozet <trozet@redhat.com>
2017-09-25 12:59:28 -04:00
Thomas Bechtold
537c525095 Move oslosphinx and openstackdocstheme to test-requirements
Both are not needed during runtime.

Change-Id: If5d8022b12b06b08c49ce825f5d1f13619e7175b
2017-09-22 13:00:01 +02:00
OpenStack Proposal Bot
245af4c7fa Updated from global requirements
Change-Id: Ibbe71d3a261c0c3d71e2033a6bba58d48b721fcb
2017-09-13 13:03:25 +00:00
OpenStack Proposal Bot
06d210fa65 Updated from global requirements
Change-Id: If6d7c31bbca0a776e9604b1471b6276835ae6720
2017-09-11 21:49:43 +00:00
Jenkins
1a749c310c Merge "Add releasenotes for bug 1629169" 2017-08-15 10:26:23 +00:00
Cong Phuoc Hoang
d8b0dbcbb7 Update creating directly VNFFG and NS from descriptor template
Currently, Tacker only support creating VNFFG and NS from VNFFG descriptor
and NS descriptor. This patch will support creating directy VNFFG and NS
from template file without initiating VNFFG and NS descriptors before.

Change-Id: If7554287bbe1184d9b84609f81e53f17d7b5fdfe
Closes-bug: #1681032
2017-08-10 06:37:45 +00:00
Jenkins
543a89e763 Merge "Update reno for stable/pike" 2017-08-09 07:32:51 +00:00
8a93fea142 Update reno for stable/pike
Change-Id: Ie0a67966902b12db3be3d7a73f46051e58264794
2017-07-28 21:08:13 +00:00
OpenStack Proposal Bot
761f0b37b2 Updated from global requirements
Change-Id: Ic05ab3e7ca83af7eb07681a38328be22fee2f041
2017-07-27 19:22:04 +00:00
OpenStack Proposal Bot
6ddc62bbad Updated from global requirements
Change-Id: I265d44a0816952a62cb1ce9f9a8df88f2505f1d3
2017-07-21 01:20:03 +00:00
dharmendra
95ddcbe966 Switch from oslosphinx to openstackdocstheme
As part of the docs migration work[0] for Pike we
need to switch to use the openstackdocstheme.

[0]https://review.openstack.org/#/c/472275/

Change-Id: Ia48ca6860012850071f8c236d404d56a480b747c
2017-07-07 01:04:42 +00:00
Jenkins
bfed79e1b0 Merge "Error handling for vnfd-create with empty vnfd-file" 2017-07-05 09:33:49 +00:00
Jenkins
3b88fbcda1 Merge "Replace six.iteritems() with .items()" 2017-07-05 03:49:03 +00:00
vagrant
a11344c318 Error handling for vnfd-create with empty vnfd-file
Error handling for empty vnfd-file and yaml load.

Closes-Bug: #1490067
Change-Id: I48c21d193251a95c8fc7b10d3c6549b74c5287df
2017-07-03 08:13:26 +00:00
OpenStack Proposal Bot
ad3f2b5ab7 Updated from global requirements
Change-Id: I390c199b8e2eccfac40c775ec3854a159e8d64dd
2017-06-27 12:22:43 +00:00
rajat29
b7a494e7d4 Replace six.iteritems() with .items()
1.As mentioned in [1], we should avoid usingg
six.iteritems to achieve iterators. We can
use dict.items instead, as it will return
iterators in PY3 as well. And dict.items/keys
will more readable. 2.In py2, the performance
about list should be negligible, see the link [2].
[1] https://wiki.openstack.org/wiki/Python3
[2] http://lists.openstack.org/pipermail/openstack-dev/2015-June/066391.html

Change-Id: I988ff08fe1d6bc52c4a601641c564db7881accf4
2017-06-14 12:08:54 +05:30
venkatamahesh
a7266637e6 Add releasenotes for bug 1629169
Change-Id: I3b13c94263c7274a0cfdf393537649813913d288
2017-05-30 01:46:01 +00:00
52 changed files with 1608 additions and 167 deletions

View File

@@ -2,4 +2,3 @@
host=review.openstack.org
port=29418
project=openstack/python-tackerclient.git
defaultbranch=stable/pike

View File

@@ -2,7 +2,7 @@ Tacker Style Commandments
================================
- Step 1: Read the OpenStack Style Commandments
http://docs.openstack.org/developer/hacking/
https://docs.openstack.org/hacking/latest
- Step 2: Read on
@@ -10,7 +10,7 @@ Running Tests
-------------
The testing system is based on a combination of tox and testr. The canonical
approach to running tests is to simply run the command `tox`. This will
create virtual environments, populate them with depenedencies and run all of
create virtual environments, populate them with dependencies and run all of
the tests that OpenStack CI systems run. Behind the scenes, tox is running
`testr run --parallel`, but is set up such that you can supply any additional
testr arguments that are needed to tox. For example, you can run:
@@ -23,4 +23,4 @@ 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
https://wiki.openstack.org/wiki/Testr

View File

@@ -2,8 +2,8 @@
Team and repository tags
========================
.. image:: http://governance.openstack.org/badges/python-tackerclient.svg
:target: http://governance.openstack.org/reference/tags/index.html
.. 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

40
doc/source/cli/index.rst Normal file
View File

@@ -0,0 +1,40 @@
..
Licensed under the Apache License, Version 2.0 (the "License"); you may
not use this file except in compliance with the License. You may obtain
a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
Convention for heading levels in Neutron devref:
======= Heading 0 (reserved for the title in a document)
------- Heading 1
~~~~~~~ Heading 2
+++++++ Heading 3
''''''' Heading 4
(Avoid deeper levels because they do not render well.)
=========
Using CLI
=========
There are two CLIs which support the Networking API:
`OpenStackClient (OSC)
<https://docs.openstack.org/python-openstackclient/latest/>`__
and :doc:`neutron CLI <neutron>` (deprecated).
OpenStackClient
---------------
#TODO
neutron CLI
-----------
#TODO

View File

@@ -1,10 +1,22 @@
project = 'python-tackerclient'
# -*- coding: utf-8 -*-
#
# -- General configuration ---------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx']
extensions = [
'sphinx.ext.autodoc',
'reno.sphinxext',
'openstackdocstheme',
'cliff.sphinxext',
]
# openstackdocstheme options
repository_name = 'openstack/python-tackerclient'
bug_project = 'python-tackerclient'
bug_tag = 'doc'
html_last_updated_fmt = '%Y-%m-%d %H:%M'
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@@ -32,21 +44,11 @@ pygments_style = 'sphinx'
# The theme to use for HTML and HTML Help pages. Major themes that come with
# Sphinx are currently 'default' and 'sphinxdoc'.
html_theme = 'nature'
html_theme = 'openstackdocs'
# Output file base name for HTML help builder.
htmlhelp_basename = '%sdoc' % project
htmlhelp_basename = 'tackerclientdoc'
# -- Options for cliff.sphinxext plugin ---------------------------------------
# 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'),
]
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {'http://docs.python.org/': None}
autoprogram_cliff_application = 'openstack'

View File

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

View File

@@ -1,25 +1,57 @@
Python bindings to the OpenStack Tacker API
============================================
..
Licensed under the Apache License, Version 2.0 (the "License"); you may
not use this file except in compliance with the License. You may obtain
a copy of the License at
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.
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
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 API. It provides
:doc:`Python API bindings <reference/index>` (the tackerclient module) and
:doc:`command-line interface (CLI) <cli/index>`.
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::
User Documentation
------------------
export OS_USERNAME=user
export OS_PASSWORD=pass
export OS_TENANT_NAME=tenant
export OS_AUTH_URL=http://auth.example.com:5000/v2.0
.. toctree::
:maxdepth: 2
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::
cli/index
reference/index
export OS_URL=http://tacker.example.org:9890/
export OS_TOKEN=3bcc3d3a03f44e3d8377f9247b0ad155
Contributor Guide
-----------------
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::
In the :doc:`Contributor Guide <contributor/index>`, you will find
information on tackerclient's lower level programming details or APIs
as well as the transition to OpenStack client.
export OS_AUTH_STRATEGY=noauth
.. toctree::
:maxdepth: 2
Once you've configured your authentication parameters, you can run ``tacker -h`` to see a complete listing of available commands.
contributor/index
History
-------
Release notes is available at
http://docs.openstack.org/releasenotes/python-tackerclient/.

View File

@@ -0,0 +1,38 @@
..
Licensed under the Apache License, Version 2.0 (the "License"); you may
not use this file except in compliance with the License. You may obtain
a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
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.)
=================
Install Guide
=================
To install ``python-tackerclient``, it is required to have ``pip``
(in most cases). Make sure that ``pip`` is installed. Then type::
$ 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
After ``python-tackerclient`` is installed you will see command ``tacker``
in your environment.

View File

@@ -0,0 +1,29 @@
..
Licensed under the Apache License, Version 2.0 (the "License"); you may
not use this file except in compliance with the License. You may obtain
a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
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.)
tackerclient Python API
========================
Basic Usage
-----------
#TODO

View File

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

View File

@@ -0,0 +1,6 @@
---
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

@@ -37,7 +37,8 @@
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = [
'oslosphinx',
'reno.sphinxext'
'reno.sphinxext',
'openstackdocstheme'
]
# Add any paths that contain templates here, relative to this directory.
@@ -56,14 +57,9 @@ master_doc = 'index'
project = u'Tacker Client Release Notes'
copyright = u'2016, Tacker Developers'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
import pbr.version
tacker_client_version = pbr.version.VersionInfo('python-tackerclient')
release = tacker_client_version.version_string_with_vcs()
version = tacker_client_version.canonical_version_string()
# Release notes are version independent.
release = ''
version = ''
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@@ -105,7 +101,7 @@ pygments_style = 'sphinx'
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'default'
html_theme = 'openstackdocs'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
@@ -262,3 +258,8 @@ texinfo_documents = [
# -- Options for Internationalization output ------------------------------
locale_dirs = ['locale/']
# -- Options for openstackdocstheme -------------------------------------------
repository_name = 'openstack/python-tackerclient'
bug_project = 'python-tackerclient'
bug_tag = ''

View File

@@ -7,5 +7,6 @@ Contents:
:maxdepth: 2
unreleased
pike
ocata
newton

View File

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

View File

@@ -2,18 +2,18 @@
# 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.8.0 # Apache-2.0
cliff!=2.9.0,>=2.8.0 # Apache-2.0
iso8601>=0.1.11 # MIT
netaddr!=0.7.16,>=0.7.13 # BSD
netaddr>=0.7.18 # BSD
requests>=2.14.2 # Apache-2.0
python-keystoneclient>=3.8.0 # Apache-2.0
simplejson>=2.2.0 # MIT
six>=1.9.0 # MIT
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.2,>=2.1.0 # Apache-2.0
oslo.log>=3.22.0 # Apache-2.0
oslo.utils>=3.20.0 # Apache-2.0
oslosphinx>=4.7.0 # Apache-2.0
oslo.serialization!=2.19.1,>=1.10.0 # Apache-2.0
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

View File

@@ -5,7 +5,7 @@ description-file =
README.rst
author = OpenStack
author-email = openstack-dev@lists.openstack.org
home-page = http://docs.openstack.org/developer/tacker/
home-page = https://docs.openstack.org/tacker/latest
classifier =
Environment :: OpenStack
Intended Audience :: Developers
@@ -31,6 +31,17 @@ 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
[build_sphinx]
all_files = 1
build-dir = doc/build

View File

@@ -14,7 +14,7 @@
"""oslo.i18n integration module.
See http://docs.openstack.org/developer/oslo.i18n/usage.html .
See https://docs.openstack.org/oslo.i18n/latest/user/index.html.
"""

View File

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

View File

@@ -0,0 +1,61 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# 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

@@ -0,0 +1,102 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# 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))

207
tackerclient/osc/utils.py Normal file
View File

@@ -0,0 +1,207 @@
# 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

View File

View File

@@ -0,0 +1,267 @@
# 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(
'--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

@@ -279,7 +279,7 @@ def parse_args_to_dict(values_specs):
# populate the parser with arguments
_parser = argparse.ArgumentParser(add_help=False)
for opt, optspec in six.iteritems(_options):
for opt, optspec in _options.items():
_parser.add_argument(opt, **optspec)
_args = _parser.parse_args(_values_specs)
@@ -303,7 +303,7 @@ def _merge_args(qCmd, parsed_args, _extra_values, value_specs):
@param values_specs: the unparsed unknown parts
"""
temp_values = _extra_values.copy()
for key, value in six.iteritems(temp_values):
for key, value in temp_values.items():
if hasattr(parsed_args, key):
arg_value = getattr(parsed_args, key)
if arg_value is not None and value is not None:
@@ -387,7 +387,7 @@ class TackerCommand(command.OpenStackCommand):
def format_output_data(self, data):
# Modify data to make it more readable
if self.resource in data:
for k, v in six.iteritems(data[self.resource]):
for k, v in data[self.resource].items():
if isinstance(v, list):
value = '\n'.join(jsonutils.dumps(
i, indent=self.json_indent) if isinstance(i, dict)
@@ -450,7 +450,7 @@ class CreateCommand(TackerCommand, show.ShowOne):
info.pop(f)
else:
info = {'': ''}
return zip(*sorted(six.iteritems(info)))
return zip(*sorted(info.items()))
class UpdateCommand(TackerCommand):
@@ -712,6 +712,6 @@ class ShowCommand(TackerCommand, show.ShowOne):
self.format_output_data(data)
resource = data[self.resource]
if self.resource in data:
return zip(*sorted(six.iteritems(resource)))
return zip(*sorted(resource.items()))
else:
return None

View File

@@ -12,6 +12,7 @@
import yaml
from tackerclient.common import exceptions
from tackerclient.i18n import _
from tackerclient.tacker import v1_0 as tackerV10
@@ -50,6 +51,9 @@ class CreateNS(tackerV10.CreateCommand):
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'))
@@ -88,11 +92,25 @@ class CreateNS(tackerV10.CreateCommand):
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()
args['attributes']['param_values'] = yaml.load(
param_yaml, Loader=yaml.SafeLoader)
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'])

View File

@@ -26,7 +26,24 @@ class ListNSD(tackerV10.ListCommand):
"""List NSDs that belong to a given tenant."""
resource = _NSD
list_columns = ['id', 'name', 'description']
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):

View File

@@ -67,7 +67,11 @@ class CreateVIM(tackerV10.CreateCommand):
if parsed_args.config_file:
with open(parsed_args.config_file) as f:
vim_config = f.read()
config_param = yaml.load(vim_config, Loader=yaml.SafeLoader)
try:
config_param = yaml.load(vim_config,
Loader=yaml.SafeLoader)
except yaml.YAMLError as e:
raise exceptions.InvalidInput(e)
vim_obj = body[self.resource]
try:
auth_url = config_param.pop('auth_url')
@@ -76,7 +80,6 @@ class CreateVIM(tackerV10.CreateCommand):
'specified',
status_code=404)
vim_obj['auth_url'] = vim_utils.validate_auth_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',
@@ -113,13 +116,19 @@ class UpdateVIM(tackerV10.UpdateCommand):
if parsed_args.config_file:
with open(parsed_args.config_file) as f:
config_yaml = f.read()
config_param = yaml.load(config_yaml)
try:
config_param = yaml.load(config_yaml)
except yaml.YAMLError as e:
raise exceptions.InvalidInput(e)
vim_obj = body[self.resource]
if config_param is not None:
vim_utils.args2body_vim(config_param, vim_obj)
tackerV10.update_dict(parsed_args, body[self.resource],
['tenant_id', 'name', 'description',
'is_default'])
# type attribute is read-only, it can't be updated, so remove it
# in update method
body['vim'].pop('type', None)
return body

View File

@@ -24,24 +24,66 @@ 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']
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}
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)
if 'ssl_ca_cert' in config_param:
ssl_ca_cert = config_param.pop('ssl_ca_cert', '')
if ssl_ca_cert == 'None':
vim['auth_cred']['ssl_ca_cert'] = None
else:
vim['auth_cred']['ssl_ca_cert'] = ssl_ca_cert
else:
raise exceptions.TackerClientException(
message='ssl_ca_cert must be provided or leave it with None',
status_code=404)
def validate_auth_url(url):

View File

@@ -10,9 +10,10 @@
# License for the specific language governing permissions and limitations
# under the License.
from tackerclient.i18n import _
import yaml
from tackerclient.common import exceptions
from tackerclient.i18n import _
from tackerclient.tacker import v1_0 as tackerV10
@@ -28,6 +29,18 @@ class ListFC(tackerV10.ListCommand):
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."""
@@ -91,6 +104,9 @@ class CreateVNFFG(tackerV10.CreateCommand):
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. '
@@ -129,12 +145,25 @@ class CreateVNFFG(tackerV10.CreateCommand):
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()
args['attributes']['param_values'] = yaml.load(
param_yaml, Loader=yaml.SafeLoader)
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',
@@ -148,16 +177,23 @@ class UpdateVNFFG(tackerV10.UpdateCommand):
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', metavar='{True,False}',
'--symmetrical',
action='store_true',
default=False,
help=_('Should a reverse path be created for the NFP'))
def args2body(self, parsed_args):
body = {self.resource: {}}
args = {}
body = {self.resource: args}
tacker_client = self.get_client()
tacker_client.format = parsed_args.request_format
@@ -173,6 +209,17 @@ class UpdateVNFFG(tackerV10.UpdateCommand):
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

View File

@@ -26,7 +26,24 @@ class ListVNFFGD(tackerV10.ListCommand):
"""List VNFFGDs that belong to a given tenant."""
resource = _VNFFGD
list_columns = ['id', 'name', 'description']
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):

View File

@@ -17,6 +17,7 @@
import yaml
from tackerclient.common import exceptions
from tackerclient.i18n import _
from tackerclient.tacker import v1_0 as tackerV10
@@ -87,8 +88,11 @@ class CreateVNF(tackerV10.CreateCommand):
if parsed_args.config_file:
with open(parsed_args.config_file) as f:
config_yaml = f.read()
config = yaml.load(
config_yaml, Loader=yaml.SafeLoader)
try:
config = yaml.load(
config_yaml, Loader=yaml.SafeLoader)
except yaml.YAMLError as e:
raise exceptions.InvalidInput(e)
if config:
args['attributes']['config'] = config
@@ -113,14 +117,20 @@ class CreateVNF(tackerV10.CreateCommand):
elif parsed_args.vnfd_template:
with open(parsed_args.vnfd_template) as f:
template = f.read()
args['vnfd_template'] = yaml.load(
template, Loader=yaml.SafeLoader)
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()
args['attributes']['param_values'] = yaml.load(
param_yaml, Loader=yaml.SafeLoader)
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'])
@@ -147,12 +157,18 @@ class UpdateVNF(tackerV10.UpdateCommand):
if parsed_args.config_file:
with open(parsed_args.config_file) as f:
config_yaml = f.read()
config = yaml.load(config_yaml, Loader=yaml.SafeLoader)
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')
config = yaml.load(config_str, Loader=yaml.SafeLoader)
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'])

View File

@@ -20,6 +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
@@ -75,13 +76,17 @@ class CreateVNFD(tackerV10.CreateCommand):
def args2body(self, parsed_args):
body = {self.resource: {}}
vnfd = None
if parsed_args.vnfd_file:
with open(parsed_args.vnfd_file) as f:
vnfd = f.read()
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)
if vnfd:
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}
tackerV10.update_dict(parsed_args, body[self.resource],
['tenant_id', 'name', 'description'])
return body

View File

@@ -4,3 +4,4 @@ password: '12345'
project_name: 'abc'
project_domain_name: 'prj_domain_name'
user_domain_name: 'user_domain_name'
type: 'openstack'

View File

@@ -5,3 +5,4 @@ project_name: 'abc'
project_domain_name: 'prj_domain_name'
user_domain_name: 'user_domain_name'
cert_verify: 'False'
type: 'openstack'

View File

@@ -3,3 +3,4 @@ password: '12345'
project_name: 'abc'
project_domain_name: 'prj_domain_name'
user_domain_name: 'user_domain_name'
type: 'openstack'

View File

@@ -0,0 +1,5 @@
auth_url: 'https://1.2.3.4:6443'
bearer_token: 'xyz'
ssl_ca_cert: None
project_name: 'default'
type: 'kubernetes'

View File

@@ -0,0 +1,4 @@
bearer_token: 'xyz'
ssl_ca_cert: None
project_name: 'default'
type: 'kubernetes'

View File

@@ -0,0 +1,6 @@
auth_url: 'https://1.2.3.4:6443'
username: 'xyz'
password: '12345'
ssl_ca_cert: 'abcxyz'
project_name: 'default'
type: 'kubernetes'

View File

@@ -0,0 +1,5 @@
username: 'xyz'
password: '12345'
ssl_ca_cert: 'abcxyz'
project_name: 'default'
type: 'kubernetes'

View File

@@ -0,0 +1 @@
abcxyz

View File

@@ -41,6 +41,7 @@ class CLITestV10VIMJSON(test_cli10.CLITestV10Base):
'user_domain_name': 'user_domain_name',
'cert_verify': 'True'}
self.auth_url = 'http://1.2.3.4:5000'
self.type = 'openstack'
def test_register_vim_all_params(self):
cmd = vim.CreateVIM(test_cli10.MyApp(sys.stdout), None)
@@ -53,9 +54,9 @@ class CLITestV10VIMJSON(test_cli10.CLITestV10Base):
name,
'--config-file', vim_config,
'--description', description]
position_names = ['auth_cred', 'vim_project', 'auth_url']
position_names = ['auth_cred', 'vim_project', 'auth_url', 'type']
position_values = [self.auth_cred, self.vim_project,
self.auth_url]
self.auth_url, self.type]
extra_body = {'type': 'openstack', 'name': name,
'description': description, 'is_default': False}
self._test_create_resource(self._RESOURCE, cmd, None, my_id,
@@ -77,9 +78,9 @@ class CLITestV10VIMJSON(test_cli10.CLITestV10Base):
name,
'--config-file', vim_config,
'--description', description]
position_names = ['auth_cred', 'vim_project', 'auth_url']
position_names = ['auth_cred', 'vim_project', 'auth_url', 'type']
position_values = [self.auth_cred, self.vim_project,
self.auth_url]
self.auth_url, self.type]
extra_body = {'type': 'openstack', 'name': name,
'description': description, 'is_default': False}
self._test_create_resource(self._RESOURCE, cmd, None, my_id,
@@ -97,9 +98,9 @@ class CLITestV10VIMJSON(test_cli10.CLITestV10Base):
name,
'--config-file', vim_config,
'--description', description]
position_names = ['auth_cred', 'vim_project', 'auth_url']
position_names = ['auth_cred', 'vim_project', 'auth_url', 'type']
position_values = [self.auth_cred, self.vim_project,
self.auth_url]
self.auth_url, self.type]
extra_body = {'type': 'openstack', 'name': name,
'description': description, 'is_default': False}
message = 'Auth URL must be specified'
@@ -122,11 +123,12 @@ class CLITestV10VIMJSON(test_cli10.CLITestV10Base):
name,
'--config-file', vim_config,
]
position_names = ['auth_cred', 'vim_project', 'auth_url']
position_names = ['auth_cred', 'vim_project', 'auth_url', 'type']
position_values = [
self.auth_cred,
self.vim_project,
self.auth_url
self.auth_url,
self.type
]
extra_body = {'type': 'openstack', 'name': name, 'is_default': False}
self._test_create_resource(self._RESOURCE, cmd, name, my_id, args,
@@ -163,8 +165,9 @@ class CLITestV10VIMJSON(test_cli10.CLITestV10Base):
'--name', name,
'--description', description,
'--is_default', is_default]
extra_fields = {'vim_project': self.vim_project, 'auth_cred':
self.auth_cred, 'is_default': 'True',
extra_fields = {'vim_project': self.vim_project,
'auth_cred': self.auth_cred,
'is_default': 'True',
'name': name, 'description': description}
self._test_update_resource(self._RESOURCE, cmd, my_id, args,
extra_fields)

View File

@@ -0,0 +1,172 @@
# Copyright 2015-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 sys
from tackerclient.common import exceptions
from tackerclient.common import utils
from tackerclient.tacker.v1_0.nfvo import vim
from tackerclient.tests.unit import test_cli10
API_VERSION = "1.0"
FORMAT = 'json'
TOKEN = 'testtoken'
ENDURL = 'localurl'
class CLITestV10VIMJSON(test_cli10.CLITestV10Base):
_RESOURCE = 'vim'
_RESOURCES = 'vims'
def setUp(self):
plurals = {'vims': 'vim'}
super(CLITestV10VIMJSON, self).setUp(plurals=plurals)
self.vim_project = {'name': 'default'}
self.auth_cred = {'username': 'xyz', 'password': '12345',
'ssl_ca_cert': 'abcxyz'}
self.auth_url = 'https://1.2.3.4:6443'
self.type = 'kubernetes'
def test_register_vim_all_params(self):
cmd = vim.CreateVIM(test_cli10.MyApp(sys.stdout), None)
name = 'my-name'
my_id = 'my-id'
description = 'Vim Description'
vim_config = utils.get_file_path(
'tests/unit/vm/samples/vim_k8s_config.yaml')
args = [
name,
'--config-file', vim_config,
'--description', description]
position_names = ['auth_cred', 'vim_project', 'auth_url', 'type']
position_values = [self.auth_cred, self.vim_project,
self.auth_url, self.type]
extra_body = {'type': 'kubernetes', 'name': name,
'description': description, 'is_default': False}
self._test_create_resource(self._RESOURCE, cmd, None, my_id,
args, position_names, position_values,
extra_body=extra_body)
def test_register_vim_with_no_auth_url(self):
cmd = vim.CreateVIM(test_cli10.MyApp(sys.stdout), None)
my_id = 'my-id'
name = 'test_vim'
description = 'Vim Description'
vim_config = utils.get_file_path(
'tests/unit/vm/samples/vim_k8s_config_without_auth_url.yaml')
args = [
name,
'--config-file', vim_config,
'--description', description]
position_names = ['auth_cred', 'vim_project', 'auth_url', 'type']
position_values = [self.auth_cred, self.vim_project,
self.auth_url, self.type]
extra_body = {'type': 'kubernetes', 'name': name,
'description': description, 'is_default': False}
message = 'Auth URL must be specified'
ex = self.assertRaises(exceptions.TackerClientException,
self._test_create_resource,
self._RESOURCE, cmd, None, my_id, args,
position_names, position_values,
extra_body=extra_body)
self.assertEqual(message, ex.message)
self.assertEqual(404, ex.status_code)
def test_register_vim_with_mandatory_params(self):
cmd = vim.CreateVIM(test_cli10.MyApp(sys.stdout), None)
name = 'my-name'
my_id = 'my-id'
vim_config = utils.get_file_path(
'tests/unit/vm/samples/vim_k8s_config.yaml')
args = [
name,
'--config-file', vim_config,
]
position_names = ['auth_cred', 'vim_project', 'auth_url', 'type']
position_values = [
self.auth_cred,
self.vim_project,
self.auth_url,
self.type
]
extra_body = {'type': 'kubernetes', 'name': name, 'is_default': False}
self._test_create_resource(self._RESOURCE, cmd, name, my_id, args,
position_names, position_values,
extra_body=extra_body)
def test_list_vims(self):
cmd = vim.ListVIM(test_cli10.MyApp(sys.stdout), None)
self._test_list_resources(self._RESOURCES, cmd, True)
def test_show_vim_id(self):
cmd = vim.ShowVIM(test_cli10.MyApp(sys.stdout), None)
args = ['--fields', 'id', self.test_id]
self._test_show_resource(self._RESOURCE, cmd, self.test_id, args,
['id'])
def test_show_vim_id_name(self):
cmd = vim.ShowVIM(test_cli10.MyApp(sys.stdout), None)
args = ['--fields', 'id', '--fields', 'name', self.test_id]
self._test_show_resource(self._RESOURCE, cmd, self.test_id,
args, ['id', 'name'])
def test_update_vim_all_params(self):
cmd = vim.UpdateVIM(test_cli10.MyApp(sys.stdout), None)
update_config = utils.get_file_path(
'tests/unit/vm/samples/vim_k8s_config_without_auth_url.yaml')
my_id = 'my-id'
name = 'new_name'
name = 'new_name'
description = 'new_description'
is_default = 'True'
args = [
my_id,
'--config-file', str(update_config),
'--name', name,
'--description', description,
'--is_default', is_default]
extra_fields = {'vim_project': self.vim_project,
'auth_cred': self.auth_cred,
'is_default': 'True',
'name': name, 'description': description}
self._test_update_resource(self._RESOURCE, cmd, my_id, args,
extra_fields)
def test_update_vim_with_mandatory_params(self):
cmd = vim.UpdateVIM(test_cli10.MyApp(sys.stdout), None)
update_config = utils.get_file_path(
'tests/unit/vm/samples/vim_k8s_config_without_auth_url.yaml')
my_id = 'my-id'
args = [
my_id,
'--config-file', str(update_config)]
extra_fields = {'vim_project': self.vim_project,
'auth_cred': self.auth_cred}
self._test_update_resource(self._RESOURCE, cmd, my_id, args,
extra_fields)
def test_delete_vim(self):
cmd = vim.DeleteVIM(test_cli10.MyApp(sys.stdout), None)
my_id = 'my-id'
args = [my_id]
self._test_delete_resource(self._RESOURCE, cmd, my_id, args)
def test_multi_delete_vim(self):
cmd = vim.DeleteVIM(test_cli10.MyApp(sys.stdout), None)
vim_ids = 'my-id1 my-id2 my-id3'
args = [vim_ids]
self._test_delete_resource(self._RESOURCE, cmd, vim_ids, args)

View File

@@ -0,0 +1,170 @@
# Copyright 2015-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 sys
from tackerclient.common import exceptions
from tackerclient.common import utils
from tackerclient.tacker.v1_0.nfvo import vim
from tackerclient.tests.unit import test_cli10
API_VERSION = "1.0"
FORMAT = 'json'
TOKEN = 'testtoken'
ENDURL = 'localurl'
class CLITestV10VIMJSON(test_cli10.CLITestV10Base):
_RESOURCE = 'vim'
_RESOURCES = 'vims'
def setUp(self):
plurals = {'vims': 'vim'}
super(CLITestV10VIMJSON, self).setUp(plurals=plurals)
self.vim_project = {'name': 'default'}
self.auth_cred = {'bearer_token': 'xyz', 'ssl_ca_cert': None}
self.auth_url = 'https://1.2.3.4:6443'
self.type = 'kubernetes'
def test_register_vim_all_params(self):
cmd = vim.CreateVIM(test_cli10.MyApp(sys.stdout), None)
name = 'my-name'
my_id = 'my-id'
description = 'Vim Description'
vim_config = utils.get_file_path(
'tests/unit/vm/samples/vim_k8s_bearer_token.yaml')
args = [
name,
'--config-file', vim_config,
'--description', description]
position_names = ['auth_cred', 'vim_project', 'auth_url', 'type']
position_values = [self.auth_cred, self.vim_project,
self.auth_url, self.type]
extra_body = {'type': 'kubernetes', 'name': name,
'description': description, 'is_default': False}
self._test_create_resource(self._RESOURCE, cmd, None, my_id,
args, position_names, position_values,
extra_body=extra_body)
def test_register_vim_with_no_auth_url(self):
cmd = vim.CreateVIM(test_cli10.MyApp(sys.stdout), None)
my_id = 'my-id'
name = 'test_vim'
description = 'Vim Description'
vim_config = utils.get_file_path(
'tests/unit/vm/samples/vim_k8s_bearer_token_without_auth_url.yaml')
args = [
name,
'--config-file', vim_config,
'--description', description]
position_names = ['auth_cred', 'vim_project', 'auth_url', 'type']
position_values = [self.auth_cred, self.vim_project,
self.auth_url, self.type]
extra_body = {'type': 'kubernetes', 'name': name,
'description': description, 'is_default': False}
message = 'Auth URL must be specified'
ex = self.assertRaises(exceptions.TackerClientException,
self._test_create_resource,
self._RESOURCE, cmd, None, my_id, args,
position_names, position_values,
extra_body=extra_body)
self.assertEqual(message, ex.message)
self.assertEqual(404, ex.status_code)
def test_register_vim_with_mandatory_params(self):
cmd = vim.CreateVIM(test_cli10.MyApp(sys.stdout), None)
name = 'my-name'
my_id = 'my-id'
vim_config = utils.get_file_path(
'tests/unit/vm/samples/vim_k8s_bearer_token.yaml')
args = [
name,
'--config-file', vim_config,
]
position_names = ['auth_cred', 'vim_project', 'auth_url', 'type']
position_values = [
self.auth_cred,
self.vim_project,
self.auth_url,
self.type
]
extra_body = {'type': 'kubernetes', 'name': name, 'is_default': False}
self._test_create_resource(self._RESOURCE, cmd, name, my_id, args,
position_names, position_values,
extra_body=extra_body)
def test_list_vims(self):
cmd = vim.ListVIM(test_cli10.MyApp(sys.stdout), None)
self._test_list_resources(self._RESOURCES, cmd, True)
def test_show_vim_id(self):
cmd = vim.ShowVIM(test_cli10.MyApp(sys.stdout), None)
args = ['--fields', 'id', self.test_id]
self._test_show_resource(self._RESOURCE, cmd, self.test_id, args,
['id'])
def test_show_vim_id_name(self):
cmd = vim.ShowVIM(test_cli10.MyApp(sys.stdout), None)
args = ['--fields', 'id', '--fields', 'name', self.test_id]
self._test_show_resource(self._RESOURCE, cmd, self.test_id,
args, ['id', 'name'])
def test_update_vim_all_params(self):
cmd = vim.UpdateVIM(test_cli10.MyApp(sys.stdout), None)
update_config = utils.get_file_path(
'tests/unit/vm/samples/vim_k8s_bearer_token_without_auth_url.yaml')
my_id = 'my-id'
name = 'new_name'
description = 'new_description'
is_default = 'True'
args = [
my_id,
'--config-file', str(update_config),
'--name', name,
'--description', description,
'--is_default', is_default]
extra_fields = {'vim_project': self.vim_project,
'auth_cred': self.auth_cred,
'is_default': 'True',
'name': name, 'description': description}
self._test_update_resource(self._RESOURCE, cmd, my_id, args,
extra_fields)
def test_update_vim_with_mandatory_params(self):
cmd = vim.UpdateVIM(test_cli10.MyApp(sys.stdout), None)
update_config = utils.get_file_path(
'tests/unit/vm/samples/vim_k8s_bearer_token_without_auth_url.yaml')
my_id = 'my-id'
args = [
my_id,
'--config-file', str(update_config)]
extra_fields = {'vim_project': self.vim_project,
'auth_cred': self.auth_cred}
self._test_update_resource(self._RESOURCE, cmd, my_id, args,
extra_fields)
def test_delete_vim(self):
cmd = vim.DeleteVIM(test_cli10.MyApp(sys.stdout), None)
my_id = 'my-id'
args = [my_id]
self._test_delete_resource(self._RESOURCE, cmd, my_id, args)
def test_multi_delete_vim(self):
cmd = vim.DeleteVIM(test_cli10.MyApp(sys.stdout), None)
vim_ids = 'my-id1 my-id2 my-id3'
args = [vim_ids]
self._test_delete_resource(self._RESOURCE, cmd, vim_ids, args)

View File

@@ -18,6 +18,7 @@ from mock import mock_open
from mock import patch
import sys
from tackerclient.common.exceptions import InvalidInput
from tackerclient.tacker.v1_0.vnfm import vnfd
from tackerclient.tests.unit import test_cli10
@@ -73,6 +74,30 @@ class CLITestV10VmVNFDJSON(test_cli10.CLITestV10Base):
args, position_names, position_values,
extra_body=extra_body)
@patch("tackerclient.tacker.v1_0.vnfm.vnfd.open",
side_effect=mock_open(read_data=""),
create=True)
def test_create_vnfd_with_empty_file(self, mo):
cmd = vnfd.CreateVNFD(
test_cli10.MyApp(sys.stdout), None)
name = 'my_name'
my_id = 'my-id'
args = [name, '--vnfd-file', 'vnfd-file', ]
position_names = ['name']
position_values = [name]
extra_body = {
'service_types': [{'service_type': 'vnfd'}],
'attributes': {'vnfd': 'vnfd'}
}
err = None
try:
self._test_create_resource(self._RESOURCE, cmd, name, my_id,
args, position_names, position_values,
extra_body=extra_body)
except InvalidInput:
err = True
self.assertEqual(True, err)
def test_list_vnfds(self):
cmd = vnfd.ListVNFD(test_cli10.MyApp(sys.stdout), None)
self._test_list_resources(self._RESOURCES, cmd, True,

View File

@@ -16,6 +16,7 @@
import sys
from tackerclient.common import utils
from tackerclient.tacker.v1_0.nfvo import vnffg
from tackerclient.tests.unit import test_cli10
@@ -94,11 +95,23 @@ class CLITestV10VmVNFFGJSON(test_cli10.CLITestV10Base):
def test_update_vnffg(self):
cmd = vnffg.UpdateVNFFG(test_cli10.MyApp(sys.stdout), None)
my_id = 'my-id'
key = 'new_key'
value = 'new-value'
update_vnffg = utils.get_file_path(
'tests/unit/vm/samples/vnffg_update_file.yaml')
vnf_mapping = 'VNFD1:VNF1'
args = [
my_id,
'--vnf-mapping', vnf_mapping,
'--vnffgd-template', str(update_vnffg),
'--symmetrical'
]
extra_fields = {
"vnf_mapping": {"VNFD1": "VNF1"},
"vnffgd_template": "abcxyz",
"symmetrical": True
}
self._test_update_resource(self._RESOURCE, cmd, my_id,
[my_id, '--%s' % key, value],
{key: value}, get_client_called_count=2)
args, extra_fields,
get_client_called_count=2)
def test_delete_vnffg(self):
cmd = vnffg.DeleteVNFFG(test_cli10.MyApp(sys.stdout), None)

View File

@@ -76,11 +76,23 @@ class CLITestV10VmVNFFGDJSON(test_cli10.CLITestV10Base):
def test_list_vnffgds(self):
cmd = vnffgd.ListVNFFGD(test_cli10.MyApp(sys.stdout), None)
self._test_list_resources(self._RESOURCES, cmd, True)
self._test_list_resources(self._RESOURCES, cmd, True,
template_source='onboarded')
def test_list_inline_vnffgds(self):
cmd = vnffgd.ListVNFFGD(test_cli10.MyApp(sys.stdout), None)
self._test_list_resources(self._RESOURCES, cmd, True,
template_source='inline')
def test_list_all_vnffgds(self):
cmd = vnffgd.ListVNFFGD(test_cli10.MyApp(sys.stdout), None)
self._test_list_resources(self._RESOURCES, cmd, True,
template_source='all')
def test_list_vnffgds_pagenation(self):
cmd = vnffgd.ListVNFFGD(test_cli10.MyApp(sys.stdout), None)
self._test_list_resources(self._RESOURCES, cmd, True)
self._test_list_resources(self._RESOURCES, cmd, True,
template_source='onboarded')
def test_show_vnffgd_id(self):
cmd = vnffgd.ShowVNFFGD(test_cli10.MyApp(sys.stdout), None)

View File

@@ -28,24 +28,61 @@ class TestVIMUtils(testtools.TestCase):
'username': sentinel.usrname1,
'password': sentinel.password1,
'project_domain_name': sentinel.prj_domain_name1,
'user_domain_name': sentinel.user_domain.name,
'cert_verify': 'True',
'user_domain_name': sentinel.user_domain.name, }
'type': 'openstack'}
vim = {}
auth_cred = config_param.copy()
auth_cred.pop('project_name')
auth_cred.pop('project_domain_name')
auth_cred.pop('type')
expected_vim = {'auth_cred': auth_cred,
'vim_project':
{'name': sentinel.prj_name,
'project_domain_name': sentinel.prj_domain_name1}}
'project_domain_name': sentinel.prj_domain_name1},
'type': 'openstack'}
vim_utils.args2body_vim(config_param.copy(), vim)
self.assertEqual(expected_vim, vim)
def test_args2body_kubernetes_vim(self):
config_param = {'username': sentinel.usrname1,
'password': sentinel.password1,
'ssl_ca_cert': 'abcxyz',
'project_name': sentinel.prj_name,
'type': 'kubernetes'}
vim = {}
auth_cred = config_param.copy()
auth_cred.pop('project_name')
auth_cred.pop('type')
expected_vim = {'auth_cred': auth_cred,
'vim_project':
{'name': sentinel.prj_name},
'type': 'kubernetes'}
vim_utils.args2body_vim(config_param.copy(), vim)
self.assertEqual(expected_vim, vim)
def test_args2body_kubernetes_vim_bearer(self):
config_param = {'bearer_token': sentinel.bearer_token,
'ssl_ca_cert': None,
'project_name': sentinel.prj_name,
'type': 'kubernetes'}
vim = {}
auth_cred = config_param.copy()
auth_cred.pop('project_name')
auth_cred.pop('type')
expected_vim = {'auth_cred': auth_cred,
'vim_project':
{'name': sentinel.prj_name},
'type': 'kubernetes'}
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,
'cert_verify': 'True',
'user_domain_name': sentinel.user_domain.name, }
'type': 'openstack'}
vim = {}
self.assertRaises(exceptions.TackerClientException,
vim_utils.args2body_vim,

View File

@@ -4,14 +4,13 @@
hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
coverage!=4.4,>=4.0 # Apache-2.0
fixtures>=3.0.0 # Apache-2.0/BSD
flake8<2.6.0,>=2.5.4 # MIT
pep8==1.5.7 # MIT
pyflakes==0.8.1 # MIT
python-subunit>=0.0.18 # Apache-2.0/BSD
sphinx>=1.6.2 # BSD
python-subunit>=1.0.0 # Apache-2.0/BSD
sphinx!=1.6.6,>=1.6.2 # BSD
testrepository>=0.0.18 # Apache-2.0/BSD
testtools>=1.4.0 # MIT
testtools>=2.2.0 # MIT
oslosphinx>=4.7.0 # Apache-2.0
openstackdocstheme>=1.18.1 # Apache-2.0
# releasenotes
reno!=2.3.1,>=1.8.0 # Apache-2.0
mock>=2.0 # BSD
reno>=2.5.0 # Apache-2.0
mock>=2.0.0 # BSD

View File

@@ -1,30 +0,0 @@
#!/usr/bin/env bash
# Client constraint file contains this client version pin that is in conflict
# with installing the client from source. We should remove the version pin in
# the constraints file before applying it for from-source installation.
CONSTRAINTS_FILE="$1"
shift 1
set -e
# NOTE(tonyb): Place this in the tox enviroment's log dir so it will get
# published to logs.openstack.org for easy debugging.
localfile="$VIRTUAL_ENV/log/upper-constraints.txt"
if [[ "$CONSTRAINTS_FILE" != http* ]]; then
CONSTRAINTS_FILE="file://$CONSTRAINTS_FILE"
fi
# NOTE(tonyb): need to add curl to bindep.txt if the project supports bindep
curl "$CONSTRAINTS_FILE" --insecure --progress-bar --output "$localfile"
pip install -c"$localfile" openstack-requirements
# This is the main purpose of the script: Allow local installation of
# the current repo. It is listed in constraints file and thus any
# install will be constrained and we need to unconstrain it.
edit-constraints "$localfile" -- "$CLIENT_NAME"
pip install -c"$localfile" -U "$@"
exit $?

11
tox.ini
View File

@@ -5,14 +5,14 @@ skipsdist = True
[testenv]
setenv = VIRTUAL_ENV={envdir}
BRANCH_NAME=master
CLIENT_NAME=python-tackerclient
LANG=en_US.UTF-8
LANGUAGE=en_US:en
LC_ALL=C
usedevelop = True
install_command = {toxinidir}/tools/tox_install.sh {env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=stable/pike} {opts} {packages}
deps = -r{toxinidir}/requirements.txt
install_command = pip install {opts} {packages}
deps =
-c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt}
-r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
commands = python setup.py testr --testr-args='{posargs}'
@@ -23,6 +23,9 @@ distribute = false
[testenv:venv]
commands = {posargs}
[testenv:docs]
commands = sphinx-build -W -b html doc/source doc/build/html
[testenv:releasenotes]
commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html