Compare commits

..

7 Commits

Author SHA1 Message Date
Yasufumi Ogawa
82e1a837bc Drop test for lower constraints
As we agreed, drop lower constraints test from stable branches to avoid
difficulty of maintaining package depencencies for the recent pip
dependency resolver.

Signed-off-by: Yasufumi Ogawa <yasufum.o@gmail.com>
Change-Id: Ifc3055086c2d0b14b5227105a0866a181f9446f0
2021-09-22 16:37:56 +09:00
Zuul
7bda108ddf Merge "Improve readability of vnflcm show command" into stable/ussuri 2020-05-25 10:09:31 +00:00
Shubham
ee2e460333 Improve readability of vnflcm show command
Display show command data in human_readable format for better
readability for complex attributes of vnf instance like
'vimConnectionInfo', 'instantiatedVnfInfo', '_links'.

Please checkout below link to see the difference in vnflcm show
response data before and after resolving the issue.
http://paste.openstack.org/show/792866

Change-Id: I52684618f7042f5566ea3bed7255d17d2fd61df2
2020-05-18 10:40:04 +05:30
Shubham
548453627f Display correct fields in output for list command
If user list vnf packages with --exclude_default option, it should
display all attributes except default set of complex.

$ openstack vnf package list --exclude_default
Expected fields:
  'id', 'vnfProductName', 'onboardingState', 'usageState',
  'operationalState', '_links', 'vnfdVersion', 'vnfProvider',
  'vnfSoftwareVersion', 'vnfdId'

Actual fields displayed are:
   'id', 'vnfProductName', 'onboardingState', 'usageState',
   'operationalState'

Fixed this issue and now it will display fields as mentioned above in
expected fields.

Closes-Bug: 1875330

Change-Id: I61da40d2e6e7e42999b7ab2d2441cddde64118d9
2020-05-14 12:55:17 +05:30
Shubham
86fefdb119 Improve readability of vnf package show command
Display show command data in human_readable format for better
readability for complex attributes of vnf package like 'softwareImages',
'checksum', '_links' and 'useDefinedData'.

Please checkout below link to see the difference in vnf package show
response data before and after resolving the issue.
http://paste.openstack.org/show/792305/

Change-Id: I4827437461fd42b4da8a90d7f8ef703625c78cf5
(cherry picked from commit e030c9019a)
2020-05-14 12:30:06 +05:30
0d6e331c93 Update TOX/UPPER_CONSTRAINTS_FILE for stable/ussuri
Update the URL to the upper-constraints file to point to the redirect
rule on releases.openstack.org so that anyone working on this branch
will switch to the correct upper-constraints list automatically when
the requirements repository branches.

Until the requirements repository has as stable/ussuri branch, tests will
continue to use the upper-constraints list on master.

Change-Id: I6408964243c70fd2ca48cdb46041a8c2c15cf52e
2020-04-10 12:25:54 +00:00
70d79b533a Update .gitreview for stable/ussuri
Change-Id: I8ebaa10d72136b2defe1f9c05923281efc4237f5
2020-04-10 12:25:52 +00:00
145 changed files with 6351 additions and 9888 deletions

View File

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

View File

@@ -1,6 +1,6 @@
- project:
templates:
- check-requirements
- openstack-python3-jobs
- openstack-python3-ussuri-jobs
- publish-openstack-docs-pti
- release-notes-jobs-python3

View File

@@ -2,6 +2,6 @@
# 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>=2.0.0,!=2.1.0 # BSD
openstackdocstheme>=2.2.1 # Apache-2.0
reno>=3.1.0 # Apache-2.0
sphinx!=1.6.6,!=1.6.7,!=2.1.0,>=1.6.2 # BSD
openstackdocstheme>=1.18.1 # Apache-2.0
reno>=2.5.0 # Apache-2.0

View File

@@ -11,6 +11,14 @@
License for the specific language governing permissions and limitations
under the License.
Convention for heading levels in Neutron devref:
======= Heading 0 (reserved for the title in a document)
------- Heading 1
~~~~~~~ Heading 2
+++++++ Heading 3
''''''' Heading 4
(Avoid deeper levels because they do not render well.)
=============
Command List
=============
@@ -23,81 +31,55 @@ of individual command can be referred by **openstack help <command-name>**.
.. code-block:: console
[legacy]
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).
[v1] --os-tacker-api-version 1
openstack ns create Create a NS.
openstack ns delete Delete given NS(s).
openstack ns list List NS that belong to a given tenant.
openstack ns show Show information of a given NS.
openstack ns descriptor create Create a NSD.
openstack ns descriptor delete Delete a given NSD.
openstack ns descriptor list List NSD(s) that belong to a given tenant.
openstack ns descriptor show Show information of a given NSD.
openstack ns descriptor template show Show template of a given NSD.
openstack vnf graph create Create a VNFFG.
openstack vnf graph delete Delete a given VNFFG.
openstack vnf graph list List VNFFG(s) that belong to a given tenant.
openstack vnf graph show Show information of a given VNFFG.
openstack vnf graph set Update a given VNFFG.
openstack vnf graph descriptor create Create a VNFFGD.
openstack vnf graph descriptor delete Delete a given VNFFGD.
openstack vnf graph descriptor list List VNFFGD(s) that belong to a given tenant.
openstack vnf graph descriptor show Show information of a given VNFFGD.
openstack vnf graph descriptor template show Show template of a given VNFFGD.
openstack vnf chain list List SFC(s) that belong to a given tenant.
openstack vnf chain show Show information of a given SFC.
openstack vnf classifier list List FC(s) that belong to a given tenant.
openstack vnf classifier show Show information of a given FC.
openstack vnf network forwarding path list List NFP(s) that belong to a given tenant.
openstack vnf network forwarding path show Show information of a given NFP.
openstack nfv event show Show event given the event id.
openstack nfv event list List events of resources.
openstack vnf package create Create a new individual VNF package resource.
openstack vnf package delete Delete given VNF package(s).
openstack vnf package list List all VNF packages.
openstack vnf package show Show package details.
openstack vnf package upload Upload a VNF package.
openstack vnf package download Download a VNF package.
openstack vnf package artifact download Download a VNF package artifact.
openstack vnf package update Update a state of a VNF package.
openstack vnflcm create Create a new VNF instance resource.
openstack vnflcm instantiate Instantiate a VNF instance.
openstack vnflcm list List VNF instance.
openstack vnflcm show Show VNF instance.
openstack vnflcm terminate Terminate a VNF instance.
openstack vnflcm delete Delete a VNF instance resource.
openstack vnflcm heal Heal a VNF instance.
openstack vnflcm update Update information of a VNF instance.
openstack vnflcm scale Scale a VNF instance.
openstack vnflcm change-ext-conn Change external VNF connectivity.
openstack vnflcm op rollback Rollback a VNF LCM operation occurrence.
openstack vnflcm op retry Retry a VNF LCM operation occurrence.
openstack vnflcm op fail Fail a VNF LCM operation occurrence.
openstack vnflcm op list List VNF LCM operation occurrence.
openstack vnflcm op show Show VNF LCM operation occurrence.
openstack vnflcm op cancel Cancel a VNF LCM operation occurrence.
openstack vnflcm versions Show VNF LCM API versions.
openstack vnflcm subsc create Create new subscription.
openstack vnflcm subsc delete Delete subscription.
openstack vnflcm subsc list List subscription.
openstack vnflcm subsc show Show subscription.
openstack vnf package upload Upload a VNF package by providing the address information
of the VNF package.
openstack vnf package delete Delete given VNF package(s).
[v2] --os-tacker-api-version 2
openstack vnflcm create Create a new VNF instance resource.
openstack vnflcm instantiate Instantiate a VNF instance.
openstack vnflcm list List VNF instance.
openstack vnflcm show Show VNF instance.
openstack vnflcm terminate Terminate a VNF instance.
openstack vnflcm delete Delete a VNF instance resource.
openstack vnflcm heal Heal a VNF instance.
openstack vnflcm update Update information of a VNF instance.
openstack vnflcm scale Scale a VNF instance.
openstack vnflcm change-ext-conn Change external VNF connectivity.
openstack vnflcm change-vnfpkg Change current VNF package.
openstack vnflcm op rollback Rollback a VNF LCM operation occurrence.
openstack vnflcm op retry Retry a VNF LCM operation occurrence.
openstack vnflcm op fail Fail a VNF LCM operation occurrence.
openstack vnflcm op list List VNF LCM operation occurrence.
openstack vnflcm op show Show VNF LCM operation occurrence.
openstack vnflcm versions Show VNF LCM API versions.
openstack vnflcm subsc create Create new subscription.
openstack vnflcm subsc delete Delete subscription.
openstack vnflcm subsc list List subscription.
openstack vnflcm subsc show Show subscription.
openstack vnffm alarm list List alarm.
openstack vnffm alarm show Show alarm.
openstack vnffm alarm update Update alarm.
openstack vnffm sub create Create FM subscription.
openstack vnffm sub list List FM subscription.
openstack vnffm sub show Show FM subscription.
openstack vnffm sub delete Delete FM subscription.
openstack vnfpm job create Create PM job.
openstack vnfpm job list List PM job.
openstack vnfpm job show Show PM job.
openstack vnfpm job update Update PM job.
openstack vnfpm job delete Delete PM job.
openstack vnfpm report show Show PM report.
openstack vnfpm threshold create Create PM threshold.
openstack vnfpm threshold list List PM threshold.
openstack vnfpm threshold show Show PM threshold.
openstack vnfpm threshold update Update PM threshold.
openstack vnfpm threshold delete Delete PM threshold.

View File

@@ -1,40 +1,9 @@
..
Licensed under the Apache License, Version 2.0 (the "License"); you may
not use this file except in compliance with the License. You may obtain
a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
=========
============
CLI Usage
=========
Command List
------------
============
.. toctree::
:glob:
:maxdepth: 3
commands
Operations for ETSI NFV-SOL implementation
------------------------------------------
.. toctree::
vnf_package_commands
vnflcm_commands
vnffm_commands
vnfpm_commands
Operations for Legacy implementation
------------------------------------
.. toctree::
vim_commands
*

View File

@@ -1,19 +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.
=======================
VIM Management commands
=======================
.. autoprogram-cliff:: openstack.tackerclient.v1
:command: vim *

View File

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

View File

@@ -1,28 +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.
===============
VNF FM commands
===============
VNF FM commands are CLI interface of VNF Fault Management interface in
`ETSI NFV-SOL 002 <https://www.etsi.org/deliver/etsi_gs/NFV-SOL/001_099/002/03.03.01_60/gs_NFV-SOL002v030301p.pdf>`_
and `ETSI NFV-SOL 003 <https://www.etsi.org/deliver/etsi_gs/NFV-SOL/001_099/003/03.03.01_60/gs_nfv-sol003v030301p.pdf>`_.
.. note::
Commands only support calling version 2 vnffm APIs.
You can use the commands with **\-\-os-tacker-api-version 2** to
call version 2 vnffm APIs.
.. autoprogram-cliff:: openstack.tackerclient.v2
:command: vnffm *

View File

@@ -1,34 +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.
================
VNF Lcm commands
================
VNF LCM commands are CLI interface of VNF Lifecycle Management Interface in
`ETSI NFV-SOL 002 <https://www.etsi.org/deliver/etsi_gs/NFV-SOL/001_099/002/03.03.01_60/gs_NFV-SOL002v030301p.pdf>`_
and `ETSI NFV-SOL 003 <https://www.etsi.org/deliver/etsi_gs/NFV-SOL/001_099/003/03.03.01_60/gs_nfv-sol003v030301p.pdf>`_.
.. note::
Commands call version 1 vnflcm APIs by default.
You can call the specific version of vnflcm APIs
by using the option **\-\-os-tacker-api-version**.
Commands with **\-\-os-tacker-api-version 2** call version 2 vnflcm APIs.
**vnflcm op cancel** is included in only version 1 vnflcm APIs
and **change-vnfpkg** is included in only version 2 vnflcm APIs.
.. autoprogram-cliff:: openstack.tackerclient.v1
:command: vnflcm *
.. autoprogram-cliff:: openstack.tackerclient.v2
:command: vnflcm change-vnfpkg

View File

@@ -1,28 +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.
===============
VNF PM commands
===============
VNF PM commands are CLI interface of VNF Performance Management interface in
`ETSI NFV-SOL 002 <https://www.etsi.org/deliver/etsi_gs/NFV-SOL/001_099/002/03.03.01_60/gs_NFV-SOL002v030301p.pdf>`_
and `ETSI NFV-SOL 003 <https://www.etsi.org/deliver/etsi_gs/NFV-SOL/001_099/003/03.03.01_60/gs_nfv-sol003v030301p.pdf>`_.
.. note::
Commands only support calling version 2 vnfpm APIs.
You can use the commands with **\-\-os-tacker-api-version 2** to
call version 2 vnfpm APIs.
.. autoprogram-cliff:: openstack.tackerclient.v2
:command: vnfpm *

View File

@@ -44,6 +44,7 @@ source_suffix = '.rst'
master_doc = 'index'
# General information about the project.
project = 'python-tackerclient'
copyright = 'OpenStack Contributors'
# If true, '()' will be appended to :func: etc. cross-reference text.
@@ -54,7 +55,7 @@ add_function_parentheses = True
add_module_names = True
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'native'
pygments_style = 'sphinx'
# -- Options for HTML output --------------------------------------------------
@@ -67,20 +68,21 @@ htmlhelp_basename = 'tackerclientdoc'
# 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', 'Client for Tacker API',
['OpenStack Contributors'], 1),
('cli/index', 'tacker', u'Client for Tacker API',
[u'OpenStack Contributors'], 1),
]
# -- Options for openstackdocstheme -------------------------------------------
openstackdocs_repo_name = 'openstack/python-tackerclient'
openstackdocs_bug_project = 'python-tackerclient'
openstackdocs_bug_tag = 'doc'
repository_name = 'openstack/python-tackerclient'
bug_project = 'python-tackerclient'
bug_tag = 'doc'
# -- Options for cliff.sphinxext plugin ---------------------------------------
autoprogram_cliff_application = 'openstack'
autoprogram_cliff_application = 'openstack'

View File

@@ -11,6 +11,14 @@
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
============

View File

@@ -11,6 +11,14 @@
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
===================================
@@ -34,6 +42,154 @@ For details please refer to the `OpenStack IRC meetings`_ page.
Testing
=======
For details please refer to the `Developing with OpenStackClient`_ page.
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
.. _`Developing with OpenStackClient`: https://docs.openstack.org/python-openstackclient/latest/contributor/developing.html

View File

@@ -11,6 +11,14 @@
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
=================

View File

@@ -11,12 +11,19 @@
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
=================================
Python-TackerClient is a client for OpenStack NFV MANO (Tacker) API.
It provides
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>`.
@@ -45,4 +52,5 @@ Indices and Tables
------------------
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

View File

@@ -11,53 +11,59 @@
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
============
This document describes how to install python-tackerclient.
**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.
.. note::
This installation guide contents are specific to Ubuntu distro.
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.
#. Clone python-tackerclient repository.
::
You can use -b for specific release, optionally.
$ cd ~/
$ git clone https://github.com/openstack/python-tackerclient -b <branch_name>
.. code-block:: console
$ cd ~/
$ git clone https://opendev.org/openstack/python-tackerclient -b <branch_name>
2. Install python-tackerclient.
.. note::
::
Make sure to replace the ``<branch_name>`` in command example with
specific branch name, such as ``stable/victoria``.
$ cd python-tackerclient
$ sudo python setup.py install
#. Install python-tackerclient.
.. code-block:: console
$ cd python-tackerclient
$ sudo python3 setup.py install
Using pip
=========
You can also install the latest version by using ``pip`` command:
.. code-block:: console
::
$ pip install python-tackerclient
$ pip3 install python-tackerclient
Or, if it is needed to install ``python-tackerclient`` from master branch,
type
.. code-block:: console
::
$ pip3 install git+https://opendev.org/openstack/python-tackerclient
$ pip install git+https://github.com/openstack/python-tackerclient.git

View File

@@ -11,9 +11,16 @@
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
=========
- `Tacker API reference <https://docs.openstack.org/api-ref/nfv-orchestration/>`_
- `Tacker CLI reference <https://docs.openstack.org/tacker/latest/cli/>`_
(To be updated)

View File

@@ -1,6 +0,0 @@
---
deprecations:
- |
Legacy APIs excluding VIM feature are deprecated and will be removed in the
first major release after the Tacker server version 9.0.0 (2023.1 Antelope
release).

View File

@@ -1,4 +0,0 @@
---
upgrade:
- |
Legacy APIs excluding VIM feature are obsoleted.

View File

@@ -1,6 +0,0 @@
===========================
2023.1 Series Release Notes
===========================
.. release-notes::
:branch: unmaintained/2023.1

View File

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

View File

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

View File

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

View File

@@ -53,8 +53,8 @@ source_suffix = '.rst'
master_doc = 'index'
# General information about the project.
project = 'Tacker Client Release Notes'
copyright = '2016, Tacker Developers'
project = u'Tacker Client Release Notes'
copyright = u'2016, Tacker Developers'
# Release notes are version independent.
release = ''
@@ -90,7 +90,7 @@ exclude_patterns = []
# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'native'
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
# modindex_common_prefix = []
@@ -131,6 +131,10 @@ html_theme = 'openstackdocs'
# 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
@@ -190,8 +194,8 @@ latex_elements = {
# [howto/manual]).
latex_documents = [
('index', 'TackerClientReleaseNotes.tex',
'Tacker Client Release Notes Documentation',
'Tacker Developers', 'manual'),
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
@@ -221,8 +225,8 @@ latex_documents = [
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'tackerreleasenotes',
'Tacker Client Release Notes Documentation',
['Tacker Developers'], 1)
u'Tacker Client Release Notes Documentation',
[u'Tacker Developers'], 1)
]
# If true, show URL addresses after external links.
@@ -236,8 +240,8 @@ man_pages = [
# dir menu entry, description, category)
texinfo_documents = [
('index', 'TackerClientReleaseNotes',
'Tacker Client Release Notes Documentation',
'Tacker Developers', 'TackerClientReleaseNotes',
u'Tacker Client Release Notes Documentation',
u'Tacker Developers', 'TackerClientReleaseNotes',
'Tacker Client Project.',
'Miscellaneous'),
]
@@ -255,7 +259,6 @@ texinfo_documents = [
locale_dirs = ['locale/']
# -- Options for openstackdocstheme -------------------------------------------
openstackdocs_repo_name = 'openstack/python-tackerclient'
openstackdocs_bug_project = 'python-tackerclient'
openstackdocs_bug_tag = ''
openstackdocs_auto_name = False
repository_name = 'openstack/python-tackerclient'
bug_project = 'python-tackerclient'
bug_tag = ''

View File

@@ -7,16 +7,6 @@ Contents:
:maxdepth: 2
unreleased
2024.2
2024.1
2023.2
2023.1
zed
yoga
xena
wallaby
victoria
ussuri
train
stein
rocky

View File

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

View File

@@ -1,6 +0,0 @@
=============================
Victoria Series Release Notes
=============================
.. release-notes::
:branch: unmaintained/victoria

View File

@@ -1,6 +0,0 @@
============================
Wallaby Series Release Notes
============================
.. release-notes::
:branch: unmaintained/wallaby

View File

@@ -1,6 +0,0 @@
=========================
Xena Series Release Notes
=========================
.. release-notes::
:branch: unmaintained/xena

View File

@@ -1,6 +0,0 @@
=========================
Yoga Series Release Notes
=========================
.. release-notes::
:branch: unmaintained/yoga

View File

@@ -1,6 +0,0 @@
========================
Zed Series Release Notes
========================
.. release-notes::
:branch: unmaintained/zed

View File

@@ -7,9 +7,12 @@ 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.40.0 # Apache-2.0
oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0

110
setup.cfg
View File

@@ -1,12 +1,13 @@
[metadata]
name = python-tackerclient
description = CLI and Client Library for OpenStack Tacker
long_description = file: README.rst
summary = CLI and Client Library for OpenStack Tacker
description-file =
README.rst
author = OpenStack
author_email = openstack-discuss@lists.openstack.org
url = https://docs.openstack.org/python-tackerclient/
python_requires = >=3.6
classifiers =
author-email = openstack-discuss@lists.openstack.org
home-page = https://docs.openstack.org/python-tackerclient/
python-requires = >=3.6
classifier =
Environment :: OpenStack
Intended Audience :: Developers
Intended Audience :: Information Technology
@@ -19,9 +20,6 @@ classifiers =
Programming Language :: Python :: 3
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
[files]
packages =
@@ -40,6 +38,45 @@ openstack.tackerclient.v1 =
vim_set = tackerclient.osc.v1.nfvo.vim:UpdateVIM
vim_delete = tackerclient.osc.v1.nfvo.vim:DeleteVIM
vim_show = tackerclient.osc.v1.nfvo.vim:ShowVIM
vnf_descriptor_create = tackerclient.osc.v1.vnfm.vnfd:CreateVNFD
vnf_descriptor_delete = tackerclient.osc.v1.vnfm.vnfd:DeleteVNFD
vnf_descriptor_list = tackerclient.osc.v1.vnfm.vnfd:ListVNFD
vnf_descriptor_show = tackerclient.osc.v1.vnfm.vnfd:ShowVNFD
vnf_descriptor_template_show = tackerclient.osc.v1.vnfm.vnfd:ShowTemplateVNFD
vnf_create = tackerclient.osc.v1.vnfm.vnf:CreateVNF
vnf_delete = tackerclient.osc.v1.vnfm.vnf:DeleteVNF
vnf_list = tackerclient.osc.v1.vnfm.vnf:ListVNF
vnf_show = tackerclient.osc.v1.vnfm.vnf:ShowVNF
vnf_resource_list = tackerclient.osc.v1.vnfm.vnf:ListVNFResources
vnf_set = tackerclient.osc.v1.vnfm.vnf:UpdateVNF
vnf_scale = tackerclient.osc.v1.vnfm.vnf:ScaleVNF
vnf_graph_descriptor_create = tackerclient.osc.v1.nfvo.vnffgd:CreateVNFFGD
vnf_graph_descriptor_delete = tackerclient.osc.v1.nfvo.vnffgd:DeleteVNFFGD
vnf_graph_descriptor_list = tackerclient.osc.v1.nfvo.vnffgd:ListVNFFGD
vnf_graph_descriptor_show = tackerclient.osc.v1.nfvo.vnffgd:ShowVNFFGD
vnf_graph_descriptor_template_show = tackerclient.osc.v1.nfvo.vnffgd:ShowTemplateVNFFGD
ns_descriptor_create = tackerclient.osc.v1.nfvo.nsd:CreateNSD
ns_descriptor_delete = tackerclient.osc.v1.nfvo.nsd:DeleteNSD
ns_descriptor_list = tackerclient.osc.v1.nfvo.nsd:ListNSD
ns_descriptor_show = tackerclient.osc.v1.nfvo.nsd:ShowNSD
ns_descriptor_template_show = tackerclient.osc.v1.nfvo.nsd:ShowTemplateNSD
nfv_event_show = tackerclient.osc.v1.events.events:ShowEvent
nfv_event_list = tackerclient.osc.v1.events.events:ListEvent
ns_create = tackerclient.osc.v1.nfvo.ns:CreateNS
ns_delete = tackerclient.osc.v1.nfvo.ns:DeleteNS
ns_list = tackerclient.osc.v1.nfvo.ns:ListNS
ns_show = tackerclient.osc.v1.nfvo.ns:ShowNS
vnf_graph_create = tackerclient.osc.v1.nfvo.vnffg:CreateVNFFG
vnf_graph_delete = tackerclient.osc.v1.nfvo.vnffg:DeleteVNFFG
vnf_graph_set = tackerclient.osc.v1.nfvo.vnffg:UpdateVNFFG
vnf_graph_list = tackerclient.osc.v1.nfvo.vnffg:ListVNFFG
vnf_graph_show = tackerclient.osc.v1.nfvo.vnffg:ShowVNFFG
vnf_network_forwarding_path_list = tackerclient.osc.v1.nfvo.vnffg:ListNFP
vnf_network_forwarding_path_show = tackerclient.osc.v1.nfvo.vnffg:ShowNFP
vnf_classifier_list = tackerclient.osc.v1.nfvo.vnffg:ListFC
vnf_classifier_show = tackerclient.osc.v1.nfvo.vnffg:ShowFC
vnf_chain_list = tackerclient.osc.v1.nfvo.vnffg:ListSFC
vnf_chain_show = tackerclient.osc.v1.nfvo.vnffg:ShowSFC
vnf_package_create = tackerclient.osc.v1.vnfpkgm.vnf_package:CreateVnfPackage
vnf_package_list = tackerclient.osc.v1.vnfpkgm.vnf_package:ListVnfPackage
vnf_package_show = tackerclient.osc.v1.vnfpkgm.vnf_package:ShowVnfPackage
@@ -47,7 +84,6 @@ openstack.tackerclient.v1 =
vnf_package_delete = tackerclient.osc.v1.vnfpkgm.vnf_package:DeleteVnfPackage
vnf_package_update = tackerclient.osc.v1.vnfpkgm.vnf_package:UpdateVnfPackage
vnf_package_download = tackerclient.osc.v1.vnfpkgm.vnf_package:DownloadVnfPackage
vnf_package_artifact_download = tackerclient.osc.v1.vnfpkgm.vnf_package:DownloadVnfPackageArtifact
vnflcm_create = tackerclient.osc.v1.vnflcm.vnflcm:CreateVnfLcm
vnflcm_show = tackerclient.osc.v1.vnflcm.vnflcm:ShowVnfLcm
vnflcm_list = tackerclient.osc.v1.vnflcm.vnflcm:ListVnfLcm
@@ -55,57 +91,3 @@ openstack.tackerclient.v1 =
vnflcm_terminate = tackerclient.osc.v1.vnflcm.vnflcm:TerminateVnfLcm
vnflcm_delete = tackerclient.osc.v1.vnflcm.vnflcm:DeleteVnfLcm
vnflcm_heal = tackerclient.osc.v1.vnflcm.vnflcm:HealVnfLcm
vnflcm_update = tackerclient.osc.v1.vnflcm.vnflcm:UpdateVnfLcm
vnflcm_scale = tackerclient.osc.v1.vnflcm.vnflcm:ScaleVnfLcm
vnflcm_change-ext-conn = tackerclient.osc.v1.vnflcm.vnflcm:ChangeExtConnVnfLcm
vnflcm_op_rollback = tackerclient.osc.v1.vnflcm.vnflcm_op_occs:RollbackVnfLcmOp
vnflcm_op_cancel = tackerclient.osc.v1.vnflcm.vnflcm_op_occs:CancelVnfLcmOp
vnflcm_op_fail = tackerclient.osc.v1.vnflcm.vnflcm_op_occs:FailVnfLcmOp
vnflcm_op_retry = tackerclient.osc.v1.vnflcm.vnflcm_op_occs:RetryVnfLcmOp
vnflcm_op_list = tackerclient.osc.v1.vnflcm.vnflcm_op_occs:ListVnfLcmOp
vnflcm_op_show = tackerclient.osc.v1.vnflcm.vnflcm_op_occs:ShowVnfLcmOp
vnflcm_subsc_create = tackerclient.osc.v1.vnflcm.vnflcm_subsc:CreateLccnSubscription
vnflcm_subsc_delete = tackerclient.osc.v1.vnflcm.vnflcm_subsc:DeleteLccnSubscription
vnflcm_subsc_list = tackerclient.osc.v1.vnflcm.vnflcm_subsc:ListLccnSubscription
vnflcm_subsc_show = tackerclient.osc.v1.vnflcm.vnflcm_subsc:ShowLccnSubscription
vnflcm_versions = tackerclient.osc.common.vnflcm.vnflcm_versions:VnfLcmVersions
openstack.tackerclient.v2 =
vnflcm_create = tackerclient.osc.v1.vnflcm.vnflcm:CreateVnfLcm
vnflcm_show = tackerclient.osc.v1.vnflcm.vnflcm:ShowVnfLcm
vnflcm_list = tackerclient.osc.v1.vnflcm.vnflcm:ListVnfLcm
vnflcm_instantiate = tackerclient.osc.v1.vnflcm.vnflcm:InstantiateVnfLcm
vnflcm_terminate = tackerclient.osc.v1.vnflcm.vnflcm:TerminateVnfLcm
vnflcm_change-vnfpkg = tackerclient.osc.v1.vnflcm.vnflcm:ChangeVnfPkgVnfLcm
vnflcm_delete = tackerclient.osc.v1.vnflcm.vnflcm:DeleteVnfLcm
vnflcm_heal = tackerclient.osc.v1.vnflcm.vnflcm:HealVnfLcm
vnflcm_update = tackerclient.osc.v1.vnflcm.vnflcm:UpdateVnfLcm
vnflcm_scale = tackerclient.osc.v1.vnflcm.vnflcm:ScaleVnfLcm
vnflcm_change-ext-conn = tackerclient.osc.v1.vnflcm.vnflcm:ChangeExtConnVnfLcm
vnflcm_op_rollback = tackerclient.osc.v1.vnflcm.vnflcm_op_occs:RollbackVnfLcmOp
vnflcm_op_fail = tackerclient.osc.v1.vnflcm.vnflcm_op_occs:FailVnfLcmOp
vnflcm_op_retry = tackerclient.osc.v1.vnflcm.vnflcm_op_occs:RetryVnfLcmOp
vnflcm_op_list = tackerclient.osc.v1.vnflcm.vnflcm_op_occs:ListVnfLcmOp
vnflcm_op_show = tackerclient.osc.v1.vnflcm.vnflcm_op_occs:ShowVnfLcmOp
vnflcm_subsc_create = tackerclient.osc.v1.vnflcm.vnflcm_subsc:CreateLccnSubscription
vnflcm_subsc_delete = tackerclient.osc.v1.vnflcm.vnflcm_subsc:DeleteLccnSubscription
vnflcm_subsc_list = tackerclient.osc.v1.vnflcm.vnflcm_subsc:ListLccnSubscription
vnflcm_subsc_show = tackerclient.osc.v1.vnflcm.vnflcm_subsc:ShowLccnSubscription
vnflcm_versions = tackerclient.osc.common.vnflcm.vnflcm_versions:VnfLcmVersions
vnfpm_job_create = tackerclient.osc.v2.vnfpm.vnfpm_job:CreateVnfPmJob
vnfpm_job_list = tackerclient.osc.v2.vnfpm.vnfpm_job:ListVnfPmJob
vnfpm_job_show = tackerclient.osc.v2.vnfpm.vnfpm_job:ShowVnfPmJob
vnfpm_job_update = tackerclient.osc.v2.vnfpm.vnfpm_job:UpdateVnfPmJob
vnfpm_job_delete = tackerclient.osc.v2.vnfpm.vnfpm_job:DeleteVnfPmJob
vnfpm_report_show = tackerclient.osc.v2.vnfpm.vnfpm_report:ShowVnfPmReport
vnfpm_threshold_create = tackerclient.osc.v2.vnfpm.vnfpm_threshold:CreateVnfPmThreshold
vnfpm_threshold_list = tackerclient.osc.v2.vnfpm.vnfpm_threshold:ListVnfPmThreshold
vnfpm_threshold_show = tackerclient.osc.v2.vnfpm.vnfpm_threshold:ShowVnfPmThreshold
vnfpm_threshold_update = tackerclient.osc.v2.vnfpm.vnfpm_threshold:UpdateVnfPmThreshold
vnfpm_threshold_delete = tackerclient.osc.v2.vnfpm.vnfpm_threshold:DeleteVnfPmThreshold
vnffm_alarm_list = tackerclient.osc.v2.vnffm.vnffm_alarm:ListVnfFmAlarm
vnffm_alarm_show = tackerclient.osc.v2.vnffm.vnffm_alarm:ShowVnfFmAlarm
vnffm_alarm_update = tackerclient.osc.v2.vnffm.vnffm_alarm:UpdateVnfFmAlarm
vnffm_sub_create = tackerclient.osc.v2.vnffm.vnffm_sub:CreateVnfFmSub
vnffm_sub_list = tackerclient.osc.v2.vnffm.vnffm_sub:ListVnfFmSub
vnffm_sub_show = tackerclient.osc.v2.vnffm.vnffm_sub:ShowVnfFmSub
vnffm_sub_delete = tackerclient.osc.v2.vnffm.vnffm_sub:DeleteVnfFmSub

20
tacker_test.sh Executable file
View File

@@ -0,0 +1,20 @@
#!/bin/bash
set -x
function die() {
local exitcode=$?
set +o xtrace
echo $@
exit $exitcode
}
noauth_tenant_id=me
if [ $1 == 'noauth' ]; then
NOAUTH="--tenant_id $noauth_tenant_id"
else
NOAUTH=
fi
FORMAT=" --request-format xml"
# test the CRUD of xxx
# TODO(yamahata)

View File

@@ -14,7 +14,10 @@
# under the License.
#
import json
try:
import json
except ImportError:
import simplejson as json
import logging
import os
@@ -84,8 +87,6 @@ class HTTPClient(object):
if 'body' in kwargs:
kargs['body'] = kwargs['body']
if 'content_type' in kwargs:
kargs['content_type'] = kwargs['content_type']
if self.log_credentials:
log_kargs = kargs
@@ -128,11 +129,7 @@ class HTTPClient(object):
content_type = kwargs.pop('content_type', None) or 'application/json'
headers = headers or {}
accept = kwargs.pop('accept', None)
if accept:
headers.setdefault('Accept', f'application/{accept}')
else:
headers.setdefault('Accept', content_type)
headers.setdefault('Accept', content_type)
if body:
headers.setdefault('Content-Type', content_type)
@@ -285,11 +282,7 @@ class SessionClient(adapter.Adapter):
content_type = kwargs.pop('content_type', None) or 'application/json'
headers = kwargs.setdefault('headers', {})
accept = kwargs.pop('accept', None)
if accept:
headers.setdefault('Accept', f'application/{accept}')
else:
headers.setdefault('Accept', content_type)
headers.setdefault('Accept', content_type)
try:
kwargs.setdefault('data', kwargs.pop('body'))

View File

@@ -14,8 +14,25 @@
# limitations under the License.
EXT_NS = '_extension_ns'
XML_NS_V10 = 'http://openstack.org/tacker/api/v1.0'
XSI_NAMESPACE = "http://www.w3.org/2001/XMLSchema-instance"
XSI_ATTR = "xsi:nil"
XSI_NIL_ATTR = "xmlns:xsi"
TYPE_XMLNS = "xmlns:tacker"
TYPE_ATTR = "tacker:type"
VIRTUAL_ROOT_KEY = "_v_root"
ATOM_NAMESPACE = "http://www.w3.org/2005/Atom"
ATOM_XMLNS = "xmlns:atom"
ATOM_LINK_NOTATION = "{%s}link" % ATOM_NAMESPACE
TYPE_BOOL = "bool"
TYPE_INT = "int"
TYPE_LONG = "long"
TYPE_FLOAT = "float"
TYPE_LIST = "list"
TYPE_DICT = "dict"
PLURALS = {'templates': 'template',
'devices': 'device'}

View File

@@ -111,6 +111,55 @@ HTTP_EXCEPTION_MAP = {
}
# Exceptions mapped to Tacker server exceptions
# These are defined if a user of client library needs specific exception.
# Exception name should be <Tacker Exception Name> + 'Client'
# e.g., NetworkNotFound -> NetworkNotFoundClient
class NetworkNotFoundClient(NotFound):
pass
class PortNotFoundClient(NotFound):
pass
class StateInvalidClient(BadRequest):
pass
class NetworkInUseClient(Conflict):
pass
class PortInUseClient(Conflict):
pass
class IpAddressInUseClient(Conflict):
pass
class InvalidIpForNetworkClient(BadRequest):
pass
class OverQuotaClient(Conflict):
pass
class IpAddressGenerationFailureClient(Conflict):
pass
class MacAddressInUseClient(Conflict):
pass
class ExternalIpAddressExhaustedClient(BadRequest):
pass
# Exceptions from client library
class NoAuthURLProvided(Unauthorized):
@@ -125,6 +174,11 @@ class EndpointTypeNotFound(TackerClientException):
message = _("Could not find endpoint type %(type_)s in Service Catalog.")
class AmbiguousEndpoints(TackerClientException):
message = _("Found more than one matching endpoint in Service Catalog: "
"%(matching_endpoints)")
class RequestURITooLong(TackerClientException):
"""Raised when a request fails with HTTP error 414."""
@@ -153,14 +207,6 @@ class InvalidInput(TackerClientException):
message = _("Invalid input: %(reason)s")
class EmptyInput(TackerClientException):
message = _("Empty input: %(reason)s")
class UnsupportedCommandVersion(TackerClientException):
message = _("This command is not supported in version %(version)s")
# Command line exceptions
class TackerCLIError(TackerException):

View File

@@ -14,14 +14,21 @@
# under the License.
import logging
from xml.etree import ElementTree as etree
from xml.parsers import expat
from oslo_serialization import jsonutils
import six
from tackerclient.common import constants
from tackerclient.common import exceptions as exception
from tackerclient.i18n import _
LOG = logging.getLogger(__name__)
if six.PY3:
long = int
class ActionDispatcher(object):
"""Maps method name to local methods through action name."""
@@ -51,10 +58,152 @@ class JSONDictSerializer(DictSerializer):
def default(self, data):
def sanitizer(obj):
return str(obj)
return six.text_type(obj)
return jsonutils.dumps(data, default=sanitizer)
class XMLDictSerializer(DictSerializer):
def __init__(self, metadata=None, xmlns=None):
"""XMLDictSerializer constructor.
:param metadata: information needed to deserialize XML into
a dictionary.
:param xmlns: XML namespace to include with serialized XML
"""
super(XMLDictSerializer, self).__init__()
self.metadata = metadata or {}
if not xmlns:
xmlns = self.metadata.get('xmlns')
if not xmlns:
xmlns = constants.XML_NS_V10
self.xmlns = xmlns
def default(self, data):
"""Default serializer of XMLDictSerializer.
:param data: expect data to contain a single key as XML root, or
contain another '*_links' key as atom links. Other
case will use 'VIRTUAL_ROOT_KEY' as XML root.
"""
try:
links = None
has_atom = False
if data is None:
root_key = constants.VIRTUAL_ROOT_KEY
root_value = None
else:
link_keys = [k for k in six.iterkeys(data) or []
if k.endswith('_links')]
if link_keys:
links = data.pop(link_keys[0], None)
has_atom = True
root_key = (len(data) == 1 and
list(data.keys())[0] or constants.VIRTUAL_ROOT_KEY)
root_value = data.get(root_key, data)
doc = etree.Element("_temp_root")
used_prefixes = []
self._to_xml_node(doc, self.metadata, root_key,
root_value, used_prefixes)
if links:
self._create_link_nodes(list(doc)[0], links)
return self.to_xml_string(list(doc)[0], used_prefixes, has_atom)
except AttributeError as e:
LOG.exception(str(e))
return ''
def __call__(self, data):
# Provides a migration path to a cleaner WSGI layer, this
# "default" stuff and extreme extensibility isn't being used
# like originally intended
return self.default(data)
def to_xml_string(self, node, used_prefixes, has_atom=False):
self._add_xmlns(node, used_prefixes, has_atom)
return etree.tostring(node, encoding='UTF-8')
# NOTE(ameade): the has_atom should be removed after all of the
# XML serializers and view builders have been updated to the current
# spec that required all responses include the xmlns:atom, the has_atom
# flag is to prevent current tests from breaking
def _add_xmlns(self, node, used_prefixes, has_atom=False):
node.set('xmlns', self.xmlns)
node.set(constants.TYPE_XMLNS, self.xmlns)
if has_atom:
node.set(constants.ATOM_XMLNS, constants.ATOM_NAMESPACE)
node.set(constants.XSI_NIL_ATTR, constants.XSI_NAMESPACE)
ext_ns = self.metadata.get(constants.EXT_NS, {})
for prefix in used_prefixes:
if prefix in ext_ns:
node.set('xmlns:' + prefix, ext_ns[prefix])
def _to_xml_node(self, parent, metadata, nodename, data, used_prefixes):
"""Recursive method to convert data members to XML nodes."""
result = etree.SubElement(parent, nodename)
if ":" in nodename:
used_prefixes.append(nodename.split(":", 1)[0])
# TODO(bcwaldon): accomplish this without a type-check
if isinstance(data, list):
if not data:
result.set(
constants.TYPE_ATTR,
constants.TYPE_LIST)
return result
singular = metadata.get('plurals', {}).get(nodename, None)
if singular is None:
if nodename.endswith('s'):
singular = nodename[:-1]
else:
singular = 'item'
for item in data:
self._to_xml_node(result, metadata, singular, item,
used_prefixes)
# TODO(bcwaldon): accomplish this without a type-check
elif isinstance(data, dict):
if not data:
result.set(
constants.TYPE_ATTR,
constants.TYPE_DICT)
return result
attrs = metadata.get('attributes', {}).get(nodename, {})
for k, v in sorted(data.items()):
if k in attrs:
result.set(k, str(v))
else:
self._to_xml_node(result, metadata, k, v,
used_prefixes)
elif data is None:
result.set(constants.XSI_ATTR, 'true')
else:
if isinstance(data, bool):
result.set(
constants.TYPE_ATTR,
constants.TYPE_BOOL)
elif isinstance(data, int):
result.set(
constants.TYPE_ATTR,
constants.TYPE_INT)
elif isinstance(data, long):
result.set(
constants.TYPE_ATTR,
constants.TYPE_LONG)
elif isinstance(data, float):
result.set(
constants.TYPE_ATTR,
constants.TYPE_FLOAT)
LOG.debug("Data %(data)s type is %(type)s",
{'data': data,
'type': type(data)})
result.text = six.text_type(data)
return result
def _create_link_nodes(self, xml_doc, links):
for link in links:
link_node = etree.SubElement(xml_doc, 'atom:link')
link_node.set('rel', link['rel'])
link_node.set('href', link['href'])
class TextDeserializer(ActionDispatcher):
"""Default request body deserialization."""
@@ -78,11 +227,140 @@ class JSONDeserializer(TextDeserializer):
return {'body': self._from_json(datastring)}
class XMLDeserializer(TextDeserializer):
def __init__(self, metadata=None):
"""XMLDeserializer constructor.
:param metadata: information needed to deserialize XML into
a dictionary.
"""
super(XMLDeserializer, self).__init__()
self.metadata = metadata or {}
xmlns = self.metadata.get('xmlns')
if not xmlns:
xmlns = constants.XML_NS_V10
self.xmlns = xmlns
def _get_key(self, tag):
tags = tag.split("}", 1)
if len(tags) == 2:
ns = tags[0][1:]
bare_tag = tags[1]
ext_ns = self.metadata.get(constants.EXT_NS, {})
if ns == self.xmlns:
return bare_tag
for prefix, _ns in ext_ns.items():
if ns == _ns:
return prefix + ":" + bare_tag
else:
return tag
def _get_links(self, root_tag, node):
link_nodes = node.findall(constants.ATOM_LINK_NOTATION)
root_tag = self._get_key(node.tag)
link_key = "%s_links" % root_tag
link_list = []
for link in link_nodes:
link_list.append({'rel': link.get('rel'),
'href': link.get('href')})
# Remove link node in order to avoid link node being
# processed as an item in _from_xml_node
node.remove(link)
return link_list and {link_key: link_list} or {}
def _from_xml(self, datastring):
if datastring is None:
return None
plurals = set(self.metadata.get('plurals', {}))
try:
node = etree.fromstring(datastring)
root_tag = self._get_key(node.tag)
links = self._get_links(root_tag, node)
result = self._from_xml_node(node, plurals)
# There is no case where root_tag = constants.VIRTUAL_ROOT_KEY
# and links is not None because of the way data are serialized
if root_tag == constants.VIRTUAL_ROOT_KEY:
return result
return dict({root_tag: result}, **links)
except Exception as e:
parseError = False
# Python2.7
if (hasattr(etree, 'ParseError') and
isinstance(e, getattr(etree, 'ParseError'))):
parseError = True
# Python2.6
elif isinstance(e, expat.ExpatError):
parseError = True
if parseError:
msg = _("Cannot understand XML")
raise exception.MalformedResponseBody(reason=msg)
else:
raise
def _from_xml_node(self, node, listnames):
"""Convert a minidom node to a simple Python type.
:param node: minidom node name
:param listnames: list of XML node names whose subnodes should
be considered list items.
"""
attrNil = node.get(str(etree.QName(constants.XSI_NAMESPACE, "nil")))
attrType = node.get(str(etree.QName(
self.metadata.get('xmlns'), "type")))
if (attrNil and attrNil.lower() == 'true'):
return None
elif not len(node) and not node.text:
if (attrType and attrType == constants.TYPE_DICT):
return {}
elif (attrType and attrType == constants.TYPE_LIST):
return []
else:
return ''
elif (len(node) == 0 and node.text):
converters = {constants.TYPE_BOOL:
lambda x: x.lower() == 'true',
constants.TYPE_INT:
lambda x: int(x),
constants.TYPE_LONG:
lambda x: long(x),
constants.TYPE_FLOAT:
lambda x: float(x)}
if attrType and attrType in converters:
return converters[attrType](node.text)
else:
return node.text
elif self._get_key(node.tag) in listnames:
return [self._from_xml_node(n, listnames) for n in node]
else:
result = dict()
for attr in node.keys():
if (attr == 'xmlns' or
attr.startswith('xmlns:') or
attr == constants.XSI_ATTR or
attr == constants.TYPE_ATTR):
continue
result[self._get_key(attr)] = node.get(attr)
children = list(node)
for child in children:
result[self._get_key(child.tag)] = self._from_xml_node(
child, listnames)
return result
def default(self, datastring):
return {'body': self._from_xml(datastring)}
def __call__(self, datastring):
# Adding a migration path to allow us to remove unncessary classes
return self.default(datastring)
# NOTE(maru): this class is duplicated from tacker.wsgi
class Serializer(object):
"""Serializes and deserializes dictionaries to certain MIME types."""
def __init__(self, metadata=None):
def __init__(self, metadata=None, default_xmlns=None):
"""Create a serializer based on the given WSGI environment.
'metadata' is an optional dict mapping MIME types to information
@@ -90,10 +368,12 @@ class Serializer(object):
"""
self.metadata = metadata or {}
self.default_xmlns = default_xmlns
def _get_serialize_handler(self, content_type):
handlers = {
'application/json': JSONDictSerializer()
'application/json': JSONDictSerializer(),
'application/xml': XMLDictSerializer(self.metadata),
}
try:
@@ -115,7 +395,8 @@ class Serializer(object):
def get_deserialize_handler(self, content_type):
handlers = {
'application/json': JSONDeserializer()
'application/json': JSONDeserializer(),
'application/xml': XMLDeserializer(self.metadata),
}
try:

View File

@@ -21,8 +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
from tackerclient.common import exceptions
from tackerclient.i18n import _
@@ -55,7 +57,7 @@ def get_client_class(api_name, version, version_map):
"one of: %(map_keys)s")
msg = msg % {'api_name': api_name, 'version': version,
'map_keys': ', '.join(version_map.keys())}
raise exceptions.UnsupportedVersion(message=msg)
raise exceptions.UnsupportedVersion(msg)
return importutils.import_class(client_path)
@@ -139,7 +141,7 @@ def http_log_resp(_logger, resp, body):
def _safe_encode_without_obj(data):
if isinstance(data, str):
if isinstance(data, six.string_types):
return encodeutils.safe_encode(data)
return data
@@ -176,3 +178,9 @@ 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)

View File

@@ -54,7 +54,7 @@ def validate_int_range(parsed_args, attr_name, min_value=None, max_value=None):
{'attr_name': attr_name.replace('_', '-'),
'val': val})
raise exceptions.CommandError(message=msg)
raise exceptions.CommandError(msg)
def validate_ip_subnet(parsed_args, attr_name):
@@ -65,5 +65,5 @@ def validate_ip_subnet(parsed_args, attr_name):
netaddr.IPNetwork(val)
except (netaddr.AddrFormatError, ValueError):
raise exceptions.CommandError(
message=(_('%(attr_name)s "%(val)s" is not a valid CIDR.') %
{'attr_name': attr_name.replace('_', '-'), 'val': val}))
(_('%(attr_name)s "%(val)s" is not a valid CIDR.') %
{'attr_name': attr_name.replace('_', '-'), 'val': val}))

View File

@@ -1,49 +0,0 @@
# Copyright (C) 2021 Nippon Telegraph and Telephone Corporation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from osc_lib.command import command
from tackerclient.common import exceptions
from tackerclient.i18n import _
SUPPORTED_VERSIONS = [1, 2]
class VnfLcmVersions(command.ShowOne):
_description = _("Show VnfLcm Api versions")
def get_parser(self, prog_name):
parser = super(VnfLcmVersions, self).get_parser(prog_name)
parser.add_argument(
'--major-version',
metavar="<major-version>",
type=int,
help=_('Show only specify major version.'))
return parser
def take_action(self, parsed_args):
v = None
if parsed_args.major_version:
if parsed_args.major_version not in SUPPORTED_VERSIONS:
msg = _("Major version %d is not supported")
reason = msg % parsed_args.major_version
raise exceptions.InvalidInput(reason=reason)
v = "v{}".format(parsed_args.major_version)
client = self.app.client_manager.tackerclient
data = client.show_vnf_lcm_versions(v)
return (tuple(data.keys()), tuple(data.values()))

View File

@@ -26,17 +26,15 @@ API_NAME = 'tackerclient'
API_VERSION_OPTION = 'os_tacker_api_version'
API_VERSIONS = {
'1': 'tackerclient.v1_0.client.Client',
'2': 'tackerclient.v1_0.client.Client',
}
def make_client(instance):
"""Returns a client to the ClientManager."""
api_version = instance._api_version[API_NAME]
tacker_client = utils.get_client_class(
API_NAME,
api_version,
instance._api_version[API_NAME],
API_VERSIONS)
LOG.debug('Instantiating tacker client: %s', tacker_client)
@@ -44,8 +42,7 @@ def make_client(instance):
'region_name': instance._region_name,
'endpoint_type': instance._interface,
'interface': instance._interface,
'session': instance.session,
'api_version': api_version
'session': instance.session
}
client = tacker_client(**kwargs)

View File

@@ -20,9 +20,7 @@ Stuffs specific to tackerclient OSC plugin should not be added
to this module. They should go to tackerclient.osc.v1.utils.
"""
import json
import operator
import os
from cliff import columns as cliff_columns
from keystoneclient import exceptions as identity_exc
@@ -31,7 +29,6 @@ from keystoneclient.v3 import projects
from osc_lib import utils
from oslo_serialization import jsonutils
from tackerclient.common import exceptions
from tackerclient.i18n import _
@@ -215,29 +212,3 @@ class FormatComplexDataColumn(cliff_columns.FormattableColumn):
def human_readable(self):
return format_dict_with_indention(self._value)
def jsonfile2body(file_path):
if file_path is None:
msg = _("File %s does not exist")
reason = msg % file_path
raise exceptions.InvalidInput(reason=reason)
if os.access(file_path, os.R_OK) is False:
msg = _("User does not have read privileges to it")
raise exceptions.InvalidInput(reason=msg)
try:
with open(file_path) as f:
body = json.load(f)
except (IOError, ValueError) as ex:
msg = _("Failed to load parameter file. Error: %s")
reason = msg % ex
raise exceptions.InvalidInput(reason=reason)
if not body:
reason = _('The parameter file is empty')
raise exceptions.EmptyInput(reason=reason)
return body

View File

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

@@ -0,0 +1,254 @@
# Copyright 2018 OpenStack Foundation.
# All Rights Reserved
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
import yaml
from osc_lib.command import command
from osc_lib import utils
from tackerclient.common import exceptions
from tackerclient.i18n import _
from tackerclient.osc import sdk_utils
from tackerclient.osc import utils as tacker_osc_utils
from tackerclient.tacker import v1_0 as tackerV10
_attr_map = (
('id', 'ID', tacker_osc_utils.LIST_BOTH),
('name', 'Name', tacker_osc_utils.LIST_BOTH),
('nsd_id', 'NSD ID', tacker_osc_utils.LIST_BOTH),
('vnf_ids', 'VNF IDs', tacker_osc_utils.LIST_BOTH),
('vnffg_ids', 'VNFFG IDs', tacker_osc_utils.LIST_BOTH),
('mgmt_ip_addresses', 'Mgmt Ip Addresses', tacker_osc_utils.LIST_BOTH),
('status', 'Status', tacker_osc_utils.LIST_BOTH),
)
_NS = 'ns'
_RESOURCE = 'resource'
def _get_columns(item):
column_map = {
'tenant_id': 'project_id',
}
return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map)
class CreateNS(command.ShowOne):
_description = _("Create a new NS")
def get_parser(self, prog_name):
parser = super(CreateNS, self).get_parser(prog_name)
parser.add_argument(
'name', metavar='NAME',
help=_('Name for NS'))
parser.add_argument(
'--tenant-id', metavar='TENANT_ID',
help=_('The owner tenant ID'))
parser.add_argument(
'--description',
help=_('Set description for the NS'))
nsd_group = parser.add_mutually_exclusive_group(required=True)
nsd_group.add_argument(
'--nsd-id',
help=_('NSD ID to use as template to create NS'))
nsd_group.add_argument(
'--nsd-template',
help=_('NSD file to create NS'))
nsd_group.add_argument(
'--nsd-name',
help=_('NSD name to use as template to create NS'))
vim_group = parser.add_mutually_exclusive_group()
vim_group.add_argument(
'--vim-id',
help=_('VIM ID to use to create NS on the specified VIM'))
vim_group.add_argument(
'--vim-name',
help=_('VIM name to use to create NS on the specified VIM'))
parser.add_argument(
'--vim-region-name',
help=_('VIM Region to use to create NS on the specified VIM'))
parser.add_argument(
'--param-file',
help=_('Specify parameter YAML file'))
return parser
def args2body(self, parsed_args):
body = {_NS: {}}
body[_NS]['attributes'] = {}
if parsed_args.vim_region_name:
body[_NS].setdefault('placement_attr', {})['region_name'] = \
parsed_args.vim_region_name
client = self.app.client_manager.tackerclient
if parsed_args.vim_name:
_id = tackerV10.find_resourceid_by_name_or_id(client, 'vim',
parsed_args.vim_name)
parsed_args.vim_id = _id
if parsed_args.nsd_name:
_id = tackerV10.find_resourceid_by_name_or_id(client, 'nsd',
parsed_args.nsd_name)
parsed_args.nsd_id = _id
elif parsed_args.nsd_template:
with open(parsed_args.nsd_template) as f:
template = f.read()
try:
template = yaml.load(
template, Loader=yaml.SafeLoader)
except yaml.YAMLError as e:
raise exceptions.InvalidInput(e)
if not template:
raise exceptions.InvalidInput('The nsd file is empty')
body[_NS]['nsd_template'] = template
if parsed_args.param_file:
with open(parsed_args.param_file) as f:
param_yaml = f.read()
try:
param_yaml = yaml.load(
param_yaml, Loader=yaml.SafeLoader)
except yaml.YAMLError as e:
raise exceptions.InvalidInput(e)
if not param_yaml:
raise exceptions.InvalidInput('The parameter file is empty')
body[_NS]['attributes'] = {'param_values': param_yaml}
tackerV10.update_dict(parsed_args, body[_NS],
['tenant_id', 'name', 'description',
'nsd_id', 'vim_id'])
return body
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
ns = client.create_ns(self.args2body(parsed_args))
display_columns, columns = _get_columns(ns[_NS])
data = utils.get_item_properties(
sdk_utils.DictModel(ns[_NS]),
columns)
lstdata = list(data)
for index, value in enumerate(lstdata):
if value is None:
lstdata[index] = ''
data = tuple(lstdata)
return (display_columns, data)
class DeleteNS(command.Command):
_description = _("Delete NS(s).")
def get_parser(self, prog_name):
parser = super(DeleteNS, self).get_parser(prog_name)
parser.add_argument(
_NS,
metavar="<NS>",
nargs="+",
help=_("NS(s) to delete (name or ID)")
)
parser.add_argument(
'--force',
default=False,
action='store_true',
help=_('Force delete Network Service')
)
return parser
def args2body(self, parsed_args):
if parsed_args.force:
body = {_NS: {'attributes': {'force': True}}}
else:
body = dict()
return body
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
failure = False
deleted_ids = []
failed_items = {}
body = self.args2body(parsed_args)
for resource_id in parsed_args.ns:
try:
obj = tackerV10.find_resourceid_by_name_or_id(
client, _NS, resource_id)
client.delete_ns(obj, body)
deleted_ids.append(resource_id)
except Exception as e:
failure = True
failed_items[resource_id] = e
if failure:
msg = ''
if deleted_ids:
msg = (_('Successfully deleted %(resource)s(s):'
' %(deleted_list)s') % {'deleted_list':
', '.join(deleted_ids),
'resource': _NS})
err_msg = _("\n\nUnable to delete the below"
" %s(s):") % _NS
for failed_id, error in failed_items.iteritems():
err_msg += (_('\n Cannot delete %(failed_id)s: %(error)s')
% {'failed_id': failed_id,
'error': error})
msg += err_msg
raise exceptions.CommandError(msg)
else:
print((_('All specified %(resource)s(s) deleted successfully')
% {'resource': _NS}))
return
class ListNS(command.Lister):
_description = ("List (NS)s that belong to a given tenant.")
def get_parser(self, prog_name):
parser = super(ListNS, self).get_parser(prog_name)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
data = client.list_nss()
headers, columns = tacker_osc_utils.get_column_definitions(
_attr_map, long_listing=None)
return (headers,
(utils.get_dict_properties(
s, columns,
) for s in data[_NS + 's']))
class ShowNS(command.ShowOne):
_description = _("Display NS details")
def get_parser(self, prog_name):
parser = super(ShowNS, self).get_parser(prog_name)
parser.add_argument(
_NS,
metavar="<NS>",
help=_("NS to display (name or ID)")
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
obj_id = tackerV10.find_resourceid_by_name_or_id(
client, _NS, parsed_args.ns)
obj = client.show_ns(obj_id)
display_columns, columns = _get_columns(obj[_NS])
data = utils.get_item_properties(
sdk_utils.DictModel(obj[_NS]),
columns)
lstdata = list(data)
for index, value in enumerate(lstdata):
if value is None:
lstdata[index] = ''
data = tuple(lstdata)
return (display_columns, data)

View File

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

@@ -124,7 +124,7 @@ class CreateVIM(command.ShowOne):
config_param = yaml.load(vim_config,
Loader=yaml.SafeLoader)
except yaml.YAMLError as e:
raise exceptions.InvalidInput(reason=e)
raise exceptions.InvalidInput(e)
vim_obj = body[_VIM]
try:
auth_url = config_param.pop('auth_url')
@@ -185,12 +185,12 @@ class DeleteVIM(command.Command):
'resource': _VIM})
err_msg = _("\n\nUnable to delete the below"
" %s(s):") % _VIM
for failed_id, error in failed_items.items():
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(message=msg)
raise exceptions.CommandError(msg)
else:
print((_('All specified %(resource)s(s) deleted successfully')
% {'resource': _VIM}))
@@ -230,10 +230,9 @@ class UpdateVIM(command.ShowOne):
with open(parsed_args.config_file) as f:
config_yaml = f.read()
try:
config_param = yaml.load(config_yaml,
Loader=yaml.SafeLoader)
config_param = yaml.load(config_yaml)
except yaml.YAMLError as e:
raise exceptions.InvalidInput(reason=e)
raise exceptions.InvalidInput(e)
vim_obj = body[_VIM]
if config_param is not None:
vim_utils.args2body_vim(config_param, vim_obj)

View File

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

@@ -0,0 +1,216 @@
# 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,69 +0,0 @@
{
"extVirtualLinks": [
{
"id": "ext-vl-uuid-VL1",
"resourceId": "neutron-network-uuid_VL1",
"extCps": [
{
"cpdId": "CP1",
"cpConfig": [
{
"cpProtocolData": [
{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
"ipAddresses": [
{
"type": "IPV4",
"numDynamicAddresses": 1,
"subnetId": "subnet-uuid"
}
]
}
}
]
}
]
},
{
"cpdId": "CP2",
"cpConfig": [
{
"cpProtocolData": [
{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
"ipAddresses": [
{
"type": "IPV4",
"fixedAddresses": [
"10.0.0.1"
],
"subnetId": "subnet-uuid"
}
]
}
}
]
}
]
}
]
}
],
"vimConnectionInfo": [
{
"id": "vim-uuid",
"vimType": "ETSINFV.OPENSTACK_KEYSTONE.v_2",
"vimConnectionId": "dummy-vimid",
"interfaceInfo": {
"key1":"value1",
"key2":"value2"
},
"accessInfo": {
"key1":"value1",
"key2":"value2"
}
}
]
}

View File

@@ -1,114 +0,0 @@
{
"filter": {
"vnfInstanceSubscriptionFilter": {
"vnfdIds": [
"dummy-vnfdId-1",
"dummy-vnfdId-2"
],
"vnfProductsFromProviders": [
{
"vnfProvider": "dummy-vnfProvider-1",
"vnfProducts": [
{
"vnfProductName": "dummy-vnfProductName-1-1",
"versions": [
{
"vnfSoftwareVersion": "1.0",
"vnfdVersions": ["1.0", "2.0"]
},
{
"vnfSoftwareVersion": "1.1",
"vnfdVersions": ["1.1", "2.1"]
}
]
},
{
"vnfProductName": "dummy-vnfProductName-1-2",
"versions": [
{
"vnfSoftwareVersion": "1.0",
"vnfdVersions": ["1.0", "2.0"]
},
{
"vnfSoftwareVersion": "1.1",
"vnfdVersions": ["1.1", "2.1"]
}
]
}
]
},
{
"vnfProvider": "dummy-vnfProvider-2",
"vnfProducts": [
{
"vnfProductName": "dummy-vnfProductName-2-1",
"versions": [
{
"vnfSoftwareVersion": "1.0",
"vnfdVersions": ["1.0", "2.0"]
},
{
"vnfSoftwareVersion": "1.1",
"vnfdVersions": ["1.1", "2.1"]
}
]
},
{
"vnfProductName": "dummy-vnfProductName-2-2",
"versions": [
{
"vnfSoftwareVersion": "1.0",
"vnfdVersions": ["1.0", "2.0"]
},
{
"vnfSoftwareVersion": "1.1",
"vnfdVersions": ["1.1", "2.1"]
}
]
}
]
}
],
"vnfInstanceIds": [
"dummy-vnfInstanceId-1",
"dummy-vnfInstanceId-2"
],
"vnfInstanceNames": [
"dummy-vnfInstanceName-1",
"dummy-vnfInstanceName-2"
]
},
"notificationTypes": [
"VnfLcmOperationOccurrenceNotification",
"VnfIdentifierCreationNotification",
"VnfIdentifierDeletionNotification"
],
"operationTypes": [
"INSTANTIATE",
"SCALE",
"TERMINATE",
"HEAL",
"MODIFY_INFO",
"CHANGE_EXT_CONN"
],
"operationStates": [
"COMPLETED",
"FAILED",
"FAILED_TEMP",
"PROCESSING",
"ROLLING_BACK",
"ROLLED_BACK",
"STARTING"
]
},
"callbackUri": "http://localhost:9990/notification/callback/test",
"authentication": {
"authType": [
"BASIC"
],
"paramsBasic": {
"password": "test_pass",
"userName": "test_user"
}
}
}

View File

@@ -1,3 +0,0 @@
{
"additionalParams": {"all": true}
}

View File

@@ -1,3 +0,0 @@
{
"additionalParams": {"key1":"value1", "key2":"value2"}
}

View File

@@ -1,5 +0,0 @@
{
"vnfInstanceName": "sample",
"vnfInstanceDescription" : "sample_description",
"vnfdId" : "sample_id"
}

View File

@@ -13,7 +13,9 @@
# License for the specific language governing permissions and limitations
# under the License.
import json
import logging
import os
import time
from osc_lib.command import command
@@ -39,8 +41,7 @@ LOG = logging.getLogger(__name__)
_mixed_case_fields = ('vnfInstanceName', 'vnfInstanceDescription', 'vnfdId',
'vnfProvider', 'vnfProductName', 'vnfSoftwareVersion',
'vnfdVersion', 'instantiationState',
'vimConnectionInfo', 'instantiatedVnfInfo',
'vnfConfigurableProperties', 'vnfPkgId')
'vimConnectionInfo', 'instantiatedVnfInfo')
_VNF_INSTANCE = 'vnf_instance'
@@ -67,7 +68,6 @@ def _get_columns(vnflcm_obj, action="/?originalUrl=https%3A%2F%2Fgit.openstack.org%2FNone)%3A%253C%2Fcode">
'vnfdVersion': 'VNFD Version',
'instantiationState': 'Instantiation State',
'_links': 'Links',
'vnfConfigurableProperties': 'VNF Configurable Properties'
}
if action == 'show':
if vnflcm_obj['instantiationState'] == 'INSTANTIATED':
@@ -78,12 +78,6 @@ def _get_columns(vnflcm_obj, action="/?originalUrl=https%3A%2F%2Fgit.openstack.org%2FNone)%3A%253C%2Fcode">
{'vimConnectionInfo': 'VIM Connection Info',
'_links': 'Links'}
)
# Note: To prevent it from appearing in the v2 API output,
# the 'VNF Package ID' will be output only if the vnfPkgId exists.
if 'vnfPkgId' in vnflcm_obj:
column_map.update(
{'vnfPkgId': 'VNF Package ID'}
)
return sdk_utils.get_osc_show_columns_for_sdk_resource(vnflcm_obj,
column_map)
@@ -117,7 +111,7 @@ class CreateVnfLcm(command.ShowOne):
body = {}
if file_path:
return tacker_osc_utils.jsonfile2body(file_path)
return instantiate_vnf_args2body(file_path)
body['vnfdId'] = parsed_args.vnfd_id
@@ -188,6 +182,26 @@ class ListVnfLcm(command.Lister):
) for s in vnf_instances))
def instantiate_vnf_args2body(file_path):
if file_path is not None and os.access(file_path, os.R_OK) is False:
msg = _("File %s does not exist or user does not have read "
"privileges to it")
raise exceptions.InvalidInput(msg % file_path)
try:
with open(file_path) as f:
body = json.load(f)
except (IOError, ValueError) as ex:
msg = _("Failed to load parameter file. Error: %s")
raise exceptions.InvalidInput(msg % ex)
if not body:
raise exceptions.InvalidInput(_('The parameter file is empty'))
return body
class InstantiateVnfLcm(command.Command):
_description = _("Instantiate a VNF Instance")
@@ -207,7 +221,7 @@ class InstantiateVnfLcm(command.Command):
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
result = client.instantiate_vnf_instance(
parsed_args.vnf_instance, tacker_osc_utils.jsonfile2body(
parsed_args.vnf_instance, instantiate_vnf_args2body(
parsed_args.instantiation_request_file))
if not result:
print((_('Instantiate request for VNF Instance %(id)s has been'
@@ -219,12 +233,6 @@ class HealVnfLcm(command.Command):
def get_parser(self, prog_name):
parser = super(HealVnfLcm, self).get_parser(prog_name)
usage_message = ('''%(prog)s [-h] [--cause CAUSE]
[--vnfc-instance <vnfc-instance-id> '''
'''[<vnfc-instance-id> ...]]
[--additional-param-file <additional-param-file>]
-- <vnf-instance>''')
parser.usage = usage_message
parser.add_argument(
_VNF_INSTANCE,
metavar="<vnf-instance>",
@@ -238,11 +246,6 @@ class HealVnfLcm(command.Command):
nargs="+",
help=_("List of VNFC instances requiring a healing action.")
)
parser.add_argument(
'--additional-param-file',
metavar="<additional-param-file>",
help=_("Additional parameters passed by the NFVO as input "
"to the healing process."))
return parser
def args2body(self, parsed_args):
@@ -251,9 +254,6 @@ class HealVnfLcm(command.Command):
body['cause'] = parsed_args.cause
if parsed_args.vnfc_instance:
body['vnfcInstanceId'] = parsed_args.vnfc_instance
if parsed_args.additional_param_file:
body.update(tacker_osc_utils.jsonfile2body(
parsed_args.additional_param_file))
return body
@@ -304,7 +304,7 @@ class TerminateVnfLcm(command.Command):
if parsed_args.graceful_termination_timeout:
if parsed_args.termination_type == 'FORCEFUL':
exceptions.InvalidInput(reason='--graceful-termination-timeout'
exceptions.InvalidInput('--graceful-termination-timeout'
' argument is invalid for "FORCEFUL"'
' termination')
body['gracefulTerminationTimeout'] = parsed_args.\
@@ -329,7 +329,7 @@ class TerminateVnfLcm(command.Command):
result = client.delete_vnf_instance(parsed_args.vnf_instance)
if not result:
print(_("VNF Instance '%(id)s' is deleted successfully") %
print(_("VNF Instance '%(id)s' deleted successfully") %
{'id': parsed_args.vnf_instance})
def _wait_until_vnf_is_terminated(self, client, vnf_instance_id,
@@ -355,8 +355,8 @@ class TerminateVnfLcm(command.Command):
msg = _("Couldn't verify vnf instance is terminated within "
"'%(timeout)s' seconds. Unable to delete vnf instance "
"%(id)s")
raise exceptions.CommandError(
message=msg % {'timeout': timeout, 'id': vnf_instance_id})
raise exceptions.CommandError(msg % {'timeout': timeout,
'id': vnf_instance_id})
time.sleep(SLEEP_TIME)
@@ -396,196 +396,11 @@ class DeleteVnfLcm(command.Command):
msg = (_("Failed to delete %(error_count)s of %(total)s "
"vnf instances.") % {'error_count': error_count,
'total': total})
raise exceptions.CommandError(message=msg)
raise exceptions.CommandError(msg)
else:
if total > 1:
print(_('All specified vnf instances are deleted '
'successfully'))
else:
print(_("Vnf instance '%s' is deleted "
print(_("Vnf instance '%s' deleted "
"successfully") % vnf_instances[0])
class UpdateVnfLcm(command.Command):
_description = _("Update VNF Instance")
def get_parser(self, prog_name):
"""Add arguments to parser.
Args:
prog_name ([string]): program name
Returns:
parser([ArgumentParser]): [description]
"""
parser = super(UpdateVnfLcm, self).get_parser(prog_name)
parser.add_argument(
_VNF_INSTANCE,
metavar="<vnf-instance>",
help=_('VNF instance ID to update.'))
parser.add_argument(
'--I',
metavar="<param-file>",
help=_("Specify update request parameters in a json file."))
return parser
def args2body(self, file_path=None):
"""Call jsonfile2body to store request body to body(dict)
Args:
file_path ([string], optional): file path of param file(json).
Defaults to None.
Returns:
body ([dict]): Request body is stored
"""
body = {}
if file_path:
return tacker_osc_utils.jsonfile2body(file_path)
return body
def take_action(self, parsed_args):
"""Execute update_vnf_instance and output result comment
Args:
parsed_args ([Namespace]): [description]
"""
client = self.app.client_manager.tackerclient
if parsed_args.I:
# Update VNF instance.
result = client.update_vnf_instance(
parsed_args.vnf_instance,
self.args2body(file_path=parsed_args.I))
if not result:
print((_('Update vnf:%(id)s ') %
{'id': parsed_args.vnf_instance}))
class ScaleVnfLcm(command.Command):
_description = _("Scale a VNF Instance")
def get_parser(self, prog_name):
parser = super(ScaleVnfLcm, self).get_parser(prog_name)
parser.add_argument(
_VNF_INSTANCE,
metavar="<vnf-instance>",
help=_('VNF instance ID to scale'))
parser.add_argument(
'--number-of-steps',
metavar="<number-of-steps>",
type=int,
help=_("Number of scaling steps to be executed as part of "
"this Scale VNF operation."))
parser.add_argument(
'--additional-param-file',
metavar="<additional-param-file>",
help=_("Additional parameters passed by the NFVO as input "
"to the scaling process."))
scale_require_parameters = parser.add_argument_group(
"require arguments"
)
scale_require_parameters.add_argument(
'--type',
metavar="<type>",
required=True,
choices=['SCALE_OUT', 'SCALE_IN'],
help=_("SCALE_OUT or SCALE_IN for type of scale operation."))
scale_require_parameters.add_argument(
'--aspect-id',
required=True,
metavar="<aspect-id>",
help=_("Identifier of the scaling aspect."))
return parser
def args2body(self, parsed_args):
"""To store request body, call jsonfile2body.
Args:
parsed_args ([Namespace]): arguments of CLI.
Returns:
body ([dict]): Request body is stored
"""
body = {'type': parsed_args.type, 'aspectId': parsed_args.aspect_id}
if parsed_args.number_of_steps:
body['numberOfSteps'] = parsed_args.number_of_steps
if parsed_args.additional_param_file:
body.update(tacker_osc_utils.jsonfile2body(
parsed_args.additional_param_file))
return body
def take_action(self, parsed_args):
"""Execute scale_vnf_instance and output result comment.
Args:
parsed_args ([Namespace]): arguments of CLI.
"""
client = self.app.client_manager.tackerclient
result = client.scale_vnf_instance(
parsed_args.vnf_instance,
self.args2body(parsed_args))
if not result:
print((_('Scale request for VNF Instance %s has been accepted.')
% parsed_args.vnf_instance))
class ChangeExtConnVnfLcm(command.Command):
_description = _("Change External VNF Connectivity")
def get_parser(self, prog_name):
parser = super(ChangeExtConnVnfLcm, self).get_parser(prog_name)
parser.add_argument(
_VNF_INSTANCE,
metavar="<vnf-instance>",
help=_("VNF instance ID to Change External VNF Connectivity"))
parser.add_argument(
'request_file',
metavar="<param-file>",
help=_("Specify change-ext-conn request parameters "
"in a json file."))
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
result = client.change_ext_conn_vnf_instance(
parsed_args.vnf_instance, tacker_osc_utils.jsonfile2body(
parsed_args.request_file))
if not result:
print((_('Change External VNF Connectivity for VNF Instance %s '
'has been accepted.') % parsed_args.vnf_instance))
class ChangeVnfPkgVnfLcm(command.Command):
_description = _("Change Current VNF Package")
def get_parser(self, prog_name):
parser = super(ChangeVnfPkgVnfLcm, self).get_parser(prog_name)
parser.add_argument(
_VNF_INSTANCE,
metavar="<vnf-instance>",
help=_("VNF instance ID to Change Current VNF Package"))
parser.add_argument(
'request_file',
metavar="<param-file>",
help=_("Specify change-vnfpkg request parameters "
"in a json file."))
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
result = client.change_vnfpkg_vnf_instance(
parsed_args.vnf_instance, tacker_osc_utils.jsonfile2body(
parsed_args.request_file))
if not result:
print((_('Change Current VNF Package for VNF Instance %s '
'has been accepted.') % parsed_args.vnf_instance))

View File

@@ -1,353 +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 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
_VNF_LCM_OP_OCC_ID = 'vnf_lcm_op_occ_id'
_MIXED_CASE_FIELDS = ['operationState', 'stateEnteredTime', 'startTime',
'vnfInstanceId', 'grantId', 'isAutomaticInvocation',
'isCancelPending', 'cancelMode', 'operationParams',
'resourceChanges', 'changedInfo',
'changedExtConnectivity']
_FORMATTERS = {
'operationParams': tacker_osc_utils.FormatComplexDataColumn,
'error': tacker_osc_utils.FormatComplexDataColumn,
'resourceChanges': tacker_osc_utils.FormatComplexDataColumn,
'changedInfo': tacker_osc_utils.FormatComplexDataColumn,
'changedExtConnectivity': tacker_osc_utils.FormatComplexDataColumn,
'_links': tacker_osc_utils.FormatComplexDataColumn
}
_ATTR_MAP = (
('id', 'id', tacker_osc_utils.LIST_BOTH),
('operationState', 'operationState', tacker_osc_utils.LIST_BOTH),
('vnfInstanceId', 'vnfInstanceId', tacker_osc_utils.LIST_BOTH),
('operation', 'operation', tacker_osc_utils.LIST_BOTH)
)
def _get_columns(vnflcm_op_occ_obj, action=None):
column_map = {
'id': 'ID',
'operationState': 'Operation State',
'stateEnteredTime': 'State Entered Time',
'startTime': 'Start Time',
'vnfInstanceId': 'VNF Instance ID',
'operation': 'Operation',
'isAutomaticInvocation': 'Is Automatic Invocation',
'isCancelPending': 'Is Cancel Pending',
'error': 'Error',
'_links': 'Links'
}
if action == 'show':
column_map.update(
{'operationParams': 'Operation Parameters',
'grantId': 'Grant ID',
'resourceChanges': 'Resource Changes',
'changedInfo': 'Changed Info',
'cancelMode': 'Cancel Mode',
'changedExtConnectivity': 'Changed External Connectivity'}
)
return sdk_utils.get_osc_show_columns_for_sdk_resource(vnflcm_op_occ_obj,
column_map)
class RollbackVnfLcmOp(command.Command):
def get_parser(self, prog_name):
"""Add arguments to parser.
Args:
prog_name ([type]): program name
Returns:
parser([ArgumentParser]):
"""
parser = super(RollbackVnfLcmOp, self).get_parser(prog_name)
parser.add_argument(
_VNF_LCM_OP_OCC_ID,
metavar="<vnf-lcm-op-occ-id>",
help=_('VNF lifecycle management operation occurrence ID.'))
return parser
def take_action(self, parsed_args):
"""Execute rollback_vnf_instance and output comment.
Args:
parsed_args ([Namespace]): arguments of CLI.
"""
client = self.app.client_manager.tackerclient
result = client.rollback_vnf_instance(parsed_args.vnf_lcm_op_occ_id)
if not result:
print((_('Rollback request for LCM operation %(id)s has been'
' accepted') % {'id': parsed_args.vnf_lcm_op_occ_id}))
class CancelVnfLcmOp(command.ShowOne):
_description = _("Cancel VNF Instance")
def get_parser(self, prog_name):
"""Add arguments to parser.
Args:
prog_name ([type]): program name
Returns:
parser([ArgumentParser]):
"""
parser = super(CancelVnfLcmOp, self).get_parser(prog_name)
parser.add_argument(
_VNF_LCM_OP_OCC_ID,
metavar="<vnf-lcm-op-occ-id>",
help=_('VNF lifecycle management operation occurrence ID.'))
parser.add_argument(
"--cancel-mode",
default='GRACEFUL',
metavar="<cancel-mode>",
choices=['GRACEFUL', 'FORCEFUL'],
help=_("Cancel mode can be 'GRACEFUL' or 'FORCEFUL'. "
"Default is 'GRACEFUL'"))
return parser
def take_action(self, parsed_args):
"""Execute cancel_vnf_instance and output comment.
Args:
parsed_args ([Namespace]): arguments of CLI.
"""
client = self.app.client_manager.tackerclient
result = client.cancel_vnf_instance(
parsed_args.vnf_lcm_op_occ_id,
{'cancelMode': parsed_args.cancel_mode})
if not result:
print((_('Cancel request for LCM operation %(id)s has been'
' accepted') % {'id': parsed_args.vnf_lcm_op_occ_id}))
class FailVnfLcmOp(command.ShowOne):
_description = _("Fail VNF Instance")
def get_parser(self, prog_name):
"""Add arguments to parser.
Args:
prog_name ([type]): program name
Returns:
parser([ArgumentParser]):
"""
parser = super(FailVnfLcmOp, self).get_parser(prog_name)
parser.add_argument(
_VNF_LCM_OP_OCC_ID,
metavar="<vnf-lcm-op-occ-id>",
help=_('VNF lifecycle management operation occurrence ID.'))
return parser
def take_action(self, parsed_args):
"""Execute fail_vnf_instance and output response.
Args:
parsed_args ([Namespace]): arguments of CLI.
"""
client = self.app.client_manager.tackerclient
obj = client.fail_vnf_instance(parsed_args.vnf_lcm_op_occ_id)
display_columns, columns = _get_columns(obj)
data = utils.get_item_properties(
sdk_utils.DictModel(obj),
columns, formatters=_FORMATTERS,
mixed_case_fields=_MIXED_CASE_FIELDS)
return (display_columns, data)
class RetryVnfLcmOp(command.Command):
_description = _("Retry VNF Instance")
def get_parser(self, prog_name):
"""Add arguments to parser.
Args:
prog_name ([type]): program name
Returns:
parser([ArgumentParser]):
"""
parser = super(RetryVnfLcmOp, self).get_parser(prog_name)
parser.add_argument(
_VNF_LCM_OP_OCC_ID,
metavar="<vnf-lcm-op-occ-id>",
help=_('VNF lifecycle management operation occurrence ID.'))
return parser
def take_action(self, parsed_args):
"""Execute retry_vnf_instance and output comment.
Args:
parsed_args ([Namespace]): arguments of CLI.
"""
client = self.app.client_manager.tackerclient
result = client.retry_vnf_instance(parsed_args.vnf_lcm_op_occ_id)
if not result:
print((_('Retry request for LCM operation %(id)s has been'
' accepted') % {'id': parsed_args.vnf_lcm_op_occ_id}))
class ListVnfLcmOp(command.Lister):
_description = _("List LCM Operation Occurrences")
def get_parser(self, program_name):
"""Add arguments to parser.
Args:
program_name ([type]): program name
Returns:
parser([ArgumentParser]):
"""
parser = super(ListVnfLcmOp, self).get_parser(program_name)
parser.add_argument(
"--filter",
metavar="<filter>",
help=_("Attribute-based-filtering parameters"),
)
fields_exclusive_group = parser.add_mutually_exclusive_group(
required=False)
fields_exclusive_group.add_argument(
"--fields",
metavar="<fields>",
help=_("Complex attributes to be included into the response"),
)
fields_exclusive_group.add_argument(
"--exclude-fields",
metavar="<exclude-fields>",
help=_("Complex attributes to be excluded from the response"),
)
return parser
def get_attributes(self, exclude=None):
"""Get attributes.
Args:
exclude([exclude]): a list of fields which needs to exclude.
Returns:
attributes([attributes]): a list of table entry definitions.
Each entry should be a tuple consisting of
(API attribute name, header name, listing mode).
"""
fields = [
{
"key": "id",
"value": "ID"
},
{
"key": "operationState",
"value": "Operation State"
},
{
"key": "vnfInstanceId",
"value": "VNF Instance ID"
},
{
"key": "operation",
"value": "Operation"
}
]
attributes = []
if exclude is None:
exclude = []
for field in fields:
if field['value'] not in exclude:
attributes.extend([(field['key'], field['value'],
tacker_osc_utils.LIST_BOTH)])
return tuple(attributes)
def take_action(self, parsed_args):
"""Execute list_vnflcm_op_occs and output response.
Args:
parsed_args ([Namespace]): arguments of CLI.
"""
params = {}
exclude_fields = []
extra_fields = []
if parsed_args.filter:
params['filter'] = parsed_args.filter
if parsed_args.fields:
params['fields'] = parsed_args.fields
fields = parsed_args.fields.split(',')
for field in fields:
extra_fields.append(field.split('/')[0])
if parsed_args.exclude_fields:
params['exclude-fields'] = parsed_args.exclude_fields
fields = parsed_args.exclude_fields.split(',')
exclude_fields.extend(fields)
client = self.app.client_manager.tackerclient
vnflcm_op_occs = client.list_vnf_lcm_op_occs(**params)
headers, columns = tacker_osc_utils.get_column_definitions(
self.get_attributes(exclude=exclude_fields),
long_listing=True)
dictionary_properties = (utils.get_dict_properties(
s, columns, mixed_case_fields=_MIXED_CASE_FIELDS)
for s in vnflcm_op_occs
)
return (headers, dictionary_properties)
class ShowVnfLcmOp(command.ShowOne):
_description = _("Display Operation Occurrence details")
def get_parser(self, program_name):
"""Add arguments to parser.
Args:
program_name ([type]): program name
Returns:
parser([ArgumentParser]):
"""
parser = super(ShowVnfLcmOp, self).get_parser(program_name)
parser.add_argument(
_VNF_LCM_OP_OCC_ID,
metavar="<vnf-lcm-op-occ-id>",
help=_('VNF lifecycle management operation occurrence ID.'))
return parser
def take_action(self, parsed_args):
"""Execute show_vnf_lcm_op_occs and output response.
Args:
parsed_args ([Namespace]): arguments of CLI.
"""
client = self.app.client_manager.tackerclient
obj = client.show_vnf_lcm_op_occs(parsed_args.vnf_lcm_op_occ_id)
display_columns, columns = _get_columns(obj, action='show')
data = utils.get_item_properties(
sdk_utils.DictModel(obj),
columns, formatters=_FORMATTERS,
mixed_case_fields=_MIXED_CASE_FIELDS)
return (display_columns, data)

View File

@@ -1,181 +0,0 @@
# Copyright (C) 2022 Nippon Telegraph and Telephone 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 logging
from osc_lib.command import command
from osc_lib import utils
from tackerclient.common import exceptions
from tackerclient.i18n import _
from tackerclient.osc import sdk_utils
from tackerclient.osc import utils as tacker_osc_utils
LOG = logging.getLogger(__name__)
_LCCN_SUBSCRIPTION_ID = 'subscription_id'
_MIXED_CASE_FIELDS = ['filter', 'callbackUri']
_FORMATTERS = {
'filter': tacker_osc_utils.FormatComplexDataColumn,
'_links': tacker_osc_utils.FormatComplexDataColumn
}
def _get_columns(lccn_subsc_obj):
column_map = {
'id': 'ID',
'filter': 'Filter',
'callbackUri': 'Callback URI',
'_links': 'Links'
}
return sdk_utils.get_osc_show_columns_for_sdk_resource(lccn_subsc_obj,
column_map)
class CreateLccnSubscription(command.ShowOne):
_description = _("Create a new Lccn Subscription")
def get_parser(self, prog_name):
parser = super(CreateLccnSubscription, self).get_parser(prog_name)
parser.add_argument(
'create_request_file',
metavar="<param-file>",
help=_('Specify create request parameters in a json file.'))
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
subsc = client.create_lccn_subscription(
tacker_osc_utils.jsonfile2body(parsed_args.create_request_file))
display_columns, columns = _get_columns(subsc)
data = utils.get_item_properties(sdk_utils.DictModel(subsc),
columns, formatters=_FORMATTERS,
mixed_case_fields=_MIXED_CASE_FIELDS)
return (display_columns, data)
class DeleteLccnSubscription(command.Command):
_description = _("Delete Lccn Subscription(s)")
def get_parser(self, prog_name):
parser = super(DeleteLccnSubscription, self).get_parser(prog_name)
parser.add_argument(
_LCCN_SUBSCRIPTION_ID,
metavar="<subscription-id>",
nargs="+",
help=_("Lccn Subscription ID(s) to delete"))
return parser
def take_action(self, parsed_args):
error_count = 0
client = self.app.client_manager.tackerclient
lccn_subscriptions = parsed_args.subscription_id
for lccn_subscription in lccn_subscriptions:
try:
client.delete_lccn_subscription(lccn_subscription)
except Exception as e:
error_count += 1
LOG.error(_("Failed to delete Lccn Subscription with "
"ID '%(subsc)s': %(e)s"),
{'subsc': lccn_subscription, 'e': e})
total = len(lccn_subscriptions)
if (error_count > 0):
msg = (_("Failed to delete %(error_count)s of %(total)s "
"Lccn Subscriptions.") % {'error_count': error_count,
'total': total})
raise exceptions.CommandError(message=msg)
else:
if total > 1:
print(_('All specified Lccn Subscriptions are deleted '
'successfully'))
else:
print(_("Lccn Subscription '%s' is deleted "
"successfully") % lccn_subscriptions[0])
class ListLccnSubscription(command.Lister):
_description = _("List Lccn Subscriptions")
def get_parser(self, program_name):
parser = super(ListLccnSubscription, self).get_parser(program_name)
parser.add_argument(
"--filter",
metavar="<filter>",
help=_("Attribute-based-filtering parameters"),
)
return parser
def get_attributes(self, exclude=None):
fields = [
{
"key": "id",
"value": "ID"
},
{
"key": "callbackUri",
"value": "Callback URI"
}
]
attributes = []
for field in fields:
attributes.extend([(field['key'], field['value'],
tacker_osc_utils.LIST_BOTH)])
return tuple(attributes)
def take_action(self, parsed_args):
params = {}
if parsed_args.filter:
params['filter'] = parsed_args.filter
client = self.app.client_manager.tackerclient
subscriptions = client.list_lccn_subscriptions(**params)
headers, columns = tacker_osc_utils.get_column_definitions(
self.get_attributes(), long_listing=True)
dictionary_properties = (utils.get_dict_properties(
s, columns, mixed_case_fields=_MIXED_CASE_FIELDS)
for s in subscriptions
)
return (headers, dictionary_properties)
class ShowLccnSubscription(command.ShowOne):
_description = _("Display Lccn Subscription details")
def get_parser(self, program_name):
parser = super(ShowLccnSubscription, self).get_parser(program_name)
parser.add_argument(
_LCCN_SUBSCRIPTION_ID,
metavar="<subscription-id>",
help=_('Lccn Subscription ID to display'))
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
obj = client.show_lccn_subscription(parsed_args.subscription_id)
display_columns, columns = _get_columns(obj)
data = utils.get_item_properties(
sdk_utils.DictModel(obj),
columns, formatters=_FORMATTERS,
mixed_case_fields=_MIXED_CASE_FIELDS)
return (display_columns, data)

View File

@@ -0,0 +1,462 @@
# 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 oslo_utils import encodeutils
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)
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument(
'--config-file',
help=_('YAML file with VNF configuration'))
group.add_argument(
'--config',
help=_('YAML data with VNF configuration'))
group.add_argument(
'--param-file',
help=_('YAML file with VNF parameter'))
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(reason=e)
if not config:
raise exceptions.InvalidInput(
reason='The config file is empty')
if parsed_args.config:
decoded_config = encodeutils.safe_decode(parsed_args.config)
try:
config = yaml.load(decoded_config, Loader=yaml.SafeLoader)
except yaml.YAMLError as e:
raise exceptions.InvalidInput(reason=e)
if not config:
raise exceptions.InvalidInput(
reason='The parameter is empty')
if config:
body[_VNF]['attributes'] = {'config': config}
if parsed_args.param_file:
with open(parsed_args.param_file) as f:
param_yaml = f.read()
try:
param = yaml.load(
param_yaml, Loader=yaml.SafeLoader)
except yaml.YAMLError as e:
raise exceptions.InvalidInput(reason=e)
if not param:
raise exceptions.InvalidInput(
reason='The parameter file is empty')
body[_VNF]['attributes'] = {'param_values': param}
tackerV10.update_dict(parsed_args, body[_VNF], ['tenant_id'])
return body
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
obj_id = tackerV10.find_resourceid_by_name_or_id(
client, _VNF, parsed_args.vnf)
vnf = client.update_vnf(obj_id, self.args2body(parsed_args))
if vnf[_VNF]['attributes'].get('monitoring_policy'):
vnf[_VNF]['attributes']['monitoring_policy'] =\
_break_string(vnf[_VNF]['attributes']['monitoring_policy'])
display_columns, columns = _get_columns(vnf[_VNF])
data = utils.get_item_properties(
sdk_utils.DictModel(vnf[_VNF]),
columns)
return (display_columns, data)
class ScaleVNF(command.Command):
_description = _("Scale a VNF.")
def get_parser(self, prog_name):
parser = super(ScaleVNF, self).get_parser(prog_name)
parser.add_argument(
'--scaling-policy-name',
help=_('VNF policy name used to scale'))
parser.add_argument(
'--scaling-type',
help=_('VNF scaling type, it could be either "out" or "in"'))
parser.add_argument(
_VNF,
metavar="<VNF>",
help=_("VNF to scale (name or ID)"))
return parser
def args2body(self, parsed_args):
args = {}
body = {"scale": args}
args['type'] = parsed_args.scaling_type
args['policy'] = parsed_args.scaling_policy_name
return body
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
obj_id = tackerV10.find_resourceid_by_name_or_id(
client, _VNF, parsed_args.vnf)
client.scale_vnf(obj_id, self.args2body(parsed_args))
return

View File

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

@@ -32,14 +32,13 @@ LOG = logging.getLogger(__name__)
formatters = {'softwareImages': tacker_osc_utils.FormatComplexDataColumn,
'checksum': tacker_osc_utils.FormatComplexDataColumn,
'_links': tacker_osc_utils.FormatComplexDataColumn,
'userDefinedData': tacker_osc_utils.FormatComplexDataColumn,
'additionalArtifacts': tacker_osc_utils.FormatComplexDataColumn}
'userDefinedData': tacker_osc_utils.FormatComplexDataColumn}
_mixed_case_fields = ('usageState', 'onboardingState', 'operationalState',
'vnfProductName', 'softwareImages', 'userDefinedData',
'vnfdId', 'vnfdVersion', 'vnfSoftwareVersion',
'vnfProvider', 'additionalArtifacts')
'vnfProvider')
def _get_columns(vnf_package_obj):
@@ -60,8 +59,7 @@ def _get_columns(vnf_package_obj):
'vnfProductName': 'VNF Product Name',
'vnfdId': 'VNFD ID',
'vnfdVersion': 'VNFD Version',
'checksum': 'Checksum',
'additionalArtifacts': 'Additional Artifacts'
'checksum': 'Checksum'
})
return sdk_utils.get_osc_show_columns_for_sdk_resource(vnf_package_obj,
@@ -148,11 +146,7 @@ class ListVnfPackage(command.Lister):
exclude_fields=None, exclude_default=False):
fields = ['id', 'vnfProductName', 'onboardingState',
'usageState', 'operationalState', '_links']
complex_fields = [
'checksum',
'softwareImages',
'userDefinedData',
'additionalArtifacts']
complex_fields = ['checksum', 'softwareImages', 'userDefinedData']
simple_fields = ['vnfdVersion', 'vnfProvider', 'vnfSoftwareVersion',
'vnfdId']
@@ -349,7 +343,7 @@ class DeleteVnfPackage(command.Command):
% {'failed_id': failed_id,
'error': error})
msg += err_msg
raise exceptions.CommandError(message=msg)
raise exceptions.CommandError(msg)
else:
print((_('All specified %(resource)s(s) deleted successfully')
% {'resource': self.resource}))
@@ -415,51 +409,6 @@ class DownloadVnfPackage(command.Command):
sdk_utils.save_data(body, parsed_args.file)
class DownloadVnfPackageArtifact(command.Command):
_description = _("Download VNF package artifact of an on-boarded "
"VNF package.")
def get_parser(self, prog_name):
parser = super(DownloadVnfPackageArtifact, self).get_parser(prog_name)
parser.add_argument(
"vnf_package",
metavar="<vnf-package>",
help=_("VNF package ID")
)
parser.add_argument(
"artifact_path",
metavar="<artifact-path>",
help=_("The artifact file's path")
)
parser.add_argument(
"--file",
metavar="<FILE>",
help=_("Local file to save downloaded VNF Package artifact "
"file data. If this is not specified and "
"there is no redirection then data will not be saved.")
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
if sys.stdout.isatty() and not (parsed_args.file):
msg = (
"No redirection or local file specified for downloaded "
"vnf package artifact data. Please specify a "
"local file with --file to "
"save downloaded vnf package artifact data "
"or use redirection.")
sdk_utils.exit(msg)
body = client.download_artifact_from_vnf_package(
parsed_args.vnf_package, parsed_args.artifact_path)
if not parsed_args.file:
print(body)
return
else:
sdk_utils.save_data(body, parsed_args.file)
class UpdateVnfPackage(command.ShowOne):
_description = _("Update information about an individual VNF package")

View File

@@ -1,62 +0,0 @@
{
"filter": {
"vnfInstanceSubscriptionFilter": {
"vnfdIds": [
"dummy-vnfdId-1"
],
"vnfProductsFromProviders": [
{
"vnfProvider": "dummy-vnfProvider-1",
"vnfProducts": [
{
"vnfProductName": "dummy-vnfProductName-1-1",
"versions": [
{
"vnfSoftwareVersion": 1.0,
"vnfdVersions": [1.0, 2.0]
}
]
}
]
}
],
"vnfInstanceIds": [
"dummy-vnfInstanceId-1"
],
"vnfInstanceNames": [
"dummy-vnfInstanceName-1"
]
},
"notificationTypes": [
"AlarmNotification"
],
"faultyResourceTypes": [
"COMPUTE"
],
"perceivedSeverities": [
"WARNING"
],
"eventTypes": [
"EQUIPMENT_ALARM"
],
"probableCauses": [
"The server cannot be connected."
]
},
"callbackUri": "/nfvo/notify/alarm",
"authentication": {
"authType": [
"BASIC",
"OAUTH2_CLIENT_CREDENTIALS"
],
"paramsBasic": {
"userName": "nfvo",
"password": "nfvopwd"
},
"paramsOauth2ClientCredentials": {
"clientId": "auth_user_name",
"clientPassword": "auth_password",
"tokenEndpoint": "token_endpoint"
}
}
}

View File

@@ -1,177 +0,0 @@
# Copyright (C) 2022 Fujitsu
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import logging
from osc_lib.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
LOG = logging.getLogger(__name__)
_ATTR_MAP = (
('id', 'ID', tacker_osc_utils.LIST_BOTH),
('managedObjectId', 'Managed Object Id', tacker_osc_utils.LIST_BOTH),
('ackState', 'Ack State', tacker_osc_utils.LIST_BOTH),
('eventType', 'Event Type', tacker_osc_utils.LIST_BOTH),
('perceivedSeverity', 'Perceived Severity', tacker_osc_utils.LIST_BOTH),
('probableCause', 'Probable Cause', tacker_osc_utils.LIST_BOTH)
)
_FORMATTERS = {
'vnfcInstanceIds': tacker_osc_utils.FormatComplexDataColumn,
'rootCauseFaultyResource': tacker_osc_utils.FormatComplexDataColumn,
'correlatedAlarmIds': tacker_osc_utils.FormatComplexDataColumn,
'faultDetails': tacker_osc_utils.FormatComplexDataColumn,
'_links': tacker_osc_utils.FormatComplexDataColumn
}
_MIXED_CASE_FIELDS = (
'managedObjectId', 'rootCauseFaultyResource', 'vnfcInstanceIds',
'alarmRaisedTime', 'alarmChangedTime', 'alarmClearedTime',
'alarmAcknowledgedTime', 'ackState', 'perceivedSeverity', 'eventTime',
'eventType', 'faultType', 'probableCause', 'isRootCause',
'correlatedAlarmIds', 'faultDetails'
)
_VNF_FM_ALARM_ID = 'vnf_fm_alarm_id'
def _get_columns(vnffm_alarm_obj, action=None):
if action == 'update':
column_map = {
'ackState': 'Ack State'
}
else:
column_map = {
'id': 'ID',
'managedObjectId': 'Managed Object Id',
'ackState': 'Ack State',
'perceivedSeverity': 'Perceived Severity',
'eventType': 'Event Type',
'probableCause': 'Probable Cause'
}
if action == 'show':
column_map.update({
'vnfcInstanceIds': 'Vnfc Instance Ids',
'rootCauseFaultyResource': 'Root Cause Faulty Resource',
'alarmRaisedTime': 'Alarm Raised Time',
'alarmChangedTime': 'Alarm Changed Time',
'alarmClearedTime': 'Alarm Cleared Time',
'alarmAcknowledgedTime': 'Alarm Acknowledged Time',
'eventTime': 'Event Time',
'faultType': 'Fault Type',
'isRootCause': 'Is Root Cause',
'correlatedAlarmIds': 'Correlated Alarm Ids',
'faultDetails': 'Fault Details',
'_links': 'Links'
})
return sdk_utils.get_osc_show_columns_for_sdk_resource(
vnffm_alarm_obj, column_map)
class ListVnfFmAlarm(command.Lister):
_description = _("List VNF FM alarms")
def get_parser(self, prog_name):
LOG.debug('get_parser(%s)', prog_name)
parser = super(ListVnfFmAlarm, self).get_parser(prog_name)
parser.add_argument(
"--filter",
metavar="<filter>",
help=_("Attribute-based-filtering parameters"),
)
return parser
def take_action(self, parsed_args):
_params = {}
if parsed_args.filter:
_params['filter'] = parsed_args.filter
client = self.app.client_manager.tackerclient
data = client.list_vnf_fm_alarms(**_params)
headers, columns = tacker_osc_utils.get_column_definitions(
_ATTR_MAP, long_listing=True)
return (headers,
(utils.get_dict_properties(
s, columns, formatters=_FORMATTERS,
mixed_case_fields=_MIXED_CASE_FIELDS,
) for s in data['vnf_fm_alarms']))
class ShowVnfFmAlarm(command.ShowOne):
_description = _("Display VNF FM alarm details")
def get_parser(self, prog_name):
parser = super(ShowVnfFmAlarm, self).get_parser(prog_name)
parser.add_argument(
_VNF_FM_ALARM_ID,
metavar="<vnf-fm-alarm-id>",
help=_("VNF FM alarm ID to display"))
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
obj = client.show_vnf_fm_alarm(parsed_args.vnf_fm_alarm_id)
display_columns, columns = _get_columns(obj, action='show')
data = utils.get_item_properties(
sdk_utils.DictModel(obj), columns,
mixed_case_fields=_MIXED_CASE_FIELDS,
formatters=_FORMATTERS)
return (display_columns, data)
class UpdateVnfFmAlarm(command.ShowOne):
_description = _("Update information about an individual VNF FM alarm")
def get_parser(self, prog_name):
LOG.debug('get_parser(%s)', prog_name)
parser = super(UpdateVnfFmAlarm, self).get_parser(prog_name)
parser.add_argument(
_VNF_FM_ALARM_ID,
metavar="<vnf-fm-alarm-id>",
help=_("VNF FM alarm ID to update.")
)
update_require_parameters = parser.add_argument_group(
"require arguments"
)
update_require_parameters.add_argument(
"--ack-state",
metavar="<ack-state>",
choices=['ACKNOWLEDGED', 'UNACKNOWLEDGED'],
help=_("Ask state can be 'ACKNOWLEDGED' or 'UNACKNOWLEDGED'."))
return parser
def args2body(self, parsed_args):
body = {'ackState': parsed_args.ack_state}
return body
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
updated_values = client.update_vnf_fm_alarm(
parsed_args.vnf_fm_alarm_id, self.args2body(parsed_args))
display_columns, columns = _get_columns(
updated_values, action='update')
data = utils.get_item_properties(
sdk_utils.DictModel(updated_values), columns,
mixed_case_fields=_MIXED_CASE_FIELDS,
formatters=_FORMATTERS)
return (display_columns, data)

View File

@@ -1,169 +0,0 @@
# Copyright (C) 2022 Fujitsu
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import logging
from osc_lib.command import command
from osc_lib import utils
from tackerclient.common import exceptions
from tackerclient.i18n import _
from tackerclient.osc import sdk_utils
from tackerclient.osc import utils as tacker_osc_utils
LOG = logging.getLogger(__name__)
_ATTR_MAP = (
('id', 'ID', tacker_osc_utils.LIST_BOTH),
('callbackUri', 'Callback Uri', tacker_osc_utils.LIST_BOTH)
)
_FORMATTERS = {
'filter': tacker_osc_utils.FormatComplexDataColumn,
'_links': tacker_osc_utils.FormatComplexDataColumn
}
_MIXED_CASE_FIELDS = (
'callbackUri'
)
_VNF_FM_SUB_ID = 'vnf_fm_sub_id'
def _get_columns(vnffm_sub_obj):
column_map = {
'id': 'ID',
'filter': 'Filter',
'callbackUri': 'Callback Uri',
'_links': 'Links'
}
return sdk_utils.get_osc_show_columns_for_sdk_resource(
vnffm_sub_obj, column_map)
class CreateVnfFmSub(command.ShowOne):
_description = _("Create a new VNF FM subscription")
def get_parser(self, prog_name):
parser = super(CreateVnfFmSub, self).get_parser(prog_name)
parser.add_argument(
'request_file',
metavar="<param-file>",
help=_('Specify create VNF FM subscription request '
'parameters in a json file.'))
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
vnf_fm_sub = client.create_vnf_fm_sub(
tacker_osc_utils.jsonfile2body(parsed_args.request_file))
display_columns, columns = _get_columns(vnf_fm_sub)
data = utils.get_item_properties(
sdk_utils.DictModel(vnf_fm_sub), columns,
formatters=_FORMATTERS, mixed_case_fields=_MIXED_CASE_FIELDS)
return (display_columns, data)
class ListVnfFmSub(command.Lister):
_description = _("List VNF FM subs")
def get_parser(self, prog_name):
LOG.debug('get_parser(%s)', prog_name)
parser = super(ListVnfFmSub, self).get_parser(prog_name)
parser.add_argument(
"--filter",
metavar="<filter>",
help=_("Attribute-based-filtering parameters"),
)
return parser
def take_action(self, parsed_args):
_params = {}
if parsed_args.filter:
_params['filter'] = parsed_args.filter
client = self.app.client_manager.tackerclient
data = client.list_vnf_fm_subs(**_params)
headers, columns = tacker_osc_utils.get_column_definitions(
_ATTR_MAP, long_listing=True)
return (headers,
(utils.get_dict_properties(
s, columns, formatters=_FORMATTERS,
mixed_case_fields=_MIXED_CASE_FIELDS,
) for s in data['vnf_fm_subs']))
class ShowVnfFmSub(command.ShowOne):
_description = _("Display VNF FM subscription details")
def get_parser(self, prog_name):
parser = super(ShowVnfFmSub, self).get_parser(prog_name)
parser.add_argument(
_VNF_FM_SUB_ID,
metavar="<vnf-fm-sub-id>",
help=_("VNF FM subscription ID to display"))
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
obj = client.show_vnf_fm_sub(parsed_args.vnf_fm_sub_id)
display_columns, columns = _get_columns(obj)
data = utils.get_item_properties(
sdk_utils.DictModel(obj), columns,
mixed_case_fields=_MIXED_CASE_FIELDS,
formatters=_FORMATTERS)
return (display_columns, data)
class DeleteVnfFmSub(command.Command):
_description = _("Delete VNF FM subscription(s)")
def get_parser(self, prog_name):
parser = super(DeleteVnfFmSub, self).get_parser(prog_name)
parser.add_argument(
_VNF_FM_SUB_ID,
metavar="<vnf-fm-sub-id>",
nargs="+",
help=_("VNF FM subscription ID(s) to delete"))
return parser
def take_action(self, parsed_args):
error_count = 0
client = self.app.client_manager.tackerclient
vnf_fm_sub_ids = parsed_args.vnf_fm_sub_id
for sub_id in vnf_fm_sub_ids:
try:
client.delete_vnf_fm_sub(sub_id)
except Exception as e:
error_count += 1
LOG.error(_("Failed to delete VNF FM subscription with "
"ID '%(sub_id)s': %(e)s"),
{'sub_id': sub_id, 'e': e})
total = len(vnf_fm_sub_ids)
if error_count > 0:
msg = (_("Failed to delete %(error_count)s of %(total)s "
"VNF FM subscriptions.") % {'error_count': error_count,
'total': total})
raise exceptions.CommandError(message=msg)
if total > 1:
print(_('All specified VNF FM subscriptions are deleted '
'successfully'))
else:
print(_("VNF FM subscription '%s' deleted "
"successfully") % vnf_fm_sub_ids[0])

View File

@@ -1,36 +0,0 @@
{
"vnfdId": "c6595341-a5bb-8246-53c4-7aeb843d60c5",
"additionalParams": {
"upgrade_type": "RollingUpdate",
"lcm-operation-coordinate-old-vnf": "./Scripts/coordinate_old_vnf.py",
"lcm-operation-coordinate-old-vnf-class": "CoordinateOldVnf",
"lcm-operation-coordinate-new-vnf": "./Scripts/coordinate_new_vnf.py",
"lcm-operation-coordinate-new-vnf-class": "CoordinateNewVnf",
"vdu_params": [{
"vduId": "VDU1",
"old_vnfc_param": {
"cp_name": "VDU1_CP1",
"username": "ubuntu",
"password": "ubuntu"
},
"new_vnfc_param": {
"cp_name": "VDU1_CP1",
"username": "ubuntu",
"password": "ubuntu"
}
}, {
"vduId": "VDU2",
"old_vnfc_param": {
"cp_name": "VDU2_CP1",
"username": "ubuntu",
"password": "ubuntu"
},
"new_vnfc_param": {
"cp_name": "VDU2_CP1",
"username": "ubuntu",
"password": "ubuntu"
}
}]
}
}

View File

@@ -1,36 +0,0 @@
{
"objectType": "VNFC",
"objectInstanceIds": [
"object-instance-id-1"
],
"subObjectInstanceIds": [
"sub-object-instance-id-2"
],
"criteria": {
"performanceMetric": [
"VCpuUsageMeanVnf.object-instance-id-1"
],
"performanceMetricGroup": [
"VirtualisedComputeResource"
],
"collectionPeriod": "500",
"reportingPeriod": "1000",
"reportingBoundary": "2022/07/25 10:43:55"
},
"callbackUri": "/nfvo/notify/job",
"authentication": {
"authType": [
"BASIC",
"OAUTH2_CLIENT_CREDENTIALS"
],
"paramsBasic": {
"userName": "nfvo",
"password": "nfvopwd"
},
"paramsOauth2ClientCredentials": {
"clientId": "auth_user_name",
"clientPassword": "auth_password",
"tokenEndpoint": "token_endpoint"
}
}
}

View File

@@ -1,40 +0,0 @@
{
"objectType": "Vnfc",
"objectInstanceId": "object-instance-id-1",
"subObjectInstanceIds": [
"sub-object-instance-id-2"
],
"criteria": {
"performanceMetric": "VCpuUsageMeanVnf.object-instance-id-1",
"thresholdType": "SIMPLE",
"simpleThresholdDetails": {
"thresholdValue": 400.5,
"hysteresis": 10.3
}
},
"callbackUri": "/nfvo/notify/threshold",
"authentication": {
"authType": [
"BASIC",
"OAUTH2_CLIENT_CREDENTIALS",
"OAUTH2_CLIENT_CERT"
],
"paramsBasic": {
"userName": "nfvo",
"password": "nfvopwd"
},
"paramsOauth2ClientCredentials": {
"clientId": "auth_user_name",
"clientPassword": "auth_password",
"tokenEndpoint": "token_endpoint"
},
"paramsOauth2ClientCert": {
"clientId": "test",
"certificateRef": {
"type": "x5t#256",
"value": "03c6e188d1fe5d3da8c9bc9a8dc531a2b3e"
},
"tokenEndpoint": "http://127.0.0.1/token"
}
}
}

View File

@@ -1,18 +0,0 @@
{
"callbackUri": "/nfvo/notify/job",
"authentication": {
"authType": [
"BASIC",
"OAUTH2_CLIENT_CREDENTIALS"
],
"paramsBasic": {
"userName": "nfvo",
"password": "nfvopwd"
},
"paramsOauth2ClientCredentials": {
"clientId": "auth_user_name",
"clientPassword": "auth_password",
"tokenEndpoint": "token_endpoint"
}
}
}

View File

@@ -1,27 +0,0 @@
{
"callbackUri": "/nfvo/notify/threshold",
"authentication": {
"authType": [
"BASIC",
"OAUTH2_CLIENT_CREDENTIALS",
"OAUTH2_CLIENT_CERT"
],
"paramsBasic": {
"userName": "nfvo",
"password": "nfvopwd"
},
"paramsOauth2ClientCredentials": {
"clientId": "auth_user_name",
"clientPassword": "auth_password",
"tokenEndpoint": "token_endpoint"
},
"paramsOauth2ClientCert": {
"clientId": "test",
"certificateRef": {
"type": "x5t#256",
"value": "03c6e188d1fe5d3da8c9bc9a8dc531a2b3e"
},
"tokenEndpoint": "http://127.0.0.1/token"
}
}
}

View File

@@ -1,300 +0,0 @@
# Copyright (C) 2022 Fujitsu
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import logging
from functools import reduce
from osc_lib.command import command
from osc_lib import utils
from tackerclient.common import exceptions
from tackerclient.i18n import _
from tackerclient.osc import sdk_utils
from tackerclient.osc import utils as tacker_osc_utils
LOG = logging.getLogger(__name__)
_FORMATTERS = {
'objectInstanceIds': tacker_osc_utils.FormatComplexDataColumn,
'subObjectInstanceIds': tacker_osc_utils.FormatComplexDataColumn,
'criteria': tacker_osc_utils.FormatComplexDataColumn,
'reports': tacker_osc_utils.FormatComplexDataColumn,
'_links': tacker_osc_utils.FormatComplexDataColumn
}
_MIXED_CASE_FIELDS = (
'objectType', 'objectInstanceIds', 'subObjectInstanceIds', 'callbackUri'
)
_MIXED_CASE_FIELDS_UPDATE = (
'callbackUri'
)
_VNF_PM_JOB_ID = 'vnf_pm_job_id'
def _get_columns(vnfpm_job_obj, action=None):
if action == 'update':
column_map = {
'callbackUri': 'Callback Uri'
}
else:
column_map = {
'id': 'ID',
'objectType': 'Object Type',
'objectInstanceIds': 'Object Instance Ids',
'subObjectInstanceIds': 'Sub Object Instance Ids',
'criteria': 'Criteria',
'callbackUri': 'Callback Uri',
'reports': 'Reports',
'_links': 'Links'
}
if action == 'show':
column_map.update(
{'reports': 'Reports'}
)
return sdk_utils.get_osc_show_columns_for_sdk_resource(
vnfpm_job_obj, column_map)
class CreateVnfPmJob(command.ShowOne):
_description = _("Create a new VNF PM job")
def get_parser(self, prog_name):
parser = super(CreateVnfPmJob, self).get_parser(prog_name)
parser.add_argument(
'request_file',
metavar="<param-file>",
help=_('Specify create VNF PM job request '
'parameters in a json file.'))
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
vnf_pm_job = client.create_vnf_pm_job(
tacker_osc_utils.jsonfile2body(parsed_args.request_file))
display_columns, columns = _get_columns(vnf_pm_job)
data = utils.get_item_properties(
sdk_utils.DictModel(vnf_pm_job), columns,
formatters=_FORMATTERS, mixed_case_fields=_MIXED_CASE_FIELDS)
return (display_columns, data)
class ListVnfPmJob(command.Lister):
_description = _("List VNF PM jobs")
def get_parser(self, prog_name):
LOG.debug('get_parser(%s)', prog_name)
parser = super(ListVnfPmJob, self).get_parser(prog_name)
parser.add_argument(
"--filter",
metavar="<filter>",
help=_("Attribute-based-filtering parameters"),
)
fields_exclusive_group = parser.add_mutually_exclusive_group(
required=False)
fields_exclusive_group.add_argument(
"--all_fields",
action="store_true",
default=False,
help=_("Include all complex attributes in the response"),
)
fields_exclusive_group.add_argument(
"--fields",
metavar="fields",
help=_("Complex attributes to be included into the response"),
)
fields_exclusive_group.add_argument(
"--exclude_fields",
metavar="exclude-fields",
help=_("Complex attributes to be excluded from the response"),
)
parser.add_argument(
"--exclude_default",
action="store_true",
default=False,
help=_("Indicates to exclude all complex attributes"
" from the response. This argument can be used alone or"
" with --fields and --filter. For all other combinations"
" tacker server will throw bad request error"),
)
return parser
def case_modify(self, field):
return reduce(
lambda x, y: x + (' ' if y.isupper() else '') + y, field).title()
def get_attributes(self, extra_fields=None, all_fields=False,
exclude_fields=None, exclude_default=False):
fields = ['id', 'objectType', '_links']
complex_fields = [
'objectInstanceIds',
'subObjectInstanceIds',
'criteria',
'reports']
simple_fields = ['callbackUri']
if extra_fields:
fields.extend(extra_fields)
if exclude_fields:
fields.extend([field for field in complex_fields
if field not in exclude_fields])
if all_fields:
fields.extend(complex_fields)
fields.extend(simple_fields)
if exclude_default:
fields.extend(simple_fields)
attrs = []
for field in fields:
if field == '_links':
attrs.extend([(field, 'Links', tacker_osc_utils.LIST_BOTH)])
else:
attrs.extend([(field, self.case_modify(field),
tacker_osc_utils.LIST_BOTH)])
return tuple(attrs)
def take_action(self, parsed_args):
_params = {}
extra_fields = []
exclude_fields = []
all_fields = False
exclude_default = False
if parsed_args.filter:
_params['filter'] = parsed_args.filter
if parsed_args.fields:
_params['fields'] = parsed_args.fields
fields = parsed_args.fields.split(',')
for field in fields:
extra_fields.append(field.split('/')[0])
if parsed_args.exclude_fields:
_params['exclude_fields'] = parsed_args.exclude_fields
fields = parsed_args.exclude_fields.split(',')
exclude_fields.extend(fields)
if parsed_args.exclude_default:
_params['exclude_default'] = None
exclude_default = True
if parsed_args.all_fields:
_params['all_fields'] = None
all_fields = True
client = self.app.client_manager.tackerclient
data = client.list_vnf_pm_jobs(**_params)
headers, columns = tacker_osc_utils.get_column_definitions(
self.get_attributes(extra_fields, all_fields, exclude_fields,
exclude_default), long_listing=True)
return (headers,
(utils.get_dict_properties(
s, columns, formatters=_FORMATTERS,
mixed_case_fields=_MIXED_CASE_FIELDS,
) for s in data['vnf_pm_jobs']))
class ShowVnfPmJob(command.ShowOne):
_description = _("Display VNF PM job details")
def get_parser(self, prog_name):
parser = super(ShowVnfPmJob, self).get_parser(prog_name)
parser.add_argument(
_VNF_PM_JOB_ID,
metavar="<vnf-pm-job-id>",
help=_("VNF PM job ID to display"))
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
obj = client.show_vnf_pm_job(parsed_args.vnf_pm_job_id)
display_columns, columns = _get_columns(obj, action='show')
data = utils.get_item_properties(
sdk_utils.DictModel(obj), columns,
mixed_case_fields=_MIXED_CASE_FIELDS,
formatters=_FORMATTERS)
return (display_columns, data)
class UpdateVnfPmJob(command.ShowOne):
_description = _("Update information about an individual VNF PM job")
def get_parser(self, prog_name):
LOG.debug('get_parser(%s)', prog_name)
parser = super(UpdateVnfPmJob, self).get_parser(prog_name)
parser.add_argument(
_VNF_PM_JOB_ID,
metavar="<vnf-pm-job-id>",
help=_("VNF PM job ID to update.")
)
parser.add_argument(
'request_file',
metavar="<param-file>",
help=_('Specify update PM job request '
'parameters in a json file.'))
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
updated_values = client.update_vnf_pm_job(
parsed_args.vnf_pm_job_id,
tacker_osc_utils.jsonfile2body(parsed_args.request_file))
display_columns, columns = _get_columns(updated_values, 'update')
data = utils.get_item_properties(
sdk_utils.DictModel(updated_values), columns,
mixed_case_fields=_MIXED_CASE_FIELDS_UPDATE)
return (display_columns, data)
class DeleteVnfPmJob(command.Command):
_description = _("Delete VNF PM job")
def get_parser(self, prog_name):
parser = super(DeleteVnfPmJob, self).get_parser(prog_name)
parser.add_argument(
_VNF_PM_JOB_ID,
metavar="<vnf-pm-job-id>",
nargs="+",
help=_("VNF PM job ID(s) to delete"))
return parser
def take_action(self, parsed_args):
error_count = 0
client = self.app.client_manager.tackerclient
vnf_pm_job_ids = parsed_args.vnf_pm_job_id
for job_id in vnf_pm_job_ids:
try:
client.delete_vnf_pm_job(job_id)
except Exception as e:
error_count += 1
LOG.error(_("Failed to delete VNF PM job with "
"ID '%(job_id)s': %(e)s"),
{'job_id': job_id, 'e': e})
total = len(vnf_pm_job_ids)
if error_count > 0:
msg = (_("Failed to delete %(error_count)s of %(total)s "
"VNF PM jobs.") % {'error_count': error_count,
'total': total})
raise exceptions.CommandError(message=msg)
if total > 1:
print(_('All specified VNF PM jobs are deleted '
'successfully'))
else:
print(_("VNF PM job '%s' deleted "
"successfully") % vnf_pm_job_ids[0])

View File

@@ -1,67 +0,0 @@
# Copyright (C) 2022 Fujitsu
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import logging
from osc_lib.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
LOG = logging.getLogger(__name__)
_FORMATTERS = {
'entries': tacker_osc_utils.FormatComplexDataColumn
}
_VNF_PM_JOB_ID = 'vnf_pm_job_id'
_VNF_PM_REPORT_ID = 'vnf_pm_report_id'
def _get_columns(vnfpm_report_obj):
column_map = {
'entries': 'Entries'
}
return sdk_utils.get_osc_show_columns_for_sdk_resource(
vnfpm_report_obj, column_map)
class ShowVnfPmReport(command.ShowOne):
_description = _("Display VNF PM report details")
def get_parser(self, prog_name):
parser = super(ShowVnfPmReport, self).get_parser(prog_name)
parser.add_argument(
_VNF_PM_JOB_ID,
metavar="<vnf-pm-job-id>",
help=_("VNF PM job id where the VNF PM report is located"))
parser.add_argument(
_VNF_PM_REPORT_ID,
metavar="<vnf-pm-report-id>",
help=_("VNF PM report ID to display"))
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
obj = client.show_vnf_pm_report(
parsed_args.vnf_pm_job_id, parsed_args.vnf_pm_report_id)
display_columns, columns = _get_columns(obj)
data = utils.get_item_properties(
sdk_utils.DictModel(obj),
columns, formatters=_FORMATTERS,
mixed_case_fields=None)
return (display_columns, data)

View File

@@ -1,216 +0,0 @@
# Copyright (C) 2023 Fujitsu
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import logging
from osc_lib.command import command
from osc_lib import utils
from tackerclient.common import exceptions
from tackerclient.i18n import _
from tackerclient.osc import sdk_utils
from tackerclient.osc import utils as tacker_osc_utils
LOG = logging.getLogger(__name__)
_ATTR_MAP = (
('id', 'ID', tacker_osc_utils.LIST_BOTH),
('objectType', 'Object Type', tacker_osc_utils.LIST_BOTH),
('_links', 'Links', tacker_osc_utils.LIST_BOTH)
)
_FORMATTERS = {
'subObjectInstanceIds': tacker_osc_utils.FormatComplexDataColumn,
'criteria': tacker_osc_utils.FormatComplexDataColumn,
'_links': tacker_osc_utils.FormatComplexDataColumn
}
_MIXED_CASE_FIELDS = (
'objectType', 'objectInstanceId', 'subObjectInstanceIds', 'callbackUri'
)
_MIXED_CASE_FIELDS_UPDATE = (
'callbackUri'
)
_VNF_PM_THRESHOLD_ID = 'vnf_pm_threshold_id'
def _get_columns(vnf_pm_threshold, action=None):
if action == 'update':
column_map = {
'callbackUri': 'Callback Uri'
}
else:
column_map = {
'id': 'ID',
'objectType': 'Object Type',
'objectInstanceId': 'Object Instance Id',
'subObjectInstanceIds': 'Sub Object Instance Ids',
'criteria': 'Criteria',
'callbackUri': 'Callback Uri',
'_links': 'Links'
}
return sdk_utils.get_osc_show_columns_for_sdk_resource(
vnf_pm_threshold, column_map)
class CreateVnfPmThreshold(command.ShowOne):
_description = _("Create a new VNF PM threshold")
def get_parser(self, prog_name):
LOG.debug('get_parser(%s)', prog_name)
parser = super(CreateVnfPmThreshold, self).get_parser(prog_name)
parser.add_argument(
'request_file',
metavar="<param-file>",
help=_('Specify create VNF PM threshold request '
'parameters in a json file.'))
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
vnf_pm_threshold = client.create_vnf_pm_threshold(
tacker_osc_utils.jsonfile2body(parsed_args.request_file))
display_columns, columns = _get_columns(vnf_pm_threshold)
data = utils.get_item_properties(
sdk_utils.DictModel(vnf_pm_threshold), columns,
formatters=_FORMATTERS, mixed_case_fields=_MIXED_CASE_FIELDS)
return (display_columns, data)
class ListVnfPmThreshold(command.Lister):
_description = _("List VNF PM thresholds")
def get_parser(self, prog_name):
LOG.debug('get_parser(%s)', prog_name)
parser = super(ListVnfPmThreshold, self).get_parser(prog_name)
parser.add_argument(
"--filter",
metavar="<filter>",
help=_("Attribute-based-filtering parameters"),
)
return parser
def take_action(self, parsed_args):
_params = {}
if parsed_args.filter:
_params['filter'] = parsed_args.filter
client = self.app.client_manager.tackerclient
data = client.list_vnf_pm_thresholds(**_params)
headers, columns = tacker_osc_utils.get_column_definitions(
_ATTR_MAP, long_listing=True)
return (headers,
(utils.get_dict_properties(
s, columns, formatters=_FORMATTERS,
mixed_case_fields=_MIXED_CASE_FIELDS,
) for s in data['vnf_pm_thresholds']))
class ShowVnfPmThreshold(command.ShowOne):
_description = _("Display VNF PM threshold details")
def get_parser(self, prog_name):
LOG.debug('get_parser(%s)', prog_name)
parser = super(ShowVnfPmThreshold, self).get_parser(prog_name)
parser.add_argument(
_VNF_PM_THRESHOLD_ID,
metavar="<vnf-pm-threshold-id>",
help=_("VNF PM threshold ID to display"))
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
obj = client.show_vnf_pm_threshold(parsed_args.vnf_pm_threshold_id)
display_columns, columns = _get_columns(obj)
data = utils.get_item_properties(
sdk_utils.DictModel(obj), columns,
mixed_case_fields=_MIXED_CASE_FIELDS,
formatters=_FORMATTERS)
return (display_columns, data)
class UpdateVnfPmThreshold(command.ShowOne):
_description = _("Update information about an individual VNF PM threshold")
def get_parser(self, prog_name):
LOG.debug('get_parser(%s)', prog_name)
parser = super(UpdateVnfPmThreshold, self).get_parser(prog_name)
parser.add_argument(
_VNF_PM_THRESHOLD_ID,
metavar="<vnf-pm-threshold-id>",
help=_("VNF PM threshold ID to update.")
)
parser.add_argument(
'request_file',
metavar="<param-file>",
help=_('Specify update PM threshold request '
'parameters in a json file.'))
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
updated_values = client.update_vnf_pm_threshold(
parsed_args.vnf_pm_threshold_id,
tacker_osc_utils.jsonfile2body(parsed_args.request_file))
display_columns, columns = _get_columns(updated_values, 'update')
data = utils.get_item_properties(
sdk_utils.DictModel(updated_values), columns,
mixed_case_fields=_MIXED_CASE_FIELDS_UPDATE)
return (display_columns, data)
class DeleteVnfPmThreshold(command.Command):
_description = _("Delete VNF PM threshold")
def get_parser(self, prog_name):
LOG.debug('get_parser(%s)', prog_name)
parser = super(DeleteVnfPmThreshold, self).get_parser(prog_name)
parser.add_argument(
_VNF_PM_THRESHOLD_ID,
metavar="<vnf-pm-threshold-id>",
nargs="+",
help=_("VNF PM threshold ID(s) to delete"))
return parser
def take_action(self, parsed_args):
error_count = 0
client = self.app.client_manager.tackerclient
vnf_pm_threshold_ids = parsed_args.vnf_pm_threshold_id
for threshold_id in vnf_pm_threshold_ids:
try:
client.delete_vnf_pm_threshold(threshold_id)
except Exception as e:
error_count += 1
LOG.error(_("Failed to delete VNF PM threshold with "
"ID '%(threshold_id)s': %(e)s"),
{'threshold_id': threshold_id, 'e': e})
total = len(vnf_pm_threshold_ids)
if error_count > 0:
msg = (_("Failed to delete %(error_count)s of %(total)s "
"VNF PM thresholds.") %
{'error_count': error_count, 'total': total})
raise exceptions.CommandError(message=msg)
if total > 1:
print(_('All specified VNF PM thresholds are deleted '
'successfully'))
return
print(_("VNF PM threshold '%s' deleted "
"successfully") % vnf_pm_threshold_ids[0])

View File

@@ -18,6 +18,8 @@
Command-line interface to the Tacker APIs
"""
from __future__ import print_function
import argparse
import getpass
import inspect
@@ -25,10 +27,6 @@ import itertools
import logging
import os
import sys
from urllib import parse as urlparse
from cliff import app
from cliff import commandmanager
from keystoneclient.auth.identity import v2 as v2_auth
from keystoneclient.auth.identity import v3 as v3_auth
@@ -36,6 +34,10 @@ from keystoneclient import discover
from keystoneclient import exceptions as ks_exc
from keystoneclient import session
from oslo_utils import encodeutils
import six.moves.urllib.parse as urlparse
from cliff import app
from cliff import commandmanager
from tackerclient.common import clientmanager
from tackerclient.common import command as openstack_command
@@ -43,7 +45,16 @@ 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.version import __version__
@@ -96,13 +107,79 @@ class BashCompletionCommand(openstack_command.OpenStackCommand):
COMMAND_V1 = {
'bash-completion': BashCompletionCommand,
'ext-list': extension.ListExt,
'ext-show': extension.ShowExt,
# MANO lingo
'vnfd-create': vnfd.CreateVNFD,
'vnfd-delete': vnfd.DeleteVNFD,
'vnfd-list': vnfd.ListVNFD,
'vnfd-show': vnfd.ShowVNFD,
'vnfd-template-show': vnfd.ShowTemplateVNFD,
'vnf-create': vnf.CreateVNF,
'vnf-update': vnf.UpdateVNF,
'vnf-delete': vnf.DeleteVNF,
'vnf-list': vnf.ListVNF,
'vnf-show': vnf.ShowVNF,
'vnf-scale': vnf.ScaleVNF,
'vnf-resource-list': vnf.ListVNFResources,
# 'vnf-config-create'
# 'vnf-config-push'
'vim-register': vim.CreateVIM,
'vim-update': vim.UpdateVIM,
'vim-delete': vim.DeleteVIM,
'vim-list': vim.ListVIM,
'vim-show': vim.ShowVIM
'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}

View File

@@ -54,9 +54,9 @@ def make_client(instance):
auth=instance._auth)
return client
else:
raise exceptions.UnsupportedVersion(
reason=_("API version %s is not supported") %
instance._api_version[API_NAME])
raise exceptions.UnsupportedVersion(_("API version %s is not "
"supported") %
instance._api_version[API_NAME])
def Client(api_version, *args, **kwargs):

View File

@@ -14,6 +14,8 @@
# under the License.
#
from __future__ import print_function
import abc
import argparse
import logging
@@ -23,6 +25,7 @@ from cliff.formatters import table
from cliff import lister
from cliff import show
from oslo_serialization import jsonutils
import six
from tackerclient.common._i18n import _
from tackerclient.common import command
@@ -163,7 +166,7 @@ def _process_previous_argument(current_arg, _value_number, current_type_str,
if _value_number == 0 and (current_type_str or _list_flag):
# This kind of argument should have value
raise exceptions.CommandError(
message=_("Invalid values_specs %s") % ' '.join(values_specs))
_("Invalid values_specs %s") % ' '.join(values_specs))
if _value_number > 1 or _list_flag or current_type_str == 'list':
current_arg.update({'nargs': '+'})
elif _value_number == 0:
@@ -234,8 +237,7 @@ def parse_args_to_dict(values_specs):
_value_number = 0
if _item in _options:
raise exceptions.CommandError(
message=_("Duplicated "
"options %s") % ' '.join(values_specs))
_("Duplicated options %s") % ' '.join(values_specs))
else:
_options.update({_item: {}})
current_arg = _options[_item]
@@ -243,8 +245,7 @@ def parse_args_to_dict(values_specs):
elif _item.startswith('type='):
if current_arg is None:
raise exceptions.CommandError(
message=_("Invalid "
"values_specs %s") % ' '.join(values_specs))
_("Invalid values_specs %s") % ' '.join(values_specs))
if 'type' not in current_arg:
current_type_str = _item.split('=', 2)[1]
current_arg.update({'type': eval(current_type_str)})
@@ -266,8 +267,7 @@ def parse_args_to_dict(values_specs):
if (not current_item or '=' in current_item or
_item.startswith('-') and not is_number(_item)):
raise exceptions.CommandError(
message=_("Invalid "
"values_specs %s") % ' '.join(values_specs))
_("Invalid values_specs %s") % ' '.join(values_specs))
_value_number += 1
_values_specs.append(_item)
@@ -332,7 +332,6 @@ class TableFormater(table.TableFormatter):
https://bugs.launchpad.net/python-tackerclient/+bug/1165962
"""
def emit_list(self, column_names, data, stdout, parsed_args):
if column_names:
super(TableFormater, self).emit_list(column_names, data, stdout,
@@ -353,7 +352,8 @@ class TackerCommandMeta(abc.ABCMeta):
name, bases, cls_dict)
class TackerCommand(command.OpenStackCommand, metaclass=TackerCommandMeta):
@six.add_metaclass(TackerCommandMeta)
class TackerCommand(command.OpenStackCommand):
api = 'nfv-orchestration'
values_specs = []
@@ -374,12 +374,12 @@ class TackerCommand(command.OpenStackCommand, metaclass=TackerCommandMeta):
parser = super(TackerCommand, self).get_parser(prog_name)
parser.add_argument(
'--request-format',
help=_('The json request format'),
help=_('The xml or json request format'),
default='json',
choices=['json', ], )
choices=['json', 'xml', ], )
parser.add_argument(
'--request_format',
choices=['json', ],
choices=['json', 'xml', ],
help=argparse.SUPPRESS)
return parser
@@ -483,8 +483,7 @@ class UpdateCommand(TackerCommand):
body[self.resource] = _extra_values
if not body[self.resource]:
raise exceptions.CommandError(
message=_("Must specify new"
" values to update %s") % self.resource)
_("Must specify new values to update %s") % self.resource)
if self.allow_names:
_id = find_resourceid_by_name_or_id(
tacker_client, self.resource, parsed_args.id)
@@ -560,12 +559,12 @@ class DeleteCommand(TackerCommand):
'resource': self.resource})
err_msg = _("\n\nUnable to delete the below"
" %s(s):") % self.resource
for failed_id, error in failed_items.items():
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(message=msg)
raise exceptions.CommandError(msg)
else:
print((_('All specified %(resource)s(s) %(msg)s successfully')
% {'msg': self.deleted_msg.get(self.resource, 'deleted'),

View File

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

@@ -0,0 +1,34 @@
# Copyright 2012 OpenStack Foundation.
# All Rights Reserved
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
from tackerclient.tacker import v1_0 as cmd_base
class ListExt(cmd_base.ListCommand):
"""List all extensions."""
resource = 'extension'
list_columns = ['alias', 'name']
class ShowExt(cmd_base.ShowCommand):
"""Show information of a given resource."""
resource = "extension"
allow_names = False
def get_id(self):
return 'EXT-ALIAS'

View File

@@ -0,0 +1,139 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import yaml
from tackerclient.common import exceptions
from tackerclient.i18n import _
from tackerclient.tacker import v1_0 as tackerV10
_NS = 'ns'
_RESOURCE = 'resource'
class ListNS(tackerV10.ListCommand):
"""List NS that belong to a given tenant."""
resource = _NS
list_columns = ['id', 'name', 'nsd_id', 'vnf_ids', 'vnffg_ids',
'mgmt_ip_addresses', 'status']
class ShowNS(tackerV10.ShowCommand):
"""Show information of a given NS."""
resource = _NS
class CreateNS(tackerV10.CreateCommand):
"""Create a NS."""
resource = _NS
remove_output_fields = ["attributes"]
def add_known_arguments(self, parser):
parser.add_argument(
'name', metavar='NAME',
help=_('Set a name for the NS'))
parser.add_argument(
'--description',
help=_('Set description for the NS'))
nsd_group = parser.add_mutually_exclusive_group(required=True)
nsd_group.add_argument(
'--nsd-id',
help=_('NSD ID to use as template to create NS'))
nsd_group.add_argument(
'--nsd-template',
help=_('NSD file to create NS'))
nsd_group.add_argument(
'--nsd-name',
help=_('NSD name to use as template to create NS'))
vim_group = parser.add_mutually_exclusive_group()
vim_group.add_argument(
'--vim-id',
help=_('VIM ID to use to create NS on the specified VIM'))
vim_group.add_argument(
'--vim-name',
help=_('VIM name to use to create NS on the specified VIM'))
parser.add_argument(
'--vim-region-name',
help=_('VIM Region to use to create NS on the specified VIM'))
parser.add_argument(
'--param-file',
help=_('Specify parameter yaml file'))
def args2body(self, parsed_args):
args = {'attributes': {}}
body = {self.resource: args}
if parsed_args.vim_region_name:
args.setdefault('placement_attr', {})['region_name'] = \
parsed_args.vim_region_name
tacker_client = self.get_client()
tacker_client.format = parsed_args.request_format
if parsed_args.vim_name:
_id = tackerV10.find_resourceid_by_name_or_id(tacker_client,
'vim',
parsed_args.
vim_name)
parsed_args.vim_id = _id
if parsed_args.nsd_name:
_id = tackerV10.find_resourceid_by_name_or_id(tacker_client,
'nsd',
parsed_args.
nsd_name)
parsed_args.nsd_id = _id
elif parsed_args.nsd_template:
with open(parsed_args.nsd_template) as f:
template = f.read()
try:
args['nsd_template'] = yaml.load(
template, Loader=yaml.SafeLoader)
except yaml.YAMLError as e:
raise exceptions.InvalidInput(e)
if not args['nsd_template']:
raise exceptions.InvalidInput('The nsd file is empty')
if parsed_args.param_file:
with open(parsed_args.param_file) as f:
param_yaml = f.read()
try:
args['attributes']['param_values'] = yaml.load(
param_yaml, Loader=yaml.SafeLoader)
except yaml.YAMLError as e:
raise exceptions.InvalidInput(e)
tackerV10.update_dict(parsed_args, body[self.resource],
['tenant_id', 'name', 'description',
'nsd_id', 'vim_id'])
return body
class DeleteNS(tackerV10.DeleteCommand):
"""Delete given NS(s)."""
resource = _NS
deleted_msg = {'ns': 'delete initiated'}
def add_known_arguments(self, parser):
parser.add_argument(
'--force',
default=False,
action='store_true',
help=_('Force delete Network Service'))
def args2body(self, parsed_args):
if parsed_args.force:
body = {self.resource: {'attributes': {'force': True}}}
else:
body = dict()
return body

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

@@ -71,7 +71,7 @@ class CreateVIM(tackerV10.CreateCommand):
config_param = yaml.load(vim_config,
Loader=yaml.SafeLoader)
except yaml.YAMLError as e:
raise exceptions.InvalidInput(reason=e)
raise exceptions.InvalidInput(e)
vim_obj = body[self.resource]
try:
auth_url = config_param.pop('auth_url')
@@ -117,10 +117,9 @@ class UpdateVIM(tackerV10.UpdateCommand):
with open(parsed_args.config_file) as f:
config_yaml = f.read()
try:
config_param = yaml.load(config_yaml,
Loader=yaml.SafeLoader)
config_param = yaml.load(config_yaml)
except yaml.YAMLError as e:
raise exceptions.InvalidInput(reason=e)
raise exceptions.InvalidInput(e)
vim_obj = body[self.resource]
if config_param is not None:
vim_utils.args2body_vim(config_param, vim_obj)

View File

@@ -13,8 +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.
from urllib import parse as urlparse
import six.moves.urllib.parse as urlparse
from tackerclient.common import exceptions
@@ -63,25 +62,7 @@ def args2body_vim(config_param, vim):
message='Project name must be specified in Kubernetes VIM,'
'it is namespace in Kubernetes environment',
status_code=404)
if 'oidc_token_url' in config_param:
if ('username' not in config_param or
'password' not in config_param or
'client_id' not in config_param):
# the username, password, client_id are required.
# client_secret is not required when client type is public.
raise exceptions.TackerClientException(
message='oidc_token_url must be specified with username,'
' password, client_id, client_secret(optional).',
status_code=404)
vim['auth_cred'] = {
'oidc_token_url': config_param.pop('oidc_token_url'),
'username': config_param.pop('username'),
'password': config_param.pop('password'),
'client_id': config_param.pop('client_id')}
if 'client_secret' in config_param:
vim['auth_cred']['client_secret'] = config_param.pop(
'client_secret')
elif ('username' in config_param) and ('password' in config_param):
if ('username' in config_param) and ('password' in config_param):
vim['auth_cred'] = {
'username': config_param.pop('username', ''),
'password': config_param.pop('password', '')}
@@ -96,8 +77,6 @@ def args2body_vim(config_param, vim):
ssl_ca_cert = config_param.pop('ssl_ca_cert', '')
if ssl_ca_cert:
vim['auth_cred']['ssl_ca_cert'] = ssl_ca_cert
if 'extra' in config_param:
vim['extra'] = config_param.pop('extra')
def validate_auth_url(url):

View File

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

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

@@ -0,0 +1,99 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# 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,341 @@
#
# 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 oslo_utils import encodeutils
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):
group = parser.add_mutually_exclusive_group()
group.add_argument(
'--config-file',
help=_('YAML file with VNF configuration'))
group.add_argument(
'--config',
help=_('YAML data with VNF configuration'))
group.add_argument(
'--param-file',
help=_('YAML file with VNF parameter'))
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(reason=e)
if not config:
raise exceptions.InvalidInput(
reason='The config file is empty')
if parsed_args.config:
config_param = encodeutils.safe_decode(parsed_args.config)
try:
config = yaml.load(config_param, Loader=yaml.SafeLoader)
except yaml.YAMLError as e:
raise exceptions.InvalidInput(reason=e)
if not config:
raise exceptions.InvalidInput(
reason='The parameter is empty')
if config:
body[self.resource]['attributes'] = {'config': config}
if parsed_args.param_file:
with open(parsed_args.param_file) as f:
param_yaml = f.read()
try:
param = yaml.load(
param_yaml, Loader=yaml.SafeLoader)
except yaml.YAMLError as e:
raise exceptions.InvalidInput(reason=e)
if not param:
raise exceptions.InvalidInput(
reason='The parameter file is empty')
body[self.resource]['attributes'] = {'param_values': param}
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,115 @@
#
# 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.
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
_VNFD = "vnfd"
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
class ShowVNFD(tackerV10.ShowCommand):
"""Show information of a given VNFD."""
resource = _VNFD
class CreateVNFD(tackerV10.CreateCommand):
"""Create a VNFD."""
resource = _VNFD
remove_output_fields = ["attributes"]
def add_known_arguments(self, parser):
parser.add_argument('--vnfd-file', help=_('Specify VNFD file'))
parser.add_argument(
'name', metavar='NAME',
help=_('Set a name for the VNFD'))
parser.add_argument(
'--description',
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}
tackerV10.update_dict(parsed_args, body[self.resource],
['tenant_id', 'name', 'description'])
return body
class DeleteVNFD(tackerV10.DeleteCommand):
"""Delete given VNFD(s)."""
resource = _VNFD
class ShowTemplateVNFD(tackerV10.ShowCommand):
"""Show template of a given VNFD."""
resource = _VNFD
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('vnfd', None)
except (IndexError, TypeError, ValueError) as e:
self.log.debug('Data handling error: %s', str(e))
print(template or _('Unable to display VNFD template!'))

View File

@@ -13,16 +13,15 @@
# License for the specific language governing permissions and limitations
# under the License.
import mock
from requests_mock.contrib import fixture as requests_mock_fixture
import testtools
from unittest import mock
from cliff import columns as cliff_columns
class FixturedTestCase(testtools.TestCase):
client_fixture_class = None
api_version = '1'
def setUp(self):
super(FixturedTestCase, self).setUp()
@@ -30,8 +29,7 @@ class FixturedTestCase(testtools.TestCase):
if self.client_fixture_class:
self.requests_mock = self.useFixture(requests_mock_fixture.
Fixture())
fix = self.client_fixture_class(self.requests_mock,
api_version=self.api_version)
fix = self.client_fixture_class(self.requests_mock)
self.cs = self.useFixture(fix).client
def check_parser(self, cmd, args, verify_args):

View File

@@ -1,101 +0,0 @@
# Copyright (C) 2021 Nippon Telegraph and Telephone 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 os
import ddt
from unittest import mock
from tackerclient.common import exceptions
from tackerclient.osc.common.vnflcm import vnflcm_versions
from tackerclient.tests.unit.osc import base
from tackerclient.tests.unit.osc.v1.fixture_data import client
class TestVnfLcm(base.FixturedTestCase):
client_fixture_class = client.ClientFixture
def setUp(self):
super(TestVnfLcm, self).setUp()
self.url = client.TACKER_URL
self.header = {'content-type': 'application/json'}
self.app = mock.Mock()
self.app_args = mock.Mock()
self.client_manager = self.cs
self.app.client_manager.tackerclient = self.client_manager
@ddt.ddt
class TestVnfLcmVersions(TestVnfLcm):
def setUp(self):
super(TestVnfLcmVersions, self).setUp()
self.vnflcm_versions = vnflcm_versions.VnfLcmVersions(
self.app, self.app_args, cmd_name='vnflcm versions')
def _versions_response(self, major_version=None):
if major_version is None:
return {"uriPrefix": "/vnflcm",
"apiVersions": [{"version": "1.3.0",
"isDeprecated": False},
{"version": "2.0.0",
"isDeprecated": False}]}
elif major_version == "1":
return {"uriPrefix": "/vnflcm/v1",
"apiVersions": [{"version": "1.3.0",
"isDeprecated": False}]}
elif major_version == "2":
return {"uriPrefix": "/vnflcm/v2",
"apiVersions": [{"version": "2.0.0",
"isDeprecated": False}]}
def test_invalid_major_version(self):
parser = self.vnflcm_versions.get_parser('vnflcm versions')
parsed_args = parser.parse_args(["--major-version", "3"])
self.assertRaises(exceptions.InvalidInput,
self.vnflcm_versions.take_action,
parsed_args)
def test_take_action_no_arg(self):
parser = self.vnflcm_versions.get_parser('vnflcm versions')
parsed_args = parser.parse_args([])
response = self._versions_response()
self.requests_mock.register_uri(
'GET', os.path.join(self.url, 'vnflcm/api_versions'),
json=response, headers=self.header)
colmns, data = self.vnflcm_versions.take_action(parsed_args)
self.assertEqual(colmns, tuple(response.keys()))
self.assertEqual(data, tuple(response.values()))
@ddt.data('1', '2')
def test_take_action_with_major_version(self, major_version):
parser = self.vnflcm_versions.get_parser('vnflcm versions')
parsed_args = parser.parse_args(["--major-version",
major_version])
response = self._versions_response(major_version)
self.requests_mock.register_uri(
'GET',
os.path.join(self.url,
'vnflcm/v{}/api_versions'.format(major_version)),
json=response, headers=self.header)
colmns, data = self.vnflcm_versions.take_action(parsed_args)
self.assertEqual(colmns, tuple(response.keys()))
self.assertEqual(data, tuple(response.values()))

View File

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

@@ -0,0 +1,7 @@
auth_url: ][
username: 'xyz'
password: '12345'
project_name: 'abc'
project_domain_name: 'prj_domain_name'
user_domain_name: 'user_domain_name'
type: 'openstack'

View File

@@ -0,0 +1 @@
old_key: ][

View File

@@ -0,0 +1 @@
key: new-value

View File

@@ -25,8 +25,7 @@ TACKER_URL = 'http://nfv-orchestration'
class ClientFixture(fixtures.Fixture):
def __init__(self, requests_mock, identity_url=IDENTITY_URL,
api_version='1'):
def __init__(self, requests_mock, identity_url=IDENTITY_URL):
super(ClientFixture, self).__init__()
self.identity_url = identity_url
self.client = None
@@ -36,7 +35,6 @@ class ClientFixture(fixtures.Fixture):
self.discovery = fixture.V2Discovery(href=self.identity_url)
s = self.token.add_service('nfv-orchestration')
s.add_endpoint(TACKER_URL)
self.api_version = api_version
def setUp(self):
super(ClientFixture, self).setUp()
@@ -59,5 +57,4 @@ class ClientFixture(fixtures.Fixture):
region_name='RegionOne',
auth_url=self.identity_url,
token=self.token.token_id,
endpoint_url=TACKER_URL,
api_version=self.api_version)
endpoint_url=TACKER_URL)

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