Compare commits

..

30 Commits

Author SHA1 Message Date
d12f2e1f87 Update .gitreview for unmaintained/zed
Change-Id: I0a4f4d45b59c03946f88fa3aef13173b790246db
2024-05-08 12:40:17 +00:00
0368ff69ed Update TOX_CONSTRAINTS_FILE for stable/zed
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/zed branch, tests will
continue to use the upper-constraints list on master.

Change-Id: I7408256b1e3c00d45610398ea650101791663ea8
2022-09-08 15:52:46 +00:00
a5436ff335 Update .gitreview for stable/zed
Change-Id: I9aaf17b13be59a75a4895895dd5019337bc47e4e
2022-09-08 15:52:45 +00:00
Koji Shimizu
64d7a87670 Add support cnf auto heal and scale
Support container based VNF AutoHeal and AutoScale operation with
External Monitoring Tools.

Add the Fault Management interfaces and CLI to support AutoHeal.
Add the Performance Management interfaces and CLI to support
AutoScale. The Fault Management and Performance Management
interfaces are based on ETSI NFV-SOL 002 v3.3.1 and ETSI NFV-SOL
003 v3.3.1, which are Version "2.0.0" API of Tacker. Add the
Prometheus Plugin that has a interface between tacker and the
External Monitoring Tool.

Implements: blueprint support-auto-lcm
Change-Id: I7023a72b73f17f3746c74d311919497c7c4e8b3f
2022-09-01 12:16:57 +00:00
Zuul
3da27cc89a Merge "Support listing all records at once with paging" 2022-09-01 01:03:51 +00:00
Koichi Edagawa
06750997e6 Support listing all records at once with paging
This patch enables you to receive all records for target list
commands at once even if Tacker's server paginates them.

Target commands are below.

 - openstack vnflcm list
 - openstack vnflcm op list
 - openstack vnflcm subsc list

* As for the following command, it will be supported after
  implementing pagination feature in Tacker's server.

 - openstack vnf package list

Implements: blueprint paging-query-result
Change-Id: I8e5c9bdd99b9c1e45aef8aa1e74bdbbfdd7c5c89
2022-08-31 17:18:24 +09:00
Zuul
52ebdc0aec Merge "Update Python-TackerClient Documentation" 2022-08-30 16:49:10 +00:00
Hirofumi Noguchi
075a327f63 Update Python-TackerClient Documentation
Current Python-TackerClient Documentation does not reflect
the latest implementation.
This patch adds the vnflcm commands to the CLI usage,
revises the contributor guide, and makes some minor modifications.

Implements: blueprint update-tackerclient-documentation
Change-Id: Iacd871c52baa0e20c254b7c75d588b52c6086851
2022-08-30 21:15:56 +09:00
Hideki Matsuda
fa4a66858e Support for 'extra' parameters for vim register
If the `extra` parameter is present in the config-file specified
when the 'openstack vim register' command is executed,
the data is sent to the tacker.

Implements: blueprint remove-cnf-restriction
Change-Id: I13420945f7d4f81f48a5be7f8f92a0635fbef18a
2022-08-30 10:56:39 +00:00
Zuul
bfc0c8fdeb Merge "Add OpenID Connect Token Auth for k8s" 2022-08-25 09:36:16 +00:00
Yi Feng
0fcb9a59a1 Add OpenID Connect Token Auth for k8s
This patch adds openid auth info settings support when calling
vim register/update.

Implements: blueprint support-openid-k8s-vim
Change-Id: Id267050ac7adb349f562477dfc7aa6a53188b4cc
2022-08-18 12:26:30 +09:00
Ai Hamano
21f60be225 Add vnflcm subscriptions APIs
This patch supports CLI of vnflcm subscriptions APIs.

They are as follows.
* vnflcm subsc create
* vnflcm subsc delete
* vnflcm subsc list
* vnflcm subsc show

The output message of "vnflcm delete" was also modified
to match "vnflcm subsc delete".

Implements: blueprint support-nfv-solv3-subscriptions
Implements: blueprint support-etsi-nfv-specs
Change-Id: I9a839a3d131afc80fec0c2bbaa30443798117e0d
2022-07-21 14:56:56 +09:00
Yasufumi Ogawa
5c643d007e Add Python3 zed unit tests
This is an automatically generated patch to ensure unit testing
is in place for all the of the tested runtimes for zed.

See also the PTI in governance [1].

[1]:https://governance.openstack.org/tc/reference/project-testing-interface.html

Signed-off-by: Yasufumi Ogawa <yasufum.o@gmail.com>
Change-Id: I4ea4b74fed206aa79078796106c4cee990ad067c
2022-07-13 23:21:46 +00:00
50c6570c51 Update master for stable/yoga
Add file to the reno documentation build to show release notes for
stable/yoga.

Use pbr instruction to increment the minor version number
automatically so that master versions are higher than the versions on
stable/yoga.

Sem-Ver: feature
Change-Id: I4f89c2cfa951117c832498a17daeb92b4ce8c70f
2022-03-03 10:51:00 +00:00
Zuul
417f27b9b8 Merge "Support ChangeVNFPackage command in tackerclient" 2022-02-25 02:32:22 +00:00
Yi Feng
ab901b6466 Support ChangeVNFPackage command in tackerclient
Add ``openstack vnflcm change_vnfpkg`` to python-tackerclient. This
command can execute change current vnf package operation [1].

[1]
https://www.etsi.org/deliver/etsi_gs/NFV-SOL/001_099/003/03.03.01_60/gs_NFV-SOL003v030301p.pdf

Implements: blueprint upgrade-vnf-package
Change-Id: I0d9c88147dee3a273cb27224db6be6c91cf7fe55
2022-02-25 09:51:40 +09:00
Itsuro Oda
3724240344 Add vnflcm v2 APIs
This patch supports CLI of vnflcm v2 APIs which are added in Yoga
release.

They are as follows.
* vnflcm update
* vnflcm scale
* vnflcm heal
* vnflcm change-ext-conn
* vnflcm op fail
* vnflcm op retry
* vnflcm op rollback

They are common with v1 except heal. An optional parameter
'--additional-param-file' is added to heal CLI.

Implements: blueprint support-nfv-solv3-heal-vnf
Implements: blueprint support-nfv-solv3-change-external-connectivity
Implements: blueprint support-nfv-solv3-modify-vnf
Implements: blueprint support-nfv-solv3-scale-vnf
Implements: blueprint support-nfv-solv3-error-handling
Change-Id: If4acd9c4d35ce7a5bf6c23db1a8238654d536c87
2022-02-22 03:01:25 +00:00
Zuul
45412f63f0 Merge "Help message of heal cli modified" 2022-02-22 00:09:08 +00:00
Pooja Singla
cc73d9d099 Help message of heal cli modified
Usage message of all cli commands of tacker is constructed by
argument parser by default. It includes optional parameter
first and insert mandatory parameter at end.

All cli commands are implemented with this design only
except heal cli.

Heal cli is a special case in which optional parameter is
coming at end and mandatory parameter is coming before optional.
To fix this issue usage parameter of argument parser is set
in heal cli.

Closes-Bug: #1954744

Change-Id: I3b1d04df210ad07d4b9a99f300017d49e2b56f0b
2022-02-18 16:37:12 +05:30
Renu
1c73f54664 Fix in "vnflcm op list" CLI with exclude-fields
"vnflcm op list" with "--exclude-fields" is not
excluding parameters from CLI result.

Closes-Bug: 1953377
Change-Id: If47beccfbab4b85173bbe159905d0605e721cedd
2022-01-27 04:53:39 +00:00
Manpreet Kaur
b7f27c3dc6 Update python testing classifier
Yoga testing runtime[1] has been updated to add py39
testing as voting. Unit tests update are handled by the
job template change in openstack-zuul-job

- https://review.opendev.org/c/openstack/openstack-zuul-jobs/+/820286

this commit updates the classifier in setup.cfg file and envlist in
tox.ini.

[1] https://governance.openstack.org/tc/reference/runtimes/yoga.html

Change-Id: Ic732cbd41691b11ae500eb9465423bb1d57c9a1e
2021-12-14 21:18:41 +05:30
Zuul
ccda0487d7 Merge "Support of Cancel VNF command in openstackclient" 2021-12-13 18:35:34 +00:00
Hiromu Asahina
6834454309 Support of Cancel VNF command in openstackclient
Add ``openstack vnflcm op cancel`` to python-tackerclient. This
command can execute Cancel operation [1]. This API makes a transition a
LCM that is stopped in the PROCESSING state to the FAILED_TEMP state.

[1]
https://www.etsi.org/deliver/etsi_gs/NFV-SOL/001_099/003/03.03.01_60/gs_NFV-SOL003v030301p.pdf

Implements: blueprint support-cancel
Change-Id: Id957f6a7992f9fd5b04806aff3d76680d8d63a8d
Signed-off-by: Hiromu Asahina <hiromu.asahina.az@hco.ntt.co.jp>
2021-12-10 18:09:33 +09:00
Zuul
7ee70f5236 Merge "setup.cfg: Replace dashes with underscores" 2021-09-29 02:24:05 +00:00
a3d485cfc5 Add Python3 yoga unit tests
This is an automatically generated patch to ensure unit testing
is in place for all the of the tested runtimes for yoga.

See also the PTI in governance [1].

[1]: https://governance.openstack.org/tc/reference/project-testing-interface.html

Signed-off-by: Yasufumi Ogawa <yasufum.o@gmail.com>
Change-Id: I92cd2f1bfbc16bdd1e36e5e9c3500b488d17bd47
2021-09-24 09:32:35 +09:00
Yasufumi Ogawa
861a57594b Drop test for lower constraints
As we agreed on IRC meeting[1], remain lower constraints test only
master branch of tacker and tacker-horizon to difficulty of maintaining
package depencencies for the recent pip dependency resolver. This patch
is to drop lower constraints test from python-tackerclient.

[1] https://meetings.opendev.org/meetings/tacker/2021/tacker.2021-09-14-08.02.log.txt

Signed-off-by: Yasufumi Ogawa <yasufum.o@gmail.com>
Change-Id: I4e10e76fb2ab06fb3269f115b0bd9b893265e2e6
2021-09-22 23:19:36 +09:00
015e6e21df Update master for stable/xena
Add file to the reno documentation build to show release notes for
stable/xena.

Use pbr instruction to increment the minor version number
automatically so that master versions are higher than the versions on
stable/xena.

Sem-Ver: feature
Change-Id: I874fe47b95881f35cc9599087feb69810cd9c803
2021-09-22 06:39:15 +00:00
Zuul
aed2714275 Merge "Update master for stable/wallaby" 2021-09-22 05:40:28 +00:00
maaoyu
b6aec209fd setup.cfg: Replace dashes with underscores
Setuptools v54.1.0 introduces a warning that the use of dash-separated
options in 'setup.cfg' will not be supported in a future version [1].
Get ahead of the issue by replacing the dashes with underscores. Without
this, we see 'UserWarning' messages like the following on new enough
versions of setuptools:

  UserWarning: Usage of dash-separated 'description-file' will not be
  supported in future versions. Please use the underscore name
  'description_file' instead

[1] https://github.com/pypa/setuptools/commit/a2e9ae4cb

Change-Id: Iab9666e111d6a5c68fe0526e40189d330be0086f
2021-05-04 10:36:37 +08:00
4299140444 Update master for stable/wallaby
Add file to the reno documentation build to show release notes for
stable/wallaby.

Use pbr instruction to increment the minor version number
automatically so that master versions are higher than the versions on
stable/wallaby.

Sem-Ver: feature
Change-Id: I600ba860fd192d8262ade921b9edbb4da8c2a538
2021-03-18 10:45:24 +00:00
61 changed files with 4577 additions and 241 deletions

View File

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

View File

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

View File

@@ -11,14 +11,6 @@
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
=============
@@ -76,10 +68,32 @@ of individual command can be referred by **openstack help <command-name>**.
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 by providing the address information
of the VNF package.
openstack vnf package delete Delete given VNF package(s).
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 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 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.

View File

@@ -1,9 +1,48 @@
============
..
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
Operations for Legacy implementation
------------------------------------
.. toctree::
vnf_commands
vnf_descriptor_commands
vim_commands
ns_commands
ns_descriptor_commands
vnf_graph_commands
vnf_graph_descriptor_commands
vnf_chain_commands
vnf_classifier_commands
vnf_network_forwarding_path_commands
nfv_event_commands

View File

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

View File

@@ -0,0 +1,19 @@
..
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.
==============================================
Network Service Descriptor Management commands
==============================================
.. autoprogram-cliff:: openstack.tackerclient.v1
:command: ns *

View File

@@ -0,0 +1,19 @@
..
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.
==============================================
Network Service Descriptor Management commands
==============================================
.. autoprogram-cliff:: openstack.tackerclient.v1
:command: ns descriptor *

View File

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

@@ -0,0 +1,19 @@
..
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.
================================================
Service Function Chain (SFC) Management commands
================================================
.. autoprogram-cliff:: openstack.tackerclient.v1
:command: vnf chain *

View File

@@ -0,0 +1,19 @@
..
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.
===================================
Flow Classifier Management commands
===================================
.. autoprogram-cliff:: openstack.tackerclient.v1
:command: vnf classifier *

View File

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

View File

@@ -0,0 +1,19 @@
..
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 Descriptor (VNFD) Management commands
=========================================
.. autoprogram-cliff:: openstack.tackerclient.v1
:command: vnf descriptor *

View File

@@ -0,0 +1,19 @@
..
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 Forwarding Graph (VNFFG) Management commands
================================================
.. autoprogram-cliff:: openstack.tackerclient.v1
:command: vnf graph *

View File

@@ -0,0 +1,19 @@
..
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 Forwarding Graph Descriptor (VNFFGD) Management commands
============================================================
.. autoprogram-cliff:: openstack.tackerclient.v1
:command: vnf graph descriptor *

View File

@@ -0,0 +1,19 @@
..
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.
===========================================
Network Forwarding Path Management commands
===========================================
.. autoprogram-cliff:: openstack.tackerclient.v1
:command: vnf network forwarding path *

View File

@@ -1,3 +1,16 @@
..
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

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

@@ -11,14 +11,6 @@
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,14 +11,6 @@
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
===================================
@@ -42,154 +34,6 @@ For details please refer to the `OpenStack IRC meetings`_ page.
Testing
=======
Install the prerequisites for Tox:
* On Ubuntu or Debian:
.. code-block:: bash
$ apt-get install gcc gettext python-dev libxml2-dev libxslt1-dev \
zlib1g-dev
You may need to use pip install for some packages.
* On RHEL or CentOS including Fedora:
.. code-block:: bash
$ yum install gcc python-devel libxml2-devel libxslt-devel
* On openSUSE or SUSE linux Enterprise:
.. code-block:: bash
$ zypper install gcc python-devel libxml2-devel libxslt-devel
Install python-tox:
.. code-block:: bash
$ pip install tox
To run the full suite of tests maintained within TackerClient.
.. code-block:: bash
$ tox
.. NOTE::
The first time you run ``tox``, it will take additional time to build
virtualenvs. You can later use the ``-r`` option with ``tox`` to rebuild
your virtualenv in a similar manner.
To run tests for one or more specific test environments(for example, the
most common configuration of Python 2.7, Python 3.5 and PEP-8), list the
environments with the ``-e`` option, separated by spaces:
.. code-block:: bash
$ tox -e py27,py35,pep8
See ``tox.ini`` for the full list of available test environments.
Building the Documentation
==========================
The documentation is generated with Sphinx using the ``tox`` command. To
create HTML docs, run the commands:
.. code-block:: bash
$ tox -e docs
The resultant HTML will be in the ``doc/build/html`` directory.
Release Notes
=============
The release notes for a patch should be included in the patch. See the
`Project Team Guide`_ for more information on using reno in OpenStack.
.. _`Project Team Guide`: http://docs.openstack.org/project-team-guide/release-management.html#managing-release-notes
If any of the following applies to the patch, a release note is required:
* The deployer needs to take an action when upgrading
* The plugin interface changes
* A new feature is implemented
* A command or option is removed
* Current behavior is changed
* A security bug is fixed
Reno is used to generate release notes. Use the commands:
.. code-block:: bash
$ tox -e venv -- reno new <bug-,bp-,whatever>
Then edit the sample file that was created and push it with your change.
To run the commands and see results:
.. code-block:: bash
$ git commit # Commit the change because reno scans git log.
$ tox -e releasenotes
At last, look at the generated release notes
files in ``releasenotes/build/html`` in your browser.
Testing new code
================
If a developer wants to test new code (feature, command or option) that
they have written, Python-TackerClient may be installed from source by running
the following commands in the base directory of the project:
.. code-block:: bash
$ python setup.py install
or
.. code-block:: bash
$ pip install -e .
Standardize Import Format
=========================
.. _`Import Order Guide`: https://docs.openstack.org/hacking/latest/user/hacking.html#imports
The import order shows below:
* {{stdlib imports in human alphabetical order}}
* \n
* {{third-party lib imports in human alphabetical order}}
* \n
* {{project imports in human alphabetical order}}
* \n
* \n
* {{begin your code}}
Example
~~~~~~~
.. code-block:: python
import copy
import fixtures
import mock
import os
from osc_lib.api import auth
from osc_lib import utils
import six
from openstackclient import shell
from openstackclient.tests import utils
For details please refer to the `Developing with OpenStackClient`_ page.
.. _`Developing with OpenStackClient`: https://docs.openstack.org/python-openstackclient/latest/contributor/developing.html

View File

@@ -11,14 +11,6 @@
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,19 +11,12 @@
License for the specific language governing permissions and limitations
under the License.
Convention for heading levels in Neutron devref:
======= Heading 0 (reserved for the title in a document)
------- Heading 1
~~~~~~~ Heading 2
+++++++ Heading 3
''''''' Heading 4
(Avoid deeper levels because they do not render well.)
=================================
Python-TackerClient Documentation
=================================
This is a client for OpenStack NFV MANO (Tacker) API. It provides
Python-TackerClient 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>`.

View File

@@ -11,16 +11,9 @@
License for the specific language governing permissions and limitations
under the License.
Convention for heading levels in Neutron devref:
======= Heading 0 (reserved for the title in a document)
------- Heading 1
~~~~~~~ Heading 2
+++++++ Heading 3
''''''' Heading 4
(Avoid deeper levels because they do not render well.)
=========
Reference
=========
(To be updated)
- `Tacker API reference <https://docs.openstack.org/api-ref/nfv-orchestration/>`_
- `Tacker CLI reference <https://docs.openstack.org/tacker/latest/cli/>`_

View File

@@ -7,6 +7,9 @@ Contents:
:maxdepth: 2
unreleased
yoga
xena
wallaby
victoria
ussuri
train

View File

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

View File

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

View File

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

View File

@@ -1,12 +1,12 @@
[metadata]
name = python-tackerclient
summary = CLI and Client Library for OpenStack Tacker
description-file =
description_file =
README.rst
author = OpenStack
author-email = openstack-discuss@lists.openstack.org
home-page = https://docs.openstack.org/python-tackerclient/
python-requires = >=3.6
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
@@ -20,6 +20,8 @@ classifier =
Programming Language :: Python :: 3
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
[files]
packages =
@@ -96,10 +98,15 @@ openstack.tackerclient.v1 =
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
@@ -107,7 +114,32 @@ openstack.tackerclient.v2 =
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
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

View File

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

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

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

View File

@@ -238,6 +238,12 @@ 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>",
@@ -251,6 +257,11 @@ 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):
@@ -259,6 +270,8 @@ 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(jsonfile2body(parsed_args.additional_param_file))
return body
@@ -334,7 +347,7 @@ class TerminateVnfLcm(command.Command):
result = client.delete_vnf_instance(parsed_args.vnf_instance)
if not result:
print(_("VNF Instance '%(id)s' deleted successfully") %
print(_("VNF Instance '%(id)s' is deleted successfully") %
{'id': parsed_args.vnf_instance})
def _wait_until_vnf_is_terminated(self, client, vnf_instance_id,
@@ -407,7 +420,7 @@ class DeleteVnfLcm(command.Command):
print(_('All specified vnf instances are deleted '
'successfully'))
else:
print(_("Vnf instance '%s' deleted "
print(_("Vnf instance '%s' is deleted "
"successfully") % vnf_instances[0])
@@ -566,3 +579,30 @@ class ChangeExtConnVnfLcm(command.Command):
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, 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

@@ -101,6 +101,47 @@ class RollbackVnfLcmOp(command.Command):
' 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")
@@ -201,9 +242,12 @@ class ListVnfLcmOp(command.Lister):
)
return parser
def get_attributes(self):
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
@@ -229,10 +273,13 @@ class ListVnfLcmOp(command.Lister):
]
attributes = []
for field in fields:
attributes.extend([(field['key'], field['value'],
tacker_osc_utils.LIST_BOTH)])
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):
@@ -260,7 +307,7 @@ class ListVnfLcmOp(command.Lister):
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(),
self.get_attributes(exclude=exclude_fields),
long_listing=True)
dictionary_properties = (utils.get_dict_properties(

View File

@@ -0,0 +1,206 @@
# 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 json
import logging
import os
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)
def jsonfile2body(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")
reason = msg % file_path
raise exceptions.InvalidInput(reason=reason)
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.InvalidInput(reason=reason)
return body
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(
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

View File

View File

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

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

@@ -0,0 +1,197 @@
# 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 json
import logging
import os
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)
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
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(
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

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

View File

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

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

@@ -0,0 +1,334 @@
# 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 json
import logging
import os
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
}
_FORMATTERS_UPDATE = {
'authentication': 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',
'authentication': 'Authentication'
}
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)
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
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(
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,
jsonfile2body(parsed_args.request_file))
display_columns, columns = _get_columns(updated_values, 'update')
data = utils.get_item_properties(
sdk_utils.DictModel(updated_values),
columns, formatters=_FORMATTERS_UPDATE,
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

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

@@ -63,7 +63,25 @@ 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 ('username' in config_param) and ('password' in config_param):
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):
vim['auth_cred'] = {
'username': config_param.pop('username', ''),
'password': config_param.pop('password', '')}
@@ -78,6 +96,9 @@ 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
extra = config_param.pop('extra', {})
if extra:
vim['extra'] = extra
def validate_auth_url(url):

View File

@@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import copy
from io import StringIO
import os
import sys
@@ -182,6 +183,48 @@ class TestListVnfLcm(TestVnfLcm):
actual_columns)
self.assertCountEqual(expected_data, list(data))
def test_take_action_with_pagination(self):
next_links_num = 3
parsed_args = self.check_parser(self.list_vnf_instance, [], [])
path = os.path.join(self.url, 'vnflcm/v1/vnf_instances')
links = [0] * next_links_num
link_headers = [0] * next_links_num
for i in range(next_links_num):
links[i] = (
'{base_url}?nextpage_opaque_marker={vnf_instance_id}'.format(
base_url=path,
vnf_instance_id=self.vnf_instances[i]['id']))
link_headers[i] = copy.deepcopy(self.header)
link_headers[i]['Link'] = '<{link_url}>; rel="next"'.format(
link_url=links[i])
self.requests_mock.register_uri(
'GET', path, json=[self.vnf_instances[0]], headers=link_headers[0])
self.requests_mock.register_uri(
'GET', links[0], json=[self.vnf_instances[1]],
headers=link_headers[1])
self.requests_mock.register_uri(
'GET', links[1], json=[self.vnf_instances[2]],
headers=link_headers[2])
self.requests_mock.register_uri(
'GET', links[2], json=[], headers=self.header)
actual_columns, data = self.list_vnf_instance.take_action(parsed_args)
headers, columns = tacker_osc_utils.get_column_definitions(
vnflcm._attr_map, long_listing=True)
expected_data = []
for vnf_instance_obj in self.vnf_instances:
expected_data.append(vnflcm_fakes.get_vnflcm_data(
vnf_instance_obj, columns=columns, list_action=True))
self.assertCountEqual(_get_columns_vnflcm(action='list'),
actual_columns)
self.assertCountEqual(expected_data, list(data))
class TestInstantiateVnfLcm(TestVnfLcm):
@@ -288,6 +331,9 @@ class TestHealVnfLcm(TestVnfLcm):
self.heal_vnf_lcm = vnflcm.HealVnfLcm(
self.app, self.app_args, cmd_name='vnflcm heal')
_heal_sample_param_file = ("./tackerclient/osc/v1/vnflcm/samples/"
"heal_vnf_instance_param_sample.json")
@ddt.data((['--cause', 'test-cause', "--vnfc-instance",
'vnfc-id-1', 'vnfc-id-2'],
[('cause', 'test-cause'),
@@ -296,6 +342,8 @@ class TestHealVnfLcm(TestVnfLcm):
[('cause', 'test-cause')]),
(["--vnfc-instance", 'vnfc-id-1', 'vnfc-id-2'],
[('vnfc_instance', ['vnfc-id-1', 'vnfc-id-2'])]),
(["--additional-param-file", _heal_sample_param_file],
[('additional_param_file', _heal_sample_param_file)]),
([], []))
@ddt.unpack
def test_take_action(self, arglist, verifylist):
@@ -340,6 +388,25 @@ class TestHealVnfLcm(TestVnfLcm):
self.heal_vnf_lcm.take_action,
parsed_args)
def test_take_action_param_file_not_exists(self):
vnf_instance = vnflcm_fakes.vnf_instance_response()
sample_param_file = "./not_exists.json"
arglist = [vnf_instance['id'],
'--additional-param-file', sample_param_file]
verifylist = [('vnf_instance', vnf_instance['id']),
('additional_param_file', sample_param_file)]
# command param
parsed_args = self.check_parser(self.heal_vnf_lcm, arglist,
verifylist)
ex = self.assertRaises(exceptions.InvalidInput,
self.heal_vnf_lcm.take_action, parsed_args)
expected_msg = ("Invalid input: File %s does not exist "
"or user does not have read privileges to it")
self.assertEqual(expected_msg % sample_param_file, str(ex))
@ddt.ddt
class TestTerminateVnfLcm(TestVnfLcm):
@@ -396,7 +463,7 @@ class TestTerminateVnfLcm(TestVnfLcm):
self.assertIn(expected_message, actual_message)
if delete_vnf:
expected_message = ("VNF Instance '%s' deleted successfully"
expected_message = ("VNF Instance '%s' is deleted successfully"
% vnf_instance['id'])
self.assertIn(expected_message, actual_message)
@@ -512,7 +579,7 @@ class TestDeleteVnfLcm(TestVnfLcm):
sys.stdout = buffer = StringIO()
result = self.delete_vnf_instance.take_action(parsed_args)
self.assertIsNone(result)
self.assertEqual(("Vnf instance '%s' deleted successfully")
self.assertEqual(("Vnf instance '%s' is deleted successfully")
% self.vnf_instances[0]['id'],
buffer.getvalue().strip())
@@ -850,6 +917,40 @@ class TestChangeExtConnVnfLcm(TestVnfLcm):
self.assertIn(expected_msg, str(ex))
class TestChangeVnfPkgVnfLcm(TestVnfLcm):
def setUp(self):
super(TestChangeVnfPkgVnfLcm, self).setUp()
self.change_vnfpkg_vnf_lcm = vnflcm.ChangeVnfPkgVnfLcm(
self.app, self.app_args,
cmd_name='vnflcm change-vnfpkg')
def test_take_action_with_v1_version(self):
vnf_instance = vnflcm_fakes.vnf_instance_response()
sample_param_file = ("./tackerclient/osc/v2/vnflcm/samples/"
"change_vnfpkg_vnf_instance_param_sample.json")
arglist = [vnf_instance['id'], sample_param_file]
verifylist = [('vnf_instance', vnf_instance['id']),
('request_file', sample_param_file)]
# command param
parsed_args = self.check_parser(self.change_vnfpkg_vnf_lcm,
arglist,
verifylist)
url = os.path.join(self.url, 'vnflcm/v1/vnf_instances',
vnf_instance['id'], 'change_vnfpkg')
self.requests_mock.register_uri(
'POST', url, headers=self.header, status_code=400, json={})
ex = self.assertRaises(exceptions.UnsupportedCommandVersion,
self.change_vnfpkg_vnf_lcm.take_action,
parsed_args)
expected_msg = "This command is not supported in version 1"
self.assertEqual(expected_msg, str(ex))
class TestVnfLcmV1(base.FixturedTestCase):
client_fixture_class = client.ClientFixture
api_version = '1'

View File

@@ -10,10 +10,12 @@
# License for the specific language governing permissions and limitations
# under the License.
import copy
from io import StringIO
import os
import sys
import ddt
from oslo_utils.fixture import uuidsentinel
from unittest import mock
@@ -25,7 +27,7 @@ from tackerclient.tests.unit.osc.v1.fixture_data import client
from tackerclient.tests.unit.osc.v1 import vnflcm_op_occs_fakes
def _get_columns_vnflcm_op_occs(action='show'):
def _get_columns_vnflcm_op_occs(action='show', parameter=None):
if action == 'fail':
return ['ID', 'Operation State', 'State Entered Time',
@@ -33,8 +35,11 @@ def _get_columns_vnflcm_op_occs(action="/?originalUrl=https%3A%2F%2Fgit.openstack.org%2F%26%2339%3Bshow%26%2339%3B)%3A%253C%2Fcode">
'Is Automatic Invocation', 'Is Cancel Pending',
'Error', 'Links']
elif action == 'list':
return ['ID', 'Operation State', 'VNF Instance ID',
'Operation']
if parameter is not None:
return ['ID', 'Operation']
else:
return ['ID', 'Operation State', 'VNF Instance ID',
'Operation']
else:
return ['ID', 'Operation State', 'State Entered Time',
'Start Time', 'VNF Instance ID', 'Grant ID',
@@ -57,6 +62,70 @@ class TestVnfLcm(base.FixturedTestCase):
self.app.client_manager.tackerclient = self.client_manager
@ddt.ddt
class TestCancelVnfLcmOp(TestVnfLcm):
def setUp(self):
super(TestCancelVnfLcmOp, self).setUp()
self.cancel_vnf_lcm = vnflcm_op_occs.CancelVnfLcmOp(
self.app, self.app_args, cmd_name='vnflcm op cancel')
@ddt.data('GRACEFUL', 'FORCEFUL')
def test_take_action(self, cancel_mode):
"""take_action normal system test"""
arglist = ['--cancel-mode', cancel_mode,
uuidsentinel.vnf_lcm_op_occ_id]
verifylist = [('cancel_mode', cancel_mode),
('vnf_lcm_op_occ_id', uuidsentinel.vnf_lcm_op_occ_id)]
parsed_args = self.check_parser(
self.cancel_vnf_lcm, arglist, verifylist)
url = os.path.join(
self.url,
'vnflcm/v1/vnf_lcm_op_occs',
uuidsentinel.vnf_lcm_op_occ_id,
'cancel')
self.requests_mock.register_uri(
'POST', url, headers=self.header, json={})
sys.stdout = buffer = StringIO()
self.cancel_vnf_lcm.take_action(parsed_args)
actual_message = buffer.getvalue().strip()
expected_message = (
'Cancel request for LCM operation ' +
uuidsentinel.vnf_lcm_op_occ_id +
' has been accepted')
self.assertEqual(expected_message, actual_message)
def test_terminate_no_options(self):
self.assertRaises(base.ParserException, self.check_parser,
self.cancel_vnf_lcm, [], [])
def test_take_action_vnf_lcm_op_occ_id_not_found(self):
"""take_action abnomaly system test"""
arglist = [uuidsentinel.vnf_lcm_op_occ_id]
verifylist = [('vnf_lcm_op_occ_id', uuidsentinel.vnf_lcm_op_occ_id)]
parsed_args = self.check_parser(
self.cancel_vnf_lcm, arglist, verifylist)
url = os.path.join(
self.url,
'vnflcm/v1/vnf_lcm_op_occs',
uuidsentinel.vnf_lcm_op_occ_id,
'cancel')
self.requests_mock.register_uri(
'POST', url, headers=self.header, status_code=404, json={})
self.assertRaises(exceptions.TackerClientException,
self.cancel_vnf_lcm.take_action,
parsed_args)
class TestRollbackVnfLcmOp(TestVnfLcm):
def setUp(self):
@@ -431,6 +500,80 @@ class TestListVnfLcmOp(TestVnfLcm):
self.list_vnflcm_op_occ.take_action,
parsed_args)
def test_take_action_with_exclude_fields(self):
vnflcm_op_occs_obj = vnflcm_op_occs_fakes.create_vnflcm_op_occs(
count=3)
parsed_args = self.check_parser(
self.list_vnflcm_op_occ,
["--exclude-fields", 'VNF Instance ID,Operation State'],
[('exclude_fields', 'VNF Instance ID,Operation State')])
self.requests_mock.register_uri(
'GET', os.path.join(
self.url,
'vnflcm/v1/vnf_lcm_op_occs?'
'exclude-fields=VNF Instance ID,Operation State'),
json=vnflcm_op_occs_obj, headers=self.header)
actual_columns, data = self.list_vnflcm_op_occ.take_action(parsed_args)
headers, columns = tacker_osc_utils.get_column_definitions(
self.list_vnflcm_op_occ.get_attributes(
exclude=['VNF Instance ID', 'Operation State']),
long_listing=True)
expected_data = []
for vnflcm_op_occ_obj_idx in vnflcm_op_occs_obj:
expected_data.append(
vnflcm_op_occs_fakes.get_vnflcm_op_occ_data(
vnflcm_op_occ_obj_idx, columns=columns))
self.assertCountEqual(_get_columns_vnflcm_op_occs(
action='list', parameter="exclude_fields"),
actual_columns)
self.assertListItemsEqual(expected_data, list(data))
def test_take_action_with_pagination(self):
next_links_num = 3
vnflcm_op_occs_obj = vnflcm_op_occs_fakes.create_vnflcm_op_occs(
count=next_links_num)
parsed_args = self.check_parser(self.list_vnflcm_op_occ, [], [])
path = os.path.join(self.url, 'vnflcm/v1/vnf_lcm_op_occs')
links = [0] * next_links_num
link_headers = [0] * next_links_num
for i in range(next_links_num):
links[i] = (
'{base_url}?nextpage_opaque_marker={vnflcm_op_occ_id}'.format(
base_url=path,
vnflcm_op_occ_id=vnflcm_op_occs_obj[i]['id']))
link_headers[i] = copy.deepcopy(self.header)
link_headers[i]['Link'] = '<{link_url}>; rel="next"'.format(
link_url=links[i])
self.requests_mock.register_uri(
'GET', path, json=[vnflcm_op_occs_obj[0]], headers=link_headers[0])
self.requests_mock.register_uri(
'GET', links[0], json=[vnflcm_op_occs_obj[1]],
headers=link_headers[1])
self.requests_mock.register_uri(
'GET', links[1], json=[vnflcm_op_occs_obj[2]],
headers=link_headers[2])
self.requests_mock.register_uri(
'GET', links[2], json=[], headers=self.header)
actual_columns, data = self.list_vnflcm_op_occ.take_action(parsed_args)
headers, columns = tacker_osc_utils.get_column_definitions(
self.list_vnflcm_op_occ.get_attributes(), long_listing=True)
expected_data = []
for vnflcm_op_occ_obj_idx in vnflcm_op_occs_obj:
expected_data.append(vnflcm_op_occs_fakes.get_vnflcm_op_occ_data(
vnflcm_op_occ_obj_idx, columns=columns))
self.assertCountEqual(_get_columns_vnflcm_op_occs(action='list'),
actual_columns)
self.assertCountEqual(expected_data, list(data))
class TestShowVnfLcmOp(TestVnfLcm):

View File

@@ -0,0 +1,254 @@
# 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 copy
import os
import sys
from io import StringIO
from tackerclient.common import exceptions
from tackerclient.osc import utils as tacker_osc_utils
from tackerclient.osc.v1.vnflcm import vnflcm_subsc
from tackerclient.tests.unit.osc import base
from tackerclient.tests.unit.osc.v1 import test_vnflcm
from tackerclient.tests.unit.osc.v1 import vnflcm_subsc_fakes
def _get_columns_vnflcm_subsc(action=None):
columns = ['ID', 'Filter', 'Callback URI', 'Links']
if action == 'list':
columns = [ele for ele in columns if ele not in
['Filter', 'Links']]
return columns
class TestCreateLccnSubscription(test_vnflcm.TestVnfLcm):
def setUp(self):
super(TestCreateLccnSubscription, self).setUp()
self.create_subscription = vnflcm_subsc.CreateLccnSubscription(
self.app, self.app_args, cmd_name='vnflcm subsc create')
def test_create_no_args(self):
self.assertRaises(base.ParserException, self.check_parser,
self.create_subscription, [], [])
def test_take_action(self):
subscription = vnflcm_subsc_fakes.lccn_subsc_response()
sample_param_file = ("./tackerclient/osc/v1/vnflcm/samples/"
"create_lccn_subscription_param_sample.json")
arglist = [sample_param_file]
verifylist = [('create_request_file', sample_param_file)]
# command param
parsed_args = self.check_parser(self.create_subscription,
arglist, verifylist)
self.requests_mock.register_uri(
'POST', os.path.join(self.url, 'vnflcm/v1/subscriptions'),
json=subscription, headers=self.header)
actual_columns, data = (self.create_subscription.take_action(
parsed_args))
headers, attributes = vnflcm_subsc._get_columns(subscription)
self.assertCountEqual(_get_columns_vnflcm_subsc(),
actual_columns)
self.assertListItemsEqual(vnflcm_subsc_fakes.get_subscription_data(
subscription, columns=attributes), data)
class TestListLccnSubscription(test_vnflcm.TestVnfLcm):
subscriptions = vnflcm_subsc_fakes.create_subscriptions(count=3)
def setUp(self):
super(TestListLccnSubscription, self).setUp()
self.list_subscription = vnflcm_subsc.ListLccnSubscription(
self.app, self.app_args, cmd_name='vnflcm subsc list')
def test_take_action(self):
parsed_args = self.check_parser(self.list_subscription, [], [])
self.requests_mock.register_uri(
'GET', os.path.join(self.url, 'vnflcm/v1/subscriptions'),
json=self.subscriptions, headers=self.header)
actual_columns, data = self.list_subscription.take_action(parsed_args)
headers, columns = tacker_osc_utils.get_column_definitions(
self.list_subscription.get_attributes(), long_listing=True)
expected_data = []
for subscription_obj in self.subscriptions:
expected_data.append(vnflcm_subsc_fakes.get_subscription_data(
subscription_obj, columns=columns, list_action=True))
self.assertCountEqual(_get_columns_vnflcm_subsc(action='list'),
actual_columns)
self.assertCountEqual(expected_data, list(data))
def test_take_action_with_pagination(self):
next_links_num = 3
path = os.path.join(self.url, 'vnflcm/v1/subscriptions')
parsed_args = self.check_parser(self.list_subscription, [], [])
links = [0] * next_links_num
link_headers = [0] * next_links_num
for i in range(next_links_num):
links[i] = (
'{base_url}?nextpage_opaque_marker={subscription_id}'.format(
base_url=path,
subscription_id=self.subscriptions[i]['id']))
link_headers[i] = copy.deepcopy(self.header)
link_headers[i]['Link'] = '<{link_url}>; rel="next"'.format(
link_url=links[i])
self.requests_mock.register_uri(
'GET', path, json=[self.subscriptions[0]], headers=link_headers[0])
self.requests_mock.register_uri(
'GET', links[0], json=[self.subscriptions[1]],
headers=link_headers[1])
self.requests_mock.register_uri(
'GET', links[1], json=[self.subscriptions[2]],
headers=link_headers[2])
self.requests_mock.register_uri(
'GET', links[2], json=[], headers=self.header)
actual_columns, data = self.list_subscription.take_action(parsed_args)
headers, columns = tacker_osc_utils.get_column_definitions(
self.list_subscription.get_attributes(), long_listing=True)
expected_data = []
for subscription_obj in self.subscriptions:
expected_data.append(vnflcm_subsc_fakes.get_subscription_data(
subscription_obj, columns=columns, list_action=True))
self.assertCountEqual(_get_columns_vnflcm_subsc(action='list'),
actual_columns)
self.assertCountEqual(expected_data, list(data))
class TestShowLccnSubscription(test_vnflcm.TestVnfLcm):
def setUp(self):
super(TestShowLccnSubscription, self).setUp()
self.show_subscription = vnflcm_subsc.ShowLccnSubscription(
self.app, self.app_args, cmd_name='vnflcm subsc show')
def test_take_action(self):
subscription = vnflcm_subsc_fakes.lccn_subsc_response()
arglist = [subscription['id']]
verifylist = [('subscription_id', subscription['id'])]
# command param
parsed_args = self.check_parser(self.show_subscription, arglist,
verifylist)
self.requests_mock.register_uri(
'GET', os.path.join(self.url, 'vnflcm/v1/subscriptions',
subscription['id']),
json=subscription, headers=self.header)
columns, data = (self.show_subscription.take_action(parsed_args))
self.assertCountEqual(_get_columns_vnflcm_subsc(),
columns)
headers, attributes = vnflcm_subsc._get_columns(subscription)
self.assertListItemsEqual(
vnflcm_subsc_fakes.get_subscription_data(subscription,
columns=attributes),
data)
class TestDeleteLccnSubscription(test_vnflcm.TestVnfLcm):
subscriptions = vnflcm_subsc_fakes.create_subscriptions(count=3)
def setUp(self):
super(TestDeleteLccnSubscription, self).setUp()
self.delete_subscription = vnflcm_subsc.DeleteLccnSubscription(
self.app, self.app_args, cmd_name='vnflcm subsc delete')
def _mock_request_url_for_delete(self, subsc_index):
url = os.path.join(self.url, 'vnflcm/v1/subscriptions',
self.subscriptions[subsc_index]['id'])
json = self.subscriptions[subsc_index]
self.requests_mock.register_uri('GET', url, json=json,
headers=self.header)
self.requests_mock.register_uri('DELETE', url,
headers=self.header, json={})
def test_delete_one_subscription(self):
arglist = [self.subscriptions[0]['id']]
verifylist = [('subscription_id',
[self.subscriptions[0]['id']])]
parsed_args = self.check_parser(self.delete_subscription,
arglist, verifylist)
self._mock_request_url_for_delete(0)
sys.stdout = buffer = StringIO()
result = self.delete_subscription.take_action(parsed_args)
self.assertIsNone(result)
self.assertEqual(("Lccn Subscription '%s' is deleted successfully")
% self.subscriptions[0]['id'],
buffer.getvalue().strip())
def test_delete_multiple_subscription(self):
arglist = []
for subscription in self.subscriptions:
arglist.append(subscription['id'])
verifylist = [('subscription_id', arglist)]
parsed_args = self.check_parser(self.delete_subscription,
arglist, verifylist)
for i in range(0, 3):
self._mock_request_url_for_delete(i)
sys.stdout = buffer = StringIO()
result = self.delete_subscription.take_action(parsed_args)
self.assertIsNone(result)
self.assertEqual('All specified Lccn Subscriptions are deleted '
'successfully', buffer.getvalue().strip())
def test_delete_multiple_subscription_exception(self):
arglist = [
self.subscriptions[0]['id'],
'xxxx-yyyy-zzzz',
self.subscriptions[1]['id'],
]
verifylist = [('subscription_id', arglist)]
parsed_args = self.check_parser(self.delete_subscription,
arglist, verifylist)
self._mock_request_url_for_delete(0)
url = os.path.join(self.url, 'vnflcm/v1/subscriptions',
'xxxx-yyyy-zzzz')
self.requests_mock.register_uri(
'GET', url, exc=exceptions.ConnectionFailed)
self._mock_request_url_for_delete(1)
exception = self.assertRaises(exceptions.CommandError,
self.delete_subscription.take_action,
parsed_args)
self.assertEqual('Failed to delete 1 of 3 Lccn Subscriptions.',
exception.message)

View File

@@ -0,0 +1,190 @@
# 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.
from oslo_utils.fixture import uuidsentinel
from oslo_utils import uuidutils
from tackerclient.osc import utils as tacker_osc_utils
def lccn_subsc_response(attrs=None):
"""Create a fake subscription.
:param Dictionary attrs:
A dictionary with all attributes
:return:
A subscription dict
"""
attrs = attrs or {}
id = uuidsentinel.lccn_subsc_id
# Set default attributes.
dummy_subscription = {
"id": id,
"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",
"_links": {
"self": {
"href": "http://127.0.0.1:9890/vnflcm/v2/subscriptions/" + id
}
}
}
# Overwrite default attributes.
dummy_subscription.update(attrs)
return dummy_subscription
def get_subscription_data(subscription, list_action=False, columns=None):
"""Get the subscription data.
:return:
A tuple object sorted based on the name of the columns.
"""
complex_attributes = ['filter', '_links']
for attribute in complex_attributes:
if subscription.get(attribute):
subscription.update(
{attribute: tacker_osc_utils.FormatComplexDataColumn(
subscription[attribute])})
if list_action:
for item in ['filter', '_links']:
subscription.pop(item)
# return the list of data as per column order
if columns:
return tuple([subscription[key] for key in columns])
return tuple([subscription[key] for key in sorted(
subscription.keys())])
def create_subscriptions(count=2):
"""Create multiple fake subscriptions.
:param count: The number of subscriptions to fake
:return:
A list of fake subscriptions dictionary
"""
uri = "http://localhost:9990/notification/callback/"
subscriptions = []
for i in range(0, count):
unique_id = uuidutils.generate_uuid()
subscriptions.append(lccn_subsc_response(
attrs={'id': unique_id,
'callbackUri': uri + str(i)}))
return subscriptions

View File

@@ -0,0 +1,307 @@
# 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 ddt
import os
from oslo_utils.fixture import uuidsentinel
from unittest import mock
from tackerclient.common import exceptions
from tackerclient.osc import utils as tacker_osc_utils
from tackerclient.osc.v2.vnffm import vnffm_alarm
from tackerclient.tests.unit.osc import base
from tackerclient.tests.unit.osc.v1.fixture_data import client
from tackerclient.tests.unit.osc.v2 import vnffm_alarm_fakes
class TestVnfFmAlarm(base.FixturedTestCase):
client_fixture_class = client.ClientFixture
def setUp(self):
super(TestVnfFmAlarm, 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
def _get_columns_vnffm_alarm(action=None):
if action == 'update':
columns = ['Ack State']
else:
columns = ['ID', 'Managed Object Id', 'Ack State',
'Perceived Severity', 'Event Type', 'Probable Cause']
if action == 'show':
columns.extend([
'Vnfc Instance Ids', 'Root Cause Faulty Resource',
'Alarm Raised Time', 'Alarm Changed Time',
'Alarm Cleared Time', 'Alarm Acknowledged Time',
'Event Time', 'Fault Type', 'Is Root Cause',
'Correlated Alarm Ids', 'Fault Details', 'Links'
])
return columns
class TestListVnfFmAlarm(TestVnfFmAlarm):
def setUp(self):
super(TestListVnfFmAlarm, self).setUp()
self.list_vnf_fm_alarms = vnffm_alarm.ListVnfFmAlarm(
self.app, self.app_args, cmd_name='vnffm alarm list')
def test_take_action(self):
vnffm_alarms_obj = vnffm_alarm_fakes.create_vnf_fm_alarms(
count=3)
parsed_args = self.check_parser(self.list_vnf_fm_alarms, [], [])
self.requests_mock.register_uri(
'GET', os.path.join(self.url, 'vnffm/v1/alarms'),
json=vnffm_alarms_obj, headers=self.header)
actual_columns, data = self.list_vnf_fm_alarms.take_action(parsed_args)
_, columns = tacker_osc_utils.get_column_definitions(
vnffm_alarm._ATTR_MAP, long_listing=True)
expected_data = []
for vnffm_alarm_obj_idx in vnffm_alarms_obj:
expected_data.append(vnffm_alarm_fakes.get_vnffm_alarm_data(
vnffm_alarm_obj_idx, columns=columns))
self.assertCountEqual(_get_columns_vnffm_alarm(action='list'),
actual_columns)
self.assertCountEqual(expected_data, list(data))
def test_take_action_with_filter(self):
vnffm_alarms_obj = vnffm_alarm_fakes.create_vnf_fm_alarms(
count=3)
parsed_args = self.check_parser(
self.list_vnf_fm_alarms,
["--filter", '(eq,perceivedSeverity,WARNING)'],
[('filter', '(eq,perceivedSeverity,WARNING)')])
self.requests_mock.register_uri(
'GET', os.path.join(
self.url,
'vnffm/v1/alarms?filter=(eq,perceivedSeverity,WARNING)'),
json=vnffm_alarms_obj, headers=self.header)
actual_columns, data = self.list_vnf_fm_alarms.take_action(parsed_args)
_, columns = tacker_osc_utils.get_column_definitions(
vnffm_alarm._ATTR_MAP, long_listing=True)
expected_data = []
for vnffm_alarm_obj_idx in vnffm_alarms_obj:
expected_data.append(vnffm_alarm_fakes.get_vnffm_alarm_data(
vnffm_alarm_obj_idx, columns=columns))
self.assertCountEqual(_get_columns_vnffm_alarm(action='list'),
actual_columns)
self.assertListItemsEqual(expected_data, list(data))
def test_take_action_with_incorrect_filter(self):
parsed_args = self.check_parser(
self.list_vnf_fm_alarms,
["--filter", '(perceivedSeverity)'],
[('filter', '(perceivedSeverity)')])
url = os.path.join(
self.url, 'vnffm/v1/alarms?filter=(perceivedSeverity)')
self.requests_mock.register_uri(
'GET', url, headers=self.header, status_code=400, json={})
self.assertRaises(exceptions.TackerClientException,
self.list_vnf_fm_alarms.take_action,
parsed_args)
def test_take_action_internal_server_error(self):
parsed_args = self.check_parser(
self.list_vnf_fm_alarms,
["--filter", '(eq,perceivedSeverity,WARNING)'],
[('filter', '(eq,perceivedSeverity,WARNING)')])
url = os.path.join(
self.url, 'vnffm/v1/alarms?filter=(eq,perceivedSeverity,WARNING)')
self.requests_mock.register_uri(
'GET', url, headers=self.header, status_code=500, json={})
self.assertRaises(exceptions.TackerClientException,
self.list_vnf_fm_alarms.take_action,
parsed_args)
class TestShowVnfFmAlarm(TestVnfFmAlarm):
def setUp(self):
super(TestShowVnfFmAlarm, self).setUp()
self.show_vnf_fm_alarm = vnffm_alarm.ShowVnfFmAlarm(
self.app, self.app_args, cmd_name='vnffm alarm show')
def test_take_action(self):
"""Test of take_action()"""
vnffm_alarm_obj = vnffm_alarm_fakes.vnf_fm_alarm_response()
arglist = [vnffm_alarm_obj['id']]
verifylist = [('vnf_fm_alarm_id', vnffm_alarm_obj['id'])]
# command param
parsed_args = self.check_parser(
self.show_vnf_fm_alarm, arglist, verifylist)
url = os.path.join(
self.url, 'vnffm/v1/alarms', vnffm_alarm_obj['id'])
self.requests_mock.register_uri(
'GET', url, headers=self.header, json=vnffm_alarm_obj)
columns, _ = (self.show_vnf_fm_alarm.take_action(parsed_args))
self.assertCountEqual(_get_columns_vnffm_alarm(action='show'),
columns)
def test_take_action_vnf_lcm_op_occ_id_not_found(self):
"""Test if vnf-lcm-op-occ-id does not find."""
arglist = [uuidsentinel.vnf_fm_alarm_id]
verifylist = [('vnf_fm_alarm_id', uuidsentinel.vnf_fm_alarm_id)]
# command param
parsed_args = self.check_parser(
self.show_vnf_fm_alarm, arglist, verifylist)
url = os.path.join(
self.url, 'vnffm/v1/alarms', uuidsentinel.vnf_fm_alarm_id)
self.requests_mock.register_uri(
'GET', url, headers=self.header, status_code=404, json={})
self.assertRaises(exceptions.TackerClientException,
self.show_vnf_fm_alarm.take_action,
parsed_args)
def test_take_action_internal_server_error(self):
"""Test for internal server error."""
arglist = [uuidsentinel.vnf_fm_alarm_id]
verifylist = [('vnf_fm_alarm_id', uuidsentinel.vnf_fm_alarm_id)]
# command param
parsed_args = self.check_parser(
self.show_vnf_fm_alarm, arglist, verifylist)
url = os.path.join(
self.url, 'vnffm/v1/alarms', uuidsentinel.vnf_fm_alarm_id)
self.requests_mock.register_uri(
'GET', url, headers=self.header, status_code=500, json={})
self.assertRaises(exceptions.TackerClientException,
self.show_vnf_fm_alarm.take_action,
parsed_args)
@ddt.ddt
class TestUpdateVnfFmAlarm(TestVnfFmAlarm):
def setUp(self):
super(TestUpdateVnfFmAlarm, self).setUp()
self.update_vnf_fm_alarm = vnffm_alarm.UpdateVnfFmAlarm(
self.app, self.app_args, cmd_name='vnffm alarm update')
@ddt.data('ACKNOWLEDGED', 'UNACKNOWLEDGED')
def test_take_action(self, ack_state):
"""Test of take_action()"""
vnffm_alarm_obj = vnffm_alarm_fakes.vnf_fm_alarm_response(
None, 'update')
arg_list = ['--ack-state', ack_state, uuidsentinel.vnf_fm_alarm_id]
verify_list = [('ack_state', ack_state),
('vnf_fm_alarm_id', uuidsentinel.vnf_fm_alarm_id)]
# command param
parsed_args = self.check_parser(
self.update_vnf_fm_alarm, arg_list, verify_list)
url = os.path.join(
self.url, 'vnffm/v1/alarms', uuidsentinel.vnf_fm_alarm_id)
self.requests_mock.register_uri(
'PATCH', url, headers=self.header, json=vnffm_alarm_obj)
actual_columns, data = (
self.update_vnf_fm_alarm.take_action(parsed_args))
expected_columns = _get_columns_vnffm_alarm(action='update')
self.assertCountEqual(expected_columns, actual_columns)
_, columns = vnffm_alarm._get_columns(
vnffm_alarm_obj, action='update')
expected_data = vnffm_alarm_fakes.get_vnffm_alarm_data(
vnffm_alarm_obj, columns=columns)
self.assertEqual(expected_data, data)
@ddt.data('ACKNOWLEDGED')
def test_take_action_vnf_lcm_op_occ_id_not_found(self, ack_state):
"""Test if vnf-lcm-op-occ-id does not find"""
arg_list = ['--ack-state', ack_state, uuidsentinel.vnf_fm_alarm_id]
verify_list = [('ack_state', ack_state),
('vnf_fm_alarm_id', uuidsentinel.vnf_fm_alarm_id)]
# command param
parsed_args = self.check_parser(
self.update_vnf_fm_alarm, arg_list, verify_list)
url = os.path.join(
self.url, 'vnffm/v1/alarms', uuidsentinel.vnf_fm_alarm_id)
self.requests_mock.register_uri(
'PATCH', url, headers=self.header, status_code=404, json={})
self.assertRaises(exceptions.TackerClientException,
self.update_vnf_fm_alarm.take_action,
parsed_args)
@ddt.data('UNACKNOWLEDGED')
def test_take_action_vnf_lcm_op_occ_state_is_conflict(self, ack_state):
"""Test if vnf-lcm-op-occ state is conflict"""
arg_list = ['--ack-state', ack_state, uuidsentinel.vnf_fm_alarm_id]
verify_list = [('ack_state', ack_state),
('vnf_fm_alarm_id', uuidsentinel.vnf_fm_alarm_id)]
# command param
parsed_args = self.check_parser(
self.update_vnf_fm_alarm, arg_list, verify_list)
url = os.path.join(
self.url, 'vnffm/v1/alarms', uuidsentinel.vnf_fm_alarm_id)
self.requests_mock.register_uri(
'PATCH', url, headers=self.header, status_code=409, json={})
self.assertRaises(exceptions.TackerClientException,
self.update_vnf_fm_alarm.take_action,
parsed_args)

View File

@@ -0,0 +1,329 @@
# 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 ddt
import os
import sys
from io import StringIO
from oslo_utils.fixture import uuidsentinel
from unittest import mock
from tackerclient.common import exceptions
from tackerclient.osc import utils as tacker_osc_utils
from tackerclient.osc.v2.vnffm import vnffm_sub
from tackerclient.tests.unit.osc import base
from tackerclient.tests.unit.osc.v1.fixture_data import client
from tackerclient.tests.unit.osc.v2 import vnffm_sub_fakes
class TestVnfFmSub(base.FixturedTestCase):
client_fixture_class = client.ClientFixture
def setUp(self):
super(TestVnfFmSub, 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
def _get_columns_vnffm_sub(action=None):
columns = ['ID', 'Callback Uri']
if action == 'show' or action == 'create':
columns.extend(['Filter', 'Links'])
return columns
@ddt.ddt
class TestCreateVnfFmSub(TestVnfFmSub):
def setUp(self):
super(TestCreateVnfFmSub, self).setUp()
self.create_vnf_fm_sub = vnffm_sub.CreateVnfFmSub(
self.app, self.app_args, cmd_name='vnffm sub create')
def test_create_no_args(self):
self.assertRaises(base.ParserException, self.check_parser,
self.create_vnf_fm_sub, [], [])
@ddt.unpack
def test_take_action(self):
param_file = ("./tackerclient/osc/v2/vnffm/samples/"
"create_vnf_fm_subscription_param_sample.json")
arg_list = [param_file]
verify_list = [('request_file', param_file)]
parsed_args = self.check_parser(self.create_vnf_fm_sub, arg_list,
verify_list)
json = vnffm_sub_fakes.vnf_fm_sub_response()
self.requests_mock.register_uri(
'POST', os.path.join(self.url, 'vnffm/v1/subscriptions'),
json=json, headers=self.header)
actual_columns, data = (
self.create_vnf_fm_sub.take_action(parsed_args))
_, attributes = vnffm_sub._get_columns(json)
self.assertCountEqual(_get_columns_vnffm_sub("create"),
actual_columns)
self.assertListItemsEqual(vnffm_sub_fakes.get_vnffm_sub_data(
json, columns=attributes), data)
class TestListVnfFmSub(TestVnfFmSub):
def setUp(self):
super(TestListVnfFmSub, self).setUp()
self.list_vnffm_sub = vnffm_sub.ListVnfFmSub(
self.app, self.app_args, cmd_name='vnffm sub list')
def test_take_action(self):
vnffm_subs_obj = vnffm_sub_fakes.create_vnf_fm_subs(
count=3)
parsed_args = self.check_parser(self.list_vnffm_sub, [], [])
self.requests_mock.register_uri(
'GET', os.path.join(self.url, 'vnffm/v1/subscriptions'),
json=vnffm_subs_obj, headers=self.header)
actual_columns, data = self.list_vnffm_sub.take_action(parsed_args)
_, columns = tacker_osc_utils.get_column_definitions(
vnffm_sub._ATTR_MAP, long_listing=True)
expected_data = []
for vnffm_sub_obj_idx in vnffm_subs_obj:
expected_data.append(vnffm_sub_fakes.get_vnffm_sub_data(
vnffm_sub_obj_idx, columns=columns))
self.assertCountEqual(_get_columns_vnffm_sub(action='list'),
actual_columns)
self.assertCountEqual(expected_data, list(data))
def test_take_action_with_filter(self):
vnffm_subs_obj = vnffm_sub_fakes.create_vnf_fm_subs(
count=3)
parsed_args = self.check_parser(
self.list_vnffm_sub,
["--filter", '(eq,callbackUri,/nfvo/notify/alarm)'],
[('filter', '(eq,callbackUri,/nfvo/notify/alarm)')])
self.requests_mock.register_uri(
'GET', os.path.join(
self.url,
'vnffm/v1/subscriptions?'
'filter=(eq,callbackUri,/nfvo/notify/alarm)'),
json=vnffm_subs_obj, headers=self.header)
actual_columns, data = self.list_vnffm_sub.take_action(parsed_args)
_, columns = tacker_osc_utils.get_column_definitions(
vnffm_sub._ATTR_MAP, long_listing=True)
expected_data = []
for vnffm_sub_obj_idx in vnffm_subs_obj:
expected_data.append(vnffm_sub_fakes.get_vnffm_sub_data(
vnffm_sub_obj_idx, columns=columns))
self.assertCountEqual(_get_columns_vnffm_sub(action='list'),
actual_columns)
self.assertListItemsEqual(expected_data, list(data))
def test_take_action_with_incorrect_filter(self):
parsed_args = self.check_parser(
self.list_vnffm_sub,
["--filter", '(callbackUri)'],
[('filter', '(callbackUri)')])
url = os.path.join(
self.url,
'vnffm/v1/subscriptions?filter=(callbackUri)')
self.requests_mock.register_uri(
'POST', url, headers=self.header, status_code=400, json={})
self.assertRaises(exceptions.TackerClientException,
self.list_vnffm_sub.take_action,
parsed_args)
def test_take_action_internal_server_error(self):
parsed_args = self.check_parser(
self.list_vnffm_sub,
["--filter", '(eq,callbackUri,/nfvo/notify/alarm)'],
[('filter', '(eq,callbackUri,/nfvo/notify/alarm)')])
url = os.path.join(
self.url,
'vnffm/v1/subscriptions?'
'filter=(eq,callbackUri,/nfvo/notify/alarm)')
self.requests_mock.register_uri(
'POST', url, headers=self.header, status_code=500, json={})
self.assertRaises(exceptions.TackerClientException,
self.list_vnffm_sub.take_action,
parsed_args)
class TestShowVnfFmSub(TestVnfFmSub):
def setUp(self):
super(TestShowVnfFmSub, self).setUp()
self.show_vnf_fm_subs = vnffm_sub.ShowVnfFmSub(
self.app, self.app_args, cmd_name='vnffm sub show')
def test_take_action(self):
"""Test of take_action()"""
vnffm_sub_obj = vnffm_sub_fakes.vnf_fm_sub_response()
arg_list = [vnffm_sub_obj['id']]
verify_list = [('vnf_fm_sub_id', vnffm_sub_obj['id'])]
# command param
parsed_args = self.check_parser(
self.show_vnf_fm_subs, arg_list, verify_list)
url = os.path.join(
self.url,
'vnffm/v1/subscriptions',
vnffm_sub_obj['id'])
self.requests_mock.register_uri(
'GET', url, headers=self.header, json=vnffm_sub_obj)
columns, _ = (self.show_vnf_fm_subs.take_action(parsed_args))
self.assertCountEqual(_get_columns_vnffm_sub('show'),
columns)
def test_take_action_vnf_fm_sub_id_not_found(self):
"""Test if vnf-lcm-op-occ-id does not find."""
arg_list = [uuidsentinel.vnf_fm_sub_id]
verify_list = [('vnf_fm_sub_id', uuidsentinel.vnf_fm_sub_id)]
# command param
parsed_args = self.check_parser(
self.show_vnf_fm_subs, arg_list, verify_list)
url = os.path.join(
self.url,
'vnffm/v1/subscriptions',
uuidsentinel.vnf_fm_sub_id)
self.requests_mock.register_uri(
'GET', url, headers=self.header, status_code=404, json={})
self.assertRaises(exceptions.TackerClientException,
self.show_vnf_fm_subs.take_action,
parsed_args)
def test_take_action_internal_server_error(self):
"""Test for internal server error."""
arg_list = [uuidsentinel.vnf_fm_sub_id]
verify_list = [('vnf_fm_sub_id', uuidsentinel.vnf_fm_sub_id)]
# command param
parsed_args = self.check_parser(
self.show_vnf_fm_subs, arg_list, verify_list)
url = os.path.join(
self.url,
'vnffm/v1/subscriptions',
uuidsentinel.vnf_fm_sub_id)
self.requests_mock.register_uri(
'GET', url, headers=self.header, status_code=500, json={})
self.assertRaises(exceptions.TackerClientException,
self.show_vnf_fm_subs.take_action,
parsed_args)
class TestDeleteVnfFmSub(TestVnfFmSub):
def setUp(self):
super(TestDeleteVnfFmSub, self).setUp()
self.delete_vnf_fm_sub = vnffm_sub.DeleteVnfFmSub(
self.app, self.app_args, cmd_name='vnffm sub delete')
# Vnf Fm subscription to delete
self.vnf_fm_subs = vnffm_sub_fakes.create_vnf_fm_subs(count=3)
def _mock_request_url_for_delete(self, index):
url = os.path.join(self.url, 'vnffm/v1/subscriptions',
self.vnf_fm_subs[index]['id'])
self.requests_mock.register_uri('DELETE', url,
headers=self.header, json={})
def test_delete_one_vnf_fm_sub(self):
arg_list = [self.vnf_fm_subs[0]['id']]
verify_list = [('vnf_fm_sub_id',
[self.vnf_fm_subs[0]['id']])]
parsed_args = self.check_parser(self.delete_vnf_fm_sub, arg_list,
verify_list)
self._mock_request_url_for_delete(0)
sys.stdout = buffer = StringIO()
result = self.delete_vnf_fm_sub.take_action(parsed_args)
self.assertIsNone(result)
self.assertEqual(
(f"VNF FM subscription '{self.vnf_fm_subs[0]['id']}' "
f"deleted successfully"), buffer.getvalue().strip())
def test_delete_multiple_vnf_fm_sub(self):
arg_list = []
for obj in self.vnf_fm_subs:
arg_list.append(obj['id'])
verify_list = [('vnf_fm_sub_id', arg_list)]
parsed_args = self.check_parser(self.delete_vnf_fm_sub, arg_list,
verify_list)
for i in range(0, 3):
self._mock_request_url_for_delete(i)
sys.stdout = buffer = StringIO()
result = self.delete_vnf_fm_sub.take_action(parsed_args)
self.assertIsNone(result)
self.assertEqual('All specified VNF FM subscriptions are deleted '
'successfully', buffer.getvalue().strip())
def test_delete_multiple_vnf_fm_sub_exception(self):
arg_list = [
self.vnf_fm_subs[0]['id'],
'xxxx-yyyy-zzzz',
self.vnf_fm_subs[1]['id'],
]
verify_list = [('vnf_fm_sub_id', arg_list)]
parsed_args = self.check_parser(self.delete_vnf_fm_sub,
arg_list, verify_list)
self._mock_request_url_for_delete(0)
url = os.path.join(self.url, 'vnffm/v1/subscriptions',
'xxxx-yyyy-zzzz')
self.requests_mock.register_uri(
'GET', url, exc=exceptions.ConnectionFailed)
self._mock_request_url_for_delete(1)
exception = self.assertRaises(exceptions.CommandError,
self.delete_vnf_fm_sub.take_action,
parsed_args)
self.assertEqual('Failed to delete 1 of 3 VNF FM subscriptions.',
exception.message)

View File

@@ -13,8 +13,18 @@
# License for the specific language governing permissions and limitations
# under the License.
from io import StringIO
import os
import sys
from unittest import mock
from tackerclient.common import exceptions
from tackerclient.osc.v1.vnflcm import vnflcm
from tackerclient.tests.unit.osc import base
from tackerclient.tests.unit.osc.v1.fixture_data import client
from tackerclient.tests.unit.osc.v1 import test_vnflcm
from tackerclient.tests.unit.osc.v1 import vnflcm_fakes
from tackerclient.v1_0 import client as proxy_client
class TestVnfLcmV2(base.FixturedTestCase):
@@ -30,3 +40,112 @@ class TestVnfLcmV2(base.FixturedTestCase):
self.assertEqual(self.cs.vnf_lcm_client.vnf_instances_path,
'/vnflcm/v2/vnf_instances')
# check of other paths is omitted.
class TestChangeVnfPkgVnfLcm(test_vnflcm.TestVnfLcm):
api_version = '2'
def setUp(self):
super(TestChangeVnfPkgVnfLcm, self).setUp()
self.change_vnfpkg_vnf_lcm = vnflcm.ChangeVnfPkgVnfLcm(
self.app, self.app_args,
cmd_name='vnflcm change-vnfpkg')
def test_take_action(self):
vnf_instance = vnflcm_fakes.vnf_instance_response()
sample_param_file = ("./tackerclient/osc/v2/vnflcm/samples/"
"change_vnfpkg_vnf_instance_param_sample.json")
arglist = [vnf_instance['id'], sample_param_file]
verifylist = [('vnf_instance', vnf_instance['id']),
('request_file', sample_param_file)]
# command param
parsed_args = self.check_parser(self.change_vnfpkg_vnf_lcm,
arglist,
verifylist)
url = os.path.join(self.url, 'vnflcm/v2/vnf_instances',
vnf_instance['id'], 'change_vnfpkg')
self.requests_mock.register_uri(
'POST', url, headers=self.header, json={})
sys.stdout = buffer = StringIO()
with mock.patch.object(proxy_client.ClientBase,
'_handle_fault_response') as m:
self.change_vnfpkg_vnf_lcm.take_action(parsed_args)
# check no fault response is received
self.assertNotCalled(m)
self.assertEqual(
('Change Current VNF Package for VNF Instance {0} '
'has been accepted.'.format(vnf_instance['id'])),
buffer.getvalue().strip())
def test_take_action_vnf_instance_not_found(self):
vnf_instance = vnflcm_fakes.vnf_instance_response()
sample_param_file = ("./tackerclient/osc/v1/vnflcm/samples/"
"change_vnfpkg_vnf_instance_param_sample.json")
arglist = [vnf_instance['id'], sample_param_file]
verifylist = [('vnf_instance', vnf_instance['id']),
('request_file', sample_param_file)]
# command param
parsed_args = self.check_parser(self.change_vnfpkg_vnf_lcm,
arglist,
verifylist)
url = os.path.join(self.url, 'vnflcm/v2/vnf_instances',
vnf_instance['id'], 'change_vnfpkg')
self.requests_mock.register_uri(
'POST', url, headers=self.header, status_code=404, json={})
self.assertRaises(exceptions.TackerClientException,
self.change_vnfpkg_vnf_lcm.take_action,
parsed_args)
def test_take_action_param_file_not_exists(self):
vnf_instance = vnflcm_fakes.vnf_instance_response()
sample_param_file = "./not_exists.json"
arglist = [vnf_instance['id'], sample_param_file]
verifylist = [('vnf_instance', vnf_instance['id']),
('request_file', sample_param_file)]
# command param
parsed_args = self.check_parser(
self.change_vnfpkg_vnf_lcm,
arglist,
verifylist)
ex = self.assertRaises(
exceptions.InvalidInput,
self.change_vnfpkg_vnf_lcm.take_action,
parsed_args)
expected_msg = ("Invalid input: File %s does not exist "
"or user does not have read privileges to it")
self.assertEqual(expected_msg % sample_param_file, str(ex))
@mock.patch("os.open")
@mock.patch("os.access")
def test_take_action_invalid_format_param_file(self, mock_access,
mock_open):
vnf_instance = vnflcm_fakes.vnf_instance_response()
sample_param_file = "./invalid_param_file.json"
arglist = [vnf_instance['id'], sample_param_file]
verifylist = [('vnf_instance', vnf_instance['id']),
('request_file', sample_param_file)]
mock_open.return_value = "invalid_json_data"
mock_access.return_value = True
# command param
parsed_args = self.check_parser(self.change_vnfpkg_vnf_lcm,
arglist,
verifylist)
ex = self.assertRaises(
exceptions.InvalidInput,
self.change_vnfpkg_vnf_lcm.take_action,
parsed_args)
expected_msg = "Failed to load parameter file."
self.assertIn(expected_msg, str(ex))

View File

@@ -0,0 +1,476 @@
# 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 ddt
import os
import sys
from io import StringIO
from oslo_utils.fixture import uuidsentinel
from unittest import mock
from tackerclient.common import exceptions
from tackerclient.osc import utils as tacker_osc_utils
from tackerclient.osc.v2.vnfpm import vnfpm_job
from tackerclient.tests.unit.osc import base
from tackerclient.tests.unit.osc.v1.fixture_data import client
from tackerclient.tests.unit.osc.v2 import vnfpm_job_fakes
class TestVnfPmJob(base.FixturedTestCase):
client_fixture_class = client.ClientFixture
def setUp(self):
super(TestVnfPmJob, 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
def _get_columns_vnfpm_job(action=None):
if action == 'update':
columns = ['Callback Uri', 'Authentication']
else:
columns = ['ID', 'Object Type', 'Object Instance Ids',
'Sub Object Instance Ids', 'Criteria', 'Callback Uri',
'Reports', 'Links']
if action == 'list':
columns = [
ele for ele in columns if ele not in [
'Criteria', 'Sub Object Instance Ids', 'Reports', 'Links'
]
]
return columns
@ddt.ddt
class TestCreateVnfPmJob(TestVnfPmJob):
def setUp(self):
super(TestCreateVnfPmJob, self).setUp()
self.create_vnf_pm_job = vnfpm_job.CreateVnfPmJob(
self.app, self.app_args, cmd_name='vnfpm job create')
def test_create_no_args(self):
self.assertRaises(base.ParserException, self.check_parser,
self.create_vnf_pm_job, [], [])
@ddt.unpack
def test_take_action(self):
param_file = ("./tackerclient/osc/v2/vnfpm/samples/"
"create_vnf_pm_job_param_sample.json")
arg_list = [param_file]
verify_list = [('request_file', param_file)]
parsed_args = self.check_parser(self.create_vnf_pm_job, arg_list,
verify_list)
json = vnfpm_job_fakes.vnf_pm_job_response()
self.requests_mock.register_uri(
'POST', os.path.join(self.url, 'vnfpm/v2/pm_jobs'),
json=json, headers=self.header)
actual_columns, data = (
self.create_vnf_pm_job.take_action(parsed_args))
self.assertCountEqual(_get_columns_vnfpm_job(),
actual_columns)
_, attributes = vnfpm_job._get_columns(json)
expected_data = vnfpm_job_fakes.get_vnfpm_job_data(
json, columns=attributes)
self.assertListItemsEqual(expected_data, data)
@ddt.ddt
class TestListVnfPmJob(TestVnfPmJob):
def setUp(self):
super(TestListVnfPmJob, self).setUp()
self.list_vnf_pm_jobs = vnfpm_job.ListVnfPmJob(
self.app, self.app_args, cmd_name='vnfpm job list')
self._vnf_pm_jobs = self._get_vnf_pm_jobs()
def _get_vnf_pm_jobs(self):
return vnfpm_job_fakes.create_vnf_pm_jobs(count=3)
def get_list_columns(self, all_fields=False, exclude_fields=None,
extra_fields=None, exclude_default=False):
columns = ['Id', 'Object Type', 'Links']
complex_columns = [
'Object Instance Ids',
'Sub Object Instance Ids',
'Criteria',
'Reports'
]
simple_columns = ['Callback Uri']
if extra_fields:
columns.extend(extra_fields)
if exclude_fields:
columns.extend([field for field in complex_columns
if field not in exclude_fields])
if all_fields:
columns.extend(complex_columns)
columns.extend(simple_columns)
if exclude_default:
columns.extend(simple_columns)
return columns
def _get_mock_response_for_list_vnf_pm_jobs(
self, filter_attribute, json=None):
self.requests_mock.register_uri(
'GET', self.url + '/vnfpm/v2/pm_jobs?' + filter_attribute,
json=json if json else self._get_vnf_pm_jobs(),
headers=self.header)
def test_take_action_default_fields(self):
parsed_args = self.check_parser(self.list_vnf_pm_jobs, [], [])
self.requests_mock.register_uri(
'GET', self.url + '/vnfpm/v2/pm_jobs',
json=self._vnf_pm_jobs, headers=self.header)
actual_columns, data = self.list_vnf_pm_jobs.take_action(parsed_args)
expected_data = []
_, columns = tacker_osc_utils.get_column_definitions(
self.list_vnf_pm_jobs.get_attributes(), long_listing=True)
for vnf_pm_job_obj in self._vnf_pm_jobs['vnf_pm_jobs']:
expected_data.append(vnfpm_job_fakes.get_vnfpm_job_data(
vnf_pm_job_obj, columns=columns))
self.assertCountEqual(self.get_list_columns(), actual_columns)
self.assertListItemsEqual(expected_data, list(data))
@ddt.data('all_fields', 'exclude_default')
def test_take_action(self, arg):
parsed_args = self.check_parser(
self.list_vnf_pm_jobs,
["--" + arg, "--filter", '(eq,objectType,VNFC)'],
[(arg, True), ('filter', '(eq,objectType,VNFC)')])
vnf_pm_jobs = self._get_vnf_pm_jobs()
self._get_mock_response_for_list_vnf_pm_jobs(
'filter=(eq,objectType,VNFC)&' + arg, json=vnf_pm_jobs)
actual_columns, data = self.list_vnf_pm_jobs.take_action(parsed_args)
expected_data = []
kwargs = {arg: True}
_, columns = tacker_osc_utils.get_column_definitions(
self.list_vnf_pm_jobs.get_attributes(**kwargs), long_listing=True)
for vnf_pm_job_obj in vnf_pm_jobs['vnf_pm_jobs']:
expected_data.append(vnfpm_job_fakes.get_vnfpm_job_data(
vnf_pm_job_obj, columns=columns))
self.assertCountEqual(self.get_list_columns(**kwargs), actual_columns)
self.assertListItemsEqual(expected_data, list(data))
def test_take_action_with_exclude_fields(self):
parsed_args = self.check_parser(
self.list_vnf_pm_jobs,
["--exclude_fields", 'objectInstanceIds,criteria',
"--filter", '(eq,objectType,VNFC)'],
[('exclude_fields', 'objectInstanceIds,criteria'),
('filter', '(eq,objectType,VNFC)')])
vnf_pm_jobs = self._get_vnf_pm_jobs()
updated_vnf_pm_jobs = {'vnf_pm_jobs': []}
for vnf_pm_job_obj in vnf_pm_jobs['vnf_pm_jobs']:
vnf_pm_job_obj.pop('objectInstanceIds')
vnf_pm_job_obj.pop('criteria')
updated_vnf_pm_jobs['vnf_pm_jobs'].append(vnf_pm_job_obj)
self._get_mock_response_for_list_vnf_pm_jobs(
'filter=(eq,objectType,VNFC)&'
'exclude_fields=objectInstanceIds,criteria',
json=updated_vnf_pm_jobs)
actual_columns, data = self.list_vnf_pm_jobs.take_action(parsed_args)
expected_data = []
_, columns = tacker_osc_utils.get_column_definitions(
self.list_vnf_pm_jobs.get_attributes(
exclude_fields=['objectInstanceIds', 'criteria']),
long_listing=True)
for updated_vnf_pm_obj in updated_vnf_pm_jobs['vnf_pm_jobs']:
expected_data.append(vnfpm_job_fakes.get_vnfpm_job_data(
updated_vnf_pm_obj, columns=columns))
expected_columns = self.get_list_columns(
exclude_fields=['Object Instance Ids', 'Criteria'])
self.assertCountEqual(expected_columns, actual_columns)
self.assertListItemsEqual(expected_data, list(data))
@ddt.data((['--all_fields', '--fields', 'objectInstanceIds'],
[('all_fields', True), ('fields', 'objectInstanceIds')]),
(['--all_fields', '--exclude_fields', 'criteria'],
[('all_fields', True), ('exclude_fields', 'criteria')]),
(['--fields', 'objectInstanceIds',
'--exclude_fields', 'criteria'],
[('fields', 'objectInstanceIds'),
('exclude_fields', 'criteria')]))
@ddt.unpack
def test_take_action_with_invalid_combination(self, arglist, verifylist):
self.assertRaises(base.ParserException, self.check_parser,
self.list_vnf_pm_jobs, arglist, verifylist)
def test_take_action_with_valid_combination(self):
parsed_args = self.check_parser(
self.list_vnf_pm_jobs,
[
"--fields", 'subObjectInstanceIds,criteria',
"--exclude_default"
],
[
('fields', 'subObjectInstanceIds,criteria'),
('exclude_default', True)
])
vnf_pm_jobs = self._get_vnf_pm_jobs()
updated_vnf_pm_jobs = {'vnf_pm_jobs': []}
for vnf_pm_job_obj in vnf_pm_jobs['vnf_pm_jobs']:
# vnf_pm_job_obj.pop('userDefinedData')
updated_vnf_pm_jobs['vnf_pm_jobs'].append(vnf_pm_job_obj)
self._get_mock_response_for_list_vnf_pm_jobs(
'exclude_default&fields=subObjectInstanceIds,criteria',
json=updated_vnf_pm_jobs)
actual_columns, data = self.list_vnf_pm_jobs.take_action(parsed_args)
expected_data = []
_, columns = tacker_osc_utils.get_column_definitions(
self.list_vnf_pm_jobs.get_attributes(
extra_fields=['subObjectInstanceIds', 'criteria'],
exclude_default=True),
long_listing=True)
for updated_vnf_pm_job_obj in updated_vnf_pm_jobs['vnf_pm_jobs']:
expected_data.append(vnfpm_job_fakes.get_vnfpm_job_data(
updated_vnf_pm_job_obj, columns=columns))
expected_columns = self.get_list_columns(
extra_fields=['Sub Object Instance Ids', 'Criteria'],
exclude_default=True)
self.assertCountEqual(expected_columns, actual_columns)
self.assertListItemsEqual(expected_data, list(data))
class TestShowVnfPmJob(TestVnfPmJob):
def setUp(self):
super(TestShowVnfPmJob, self).setUp()
self.show_vnf_pm_jobs = vnfpm_job.ShowVnfPmJob(
self.app, self.app_args, cmd_name='vnfpm job show')
def test_take_action(self):
"""Test of take_action()"""
vnfpm_job_obj = vnfpm_job_fakes.vnf_pm_job_response()
arg_list = [vnfpm_job_obj['id']]
verify_list = [('vnf_pm_job_id', vnfpm_job_obj['id'])]
# command param
parsed_args = self.check_parser(
self.show_vnf_pm_jobs, arg_list, verify_list)
url = os.path.join(
self.url, 'vnfpm/v2/pm_jobs', vnfpm_job_obj['id'])
self.requests_mock.register_uri(
'GET', url, headers=self.header, json=vnfpm_job_obj)
columns, data = (self.show_vnf_pm_jobs.take_action(parsed_args))
self.assertCountEqual(_get_columns_vnfpm_job('show'), columns)
_, attributes = vnfpm_job._get_columns(vnfpm_job_obj)
self.assertListItemsEqual(
vnfpm_job_fakes.get_vnfpm_job_data(
vnfpm_job_obj, columns=attributes), data)
def test_take_action_vnf_pm_job_id_not_found(self):
"""Test if vnf-pm-job-id does not find."""
arg_list = [uuidsentinel.vnf_pm_job_id]
verify_list = [('vnf_pm_job_id', uuidsentinel.vnf_pm_job_id)]
# command param
parsed_args = self.check_parser(
self.show_vnf_pm_jobs, arg_list, verify_list)
url = os.path.join(
self.url, 'vnfpm/v2/pm_jobs', uuidsentinel.vnf_pm_job_id)
self.requests_mock.register_uri(
'GET', url, headers=self.header, status_code=404, json={})
self.assertRaises(exceptions.TackerClientException,
self.show_vnf_pm_jobs.take_action,
parsed_args)
def test_take_action_internal_server_error(self):
"""Test for internal server error."""
arg_list = [uuidsentinel.vnf_pm_job_id]
verify_list = [('vnf_pm_job_id', uuidsentinel.vnf_pm_job_id)]
# command param
parsed_args = self.check_parser(
self.show_vnf_pm_jobs, arg_list, verify_list)
url = os.path.join(
self.url, 'vnfpm/v2/pm_jobs', uuidsentinel.vnf_pm_job_id)
self.requests_mock.register_uri(
'GET', url, headers=self.header, status_code=500, json={})
self.assertRaises(exceptions.TackerClientException,
self.show_vnf_pm_jobs.take_action,
parsed_args)
@ddt.ddt
class TestUpdateVnfPmJob(TestVnfPmJob):
def setUp(self):
super(TestUpdateVnfPmJob, self).setUp()
self.update_vnf_pm_job = vnfpm_job.UpdateVnfPmJob(
self.app, self.app_args, cmd_name='vnfpm job update')
def test_take_action(self):
"""Test of take_action()"""
param_file = ("./tackerclient/osc/v2/vnfpm/samples/"
"update_vnf_pm_job_param_sample.json")
arg_list = [uuidsentinel.vnf_pm_job_id, param_file]
verify_list = [
('vnf_pm_job_id', uuidsentinel.vnf_pm_job_id),
('request_file', param_file)
]
vnfpm_job_obj = vnfpm_job_fakes.vnf_pm_job_response(
None, 'update')
# command param
parsed_args = self.check_parser(
self.update_vnf_pm_job, arg_list, verify_list)
url = os.path.join(
self.url, 'vnfpm/v2/pm_jobs', uuidsentinel.vnf_pm_job_id)
self.requests_mock.register_uri(
'PATCH', url, headers=self.header, json=vnfpm_job_obj)
actual_columns, data = (
self.update_vnf_pm_job.take_action(parsed_args))
expected_columns = _get_columns_vnfpm_job(action='update')
self.assertCountEqual(expected_columns, actual_columns)
_, columns = vnfpm_job._get_columns(
vnfpm_job_obj, action='update')
expected_data = vnfpm_job_fakes.get_vnfpm_job_data(
vnfpm_job_obj, columns=columns)
self.assertEqual(expected_data, data)
def test_take_action_vnf_pm_job_id_not_found(self):
"""Test if vnf-pm-job-id does not find"""
param_file = ("./tackerclient/osc/v2/vnfpm/samples/"
"update_vnf_pm_job_param_sample.json")
arg_list = [uuidsentinel.vnf_pm_job_id, param_file]
verify_list = [
('vnf_pm_job_id', uuidsentinel.vnf_pm_job_id),
('request_file', param_file)
]
# command param
parsed_args = self.check_parser(
self.update_vnf_pm_job, arg_list, verify_list)
url = os.path.join(
self.url, 'vnfpm/v2/pm_jobs', uuidsentinel.vnf_pm_job_id)
self.requests_mock.register_uri(
'PATCH', url, headers=self.header, status_code=404, json={})
self.assertRaises(exceptions.TackerClientException,
self.update_vnf_pm_job.take_action,
parsed_args)
class TestDeleteVnfPmJob(TestVnfPmJob):
def setUp(self):
super(TestDeleteVnfPmJob, self).setUp()
self.delete_vnf_pm_job = vnfpm_job.DeleteVnfPmJob(
self.app, self.app_args, cmd_name='vnfpm job delete')
# Vnf Fm job to delete
self.vnf_pm_jobs = vnfpm_job_fakes.create_vnf_pm_jobs(
count=3)['vnf_pm_jobs']
def _mock_request_url_for_delete(self, index):
url = os.path.join(self.url, 'vnfpm/v2/pm_jobs',
self.vnf_pm_jobs[index]['id'])
self.requests_mock.register_uri('DELETE', url,
headers=self.header, json={})
def test_delete_one_vnf_pm_job(self):
arg_list = [self.vnf_pm_jobs[0]['id']]
verify_list = [('vnf_pm_job_id',
[self.vnf_pm_jobs[0]['id']])]
parsed_args = self.check_parser(self.delete_vnf_pm_job, arg_list,
verify_list)
self._mock_request_url_for_delete(0)
sys.stdout = buffer = StringIO()
result = self.delete_vnf_pm_job.take_action(parsed_args)
self.assertIsNone(result)
self.assertEqual(
(f"VNF PM job '{self.vnf_pm_jobs[0]['id']}' "
f"deleted successfully"), buffer.getvalue().strip())
def test_delete_multiple_vnf_pm_job(self):
arg_list = []
for obj in self.vnf_pm_jobs:
arg_list.append(obj['id'])
verify_list = [('vnf_pm_job_id', arg_list)]
parsed_args = self.check_parser(self.delete_vnf_pm_job, arg_list,
verify_list)
for i in range(0, 3):
self._mock_request_url_for_delete(i)
sys.stdout = buffer = StringIO()
result = self.delete_vnf_pm_job.take_action(parsed_args)
self.assertIsNone(result)
self.assertEqual('All specified VNF PM jobs are deleted '
'successfully', buffer.getvalue().strip())
def test_delete_multiple_vnf_pm_job_exception(self):
arg_list = [
self.vnf_pm_jobs[0]['id'],
'xxxx-yyyy-zzzz',
self.vnf_pm_jobs[1]['id'],
]
verify_list = [('vnf_pm_job_id', arg_list)]
parsed_args = self.check_parser(self.delete_vnf_pm_job,
arg_list, verify_list)
self._mock_request_url_for_delete(0)
url = os.path.join(self.url, 'vnfpm/v2/jobs',
'xxxx-yyyy-zzzz')
self.requests_mock.register_uri(
'GET', url, exc=exceptions.ConnectionFailed)
self._mock_request_url_for_delete(1)
exception = self.assertRaises(exceptions.CommandError,
self.delete_vnf_pm_job.take_action,
parsed_args)
self.assertEqual('Failed to delete 1 of 3 VNF PM jobs.',
exception.message)

View File

@@ -0,0 +1,131 @@
# 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 os
from oslo_utils.fixture import uuidsentinel
from unittest import mock
from tackerclient.common import exceptions
from tackerclient.osc.v2.vnfpm import vnfpm_report
from tackerclient.tests.unit.osc import base
from tackerclient.tests.unit.osc.v1.fixture_data import client
from tackerclient.tests.unit.osc.v2 import vnfpm_report_fakes
class TestVnfPmReport(base.FixturedTestCase):
client_fixture_class = client.ClientFixture
def setUp(self):
super(TestVnfPmReport, 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
def _get_columns_vnfpm_report():
columns = ['Entries']
return columns
class TestShowVnfPmReport(TestVnfPmReport):
def setUp(self):
super(TestShowVnfPmReport, self).setUp()
self.show_vnf_pm_reports = vnfpm_report.ShowVnfPmReport(
self.app, self.app_args, cmd_name='vnfpm report show')
def test_take_action(self):
"""Test of take_action()"""
vnfpm_report_obj = vnfpm_report_fakes.vnf_pm_report_response()
vnf_pm_job_id = uuidsentinel.vnf_pm_job_id
vnf_pm_report_id = uuidsentinel.vnfpm_report_obj
arg_list = [vnf_pm_job_id, vnf_pm_report_id]
verify_list = [
('vnf_pm_job_id', vnf_pm_job_id),
('vnf_pm_report_id', vnf_pm_report_id)
]
# command param
parsed_args = self.check_parser(
self.show_vnf_pm_reports, arg_list, verify_list)
url = os.path.join(
self.url, 'vnfpm/v2/pm_jobs', vnf_pm_job_id,
'reports', vnf_pm_report_id)
self.requests_mock.register_uri(
'GET', url, headers=self.header, json=vnfpm_report_obj)
columns, data = (self.show_vnf_pm_reports.take_action(parsed_args))
self.assertCountEqual(_get_columns_vnfpm_report(), columns)
_, attributes = vnfpm_report._get_columns(vnfpm_report_obj)
expected_data = vnfpm_report_fakes.get_vnfpm_report_data(
vnfpm_report_obj, columns=attributes)
print(f'123, {expected_data}')
print(f'456, {data}')
self.assertListItemsEqual(expected_data, data)
def test_take_action_vnf_pm_report_id_not_found(self):
"""Test if vnf-pm-report-id does not find."""
vnf_pm_job_id = uuidsentinel.vnf_pm_job_id
vnf_pm_report_id = uuidsentinel.vnf_pm_report_id
arg_list = [vnf_pm_job_id, vnf_pm_report_id]
verify_list = [
('vnf_pm_job_id', vnf_pm_job_id),
('vnf_pm_report_id', vnf_pm_report_id)
]
# command param
parsed_args = self.check_parser(
self.show_vnf_pm_reports, arg_list, verify_list)
url = os.path.join(
self.url, 'vnfpm/v2/pm_jobs', vnf_pm_job_id,
'reports', vnf_pm_report_id)
self.requests_mock.register_uri(
'GET', url, headers=self.header, status_code=404, json={})
self.assertRaises(exceptions.TackerClientException,
self.show_vnf_pm_reports.take_action,
parsed_args)
def test_take_action_internal_server_error(self):
"""Test for internal server error."""
vnf_pm_job_id = uuidsentinel.vnf_pm_job_id
vnf_pm_report_id = uuidsentinel.vnf_pm_report_id
arg_list = [vnf_pm_job_id, vnf_pm_report_id]
verify_list = [
('vnf_pm_job_id', vnf_pm_job_id),
('vnf_pm_report_id', vnf_pm_report_id)
]
# command param
parsed_args = self.check_parser(
self.show_vnf_pm_reports, arg_list, verify_list)
url = os.path.join(
self.url, 'vnfpm/v2/pm_jobs', vnf_pm_job_id,
'reports', vnf_pm_report_id)
self.requests_mock.register_uri(
'GET', url, headers=self.header, status_code=500, json={})
self.assertRaises(exceptions.TackerClientException,
self.show_vnf_pm_reports.take_action,
parsed_args)

View File

@@ -0,0 +1,127 @@
# 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.
from oslo_utils import uuidutils
from tackerclient.osc import utils as tacker_osc_utils
def create_vnf_fm_alarms(count=2):
"""Create multiple fake vnf packages.
:param int count:
The number of vnf_fm_alarms to fake
:return:
A list of fake vnf fm alarms dictionary
"""
vnf_fm_alarms = []
for i in range(0, count):
unique_id = uuidutils.generate_uuid()
vnf_fm_alarms.append(vnf_fm_alarm_response(attrs={'id': unique_id}))
return vnf_fm_alarms
def vnf_fm_alarm_response(attrs=None, action=None):
"""Create a fake vnf fm alarm.
:param Dictionary attrs:
A dictionary with all attributes
:return:
A FakeVnfFmAlarm dict
"""
if action == 'update':
fake_vnf_fm_alarm = {
"ackState": "UNACKNOWLEDGED"
}
return fake_vnf_fm_alarm
attrs = attrs or {}
# Set default attributes.
fake_vnf_fm_alarm = {
"id": "78a39661-60a8-4824-b989-88c1b0c3534a",
"managedObjectId": "c61314d0-f583-4ab3-a457-46426bce02d3",
"vnfcInstanceIds": "0e5f3086-4e79-47ed-a694-54c29155fa26",
"rootCauseFaultyResource": {
"faultyResource": {
"vimConnectionId": "0d57e928-86a4-4445-a4bd-1634edae73f3",
"resourceId": "4e6ccbe1-38ec-4b1b-a278-64de09ba01b3",
"vimLevelResourceType": "OS::Nova::Server"
},
"faultyResourceType": "COMPUTE"
},
"alarmRaisedTime": "2021-09-03 10:21:03",
"alarmChangedTime": "2021-09-04 10:21:03",
"alarmClearedTime": "2021-09-05 10:21:03",
"alarmAcknowledgedTime": "2021-09-06 10:21:03",
"ackState": "UNACKNOWLEDGED",
"perceivedSeverity": "WARNING",
"eventTime": "2021-09-07 10:06:03",
"eventType": "EQUIPMENT_ALARM",
"faultType": "Fault Type",
"probableCause": "The server cannot be connected.",
"isRootCause": False,
"correlatedAlarmIds": [
"c88b624e-e997-4b17-b674-10ca2bab62e0",
"c16d41fd-12e2-49a6-bb17-72faf702353f"
],
"faultDetails": [
"Fault",
"Details"
],
"_links": {
"self": {
"href": "/vnffm/v1/alarms/"
"78a39661-60a8-4824-b989-88c1b0c3534a"
},
"objectInstance": {
"href": "/vnflcm/v1/vnf_instances/"
"0e5f3086-4e79-47ed-a694-54c29155fa26"
}
}
}
# Overwrite default attributes.
fake_vnf_fm_alarm.update(attrs)
return fake_vnf_fm_alarm
def get_vnffm_alarm_data(vnf_fm_alarm, columns=None):
"""Get the vnffm alarm.
:return:
A tuple object sorted based on the name of the columns.
"""
complex_attributes = [
'vnfcInstanceIds',
'rootCauseFaultyResource',
'correlatedAlarmIds',
'faultDetails',
'_links'
]
for attribute in complex_attributes:
if vnf_fm_alarm.get(attribute):
vnf_fm_alarm.update(
{attribute: tacker_osc_utils.FormatComplexDataColumn(
vnf_fm_alarm[attribute])})
# return the list of data as per column order
if columns:
return tuple([vnf_fm_alarm[key] for key in columns])
return tuple([vnf_fm_alarm[key] for key in sorted(
vnf_fm_alarm.keys())])

View File

@@ -0,0 +1,127 @@
# 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.
from oslo_utils import uuidutils
from tackerclient.osc import utils as tacker_osc_utils
def create_vnf_fm_subs(count=2):
"""Create multiple fake vnf packages.
:param int count:
The number of vnf_fm_subs to fake
:return:
A list of fake vnf fm subs dictionary
"""
vnf_fm_subs = []
for i in range(0, count):
unique_id = uuidutils.generate_uuid()
vnf_fm_subs.append(vnf_fm_sub_response(attrs={'id': unique_id}))
return vnf_fm_subs
def vnf_fm_sub_response(attrs=None):
"""Create a fake vnf fm sub.
:param Dictionary attrs:
A dictionary with all attributes
:return:
A FakeVnfFmAlarm dict
"""
attrs = attrs or {}
# Set default attributes.
fake_vnf_fm_sub = {
"id": "78a39661-60a8-4824-b989-88c1b0c3534a",
"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",
"_links": {
"self": {
"href": "/vnffm/v1/subscriptions/"
"78a39661-60a8-4824-b989-88c1b0c3534a"
}
}
}
# Overwrite default attributes.
fake_vnf_fm_sub.update(attrs)
return fake_vnf_fm_sub
def get_vnffm_sub_data(vnf_fm_sub, columns=None):
"""Get the vnffm sub.
:return:
A tuple object sorted based on the name of the columns.
"""
complex_attributes = ['filter', '_links']
for attribute in complex_attributes:
if vnf_fm_sub.get(attribute):
vnf_fm_sub.update(
{attribute: tacker_osc_utils.FormatComplexDataColumn(
vnf_fm_sub[attribute])})
# return the list of data as per column order
if columns:
return tuple([vnf_fm_sub[key] for key in columns])
return tuple([vnf_fm_sub[key] for key in sorted(
vnf_fm_sub.keys())])

View File

@@ -0,0 +1,134 @@
# 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.
from oslo_utils import uuidutils
from tackerclient.osc import utils as tacker_osc_utils
def create_vnf_pm_jobs(count=2):
"""Create multiple fake vnf pm jobs.
:param int count:
The number of vnf_pm_jobs to fake
:return:
A list of fake vnf pm jobs dictionary
"""
vnf_pm_jobs = []
for i in range(0, count):
unique_id = uuidutils.generate_uuid()
vnf_pm_jobs.append(vnf_pm_job_response(attrs={'id': unique_id}))
return {'vnf_pm_jobs': vnf_pm_jobs}
def vnf_pm_job_response(attrs=None, action=None):
"""Create a fake vnf pm job.
:param Dictionary attrs:
A dictionary with all attributes
:return:
A pm job dict
"""
if action == 'update':
fake_vnf_pm_job = {
"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"
}
}
}
return fake_vnf_pm_job
attrs = attrs or {}
# Set default attributes.
fake_vnf_pm_job = {
"id": "2bb72d78-b1d9-48fe-8c64-332654ffeb5d",
"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",
"reports": [{
"href": "/vnfpm/v2/pm_jobs/2bb72d78-b1d9-48fe-8c64-332654ffeb5d/"
"reports/09d46aed-3ec2-45d9-bfa2-add431e069b3",
"readyTime": "2022/07/25 10:43:55",
"expiryTime": "2022/07/25 10:43:55",
"fileSize": 9999
}],
"_links": {
"self": {
"href": "/vnfpm/v2/pm_jobs/"
"78a39661-60a8-4824-b989-88c1b0c3534a"
},
"objects": [{
"href": "/vnflcm/v1/vnf_instances/"
"0e5f3086-4e79-47ed-a694-54c29155fa26"
}]
}
}
# Overwrite default attributes.
fake_vnf_pm_job.update(attrs)
return fake_vnf_pm_job
def get_vnfpm_job_data(vnf_pm_job, columns=None):
"""Get the vnfpm job.
:return:
A tuple object sorted based on the name of the columns.
"""
complex_attributes = [
'objectInstanceIds', 'subObjectInstanceIds',
'criteria', 'reports', '_links',
'authentication'
]
for attribute in complex_attributes:
if vnf_pm_job.get(attribute):
vnf_pm_job.update(
{attribute: tacker_osc_utils.FormatComplexDataColumn(
vnf_pm_job[attribute])})
# return the list of data as per column order
if columns is None:
columns = sorted(vnf_pm_job.keys())
return tuple([vnf_pm_job[key] for key in columns])

View File

@@ -0,0 +1,73 @@
# 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.
from tackerclient.osc import utils as tacker_osc_utils
def vnf_pm_report_response(attrs=None):
"""Create a fake vnf pm report.
:param Dictionary attrs:
A dictionary with all attributes
:return:
A pm report dict
"""
attrs = attrs or {}
# Set default attributes.
fake_vnf_pm_report = {
"entries": [
{
"objectType": "VNFC",
"objectInstanceId": "2bb72d78-b1d9-48fe-8c64-332654ffeb5d",
"subObjectInstanceId": "09d46aed-3ec2-45d9-bfa2-add431e069b3",
"performanceMetric":
"VCpuUsagePeakVnf.2bb72d78-b1d9-48fe-8c64-332654ffeb5d,",
"performanceValues": [
{
"timeStamp": "2022/07/27 08:58:58",
"value": "1.88",
"context": {
"key": "value"
}
}
]
}
]
}
# Overwrite default attributes.
fake_vnf_pm_report.update(attrs)
return fake_vnf_pm_report
def get_vnfpm_report_data(vnf_pm_report, columns=None):
"""Get the vnfpm report.
:return:
A tuple object sorted based on the name of the columns.
"""
attribute = 'entries'
if vnf_pm_report.get(attribute):
vnf_pm_report.update(
{attribute: tacker_osc_utils.FormatComplexDataColumn(
vnf_pm_report[attribute])})
# return the list of data as per column order
if columns is None:
columns = sorted(vnf_pm_report.keys())
return tuple([vnf_pm_report[key] for key in columns])

View File

@@ -76,6 +76,68 @@ class TestVIMUtils(testtools.TestCase):
vim_utils.args2body_vim(config_param.copy(), vim)
self.assertEqual(expected_vim, vim)
def test_args2body_kubernetes_vim_oidc(self):
config_param = {'oidc_token_url': sentinel.oidc_token_url,
'username': sentinel.username,
'password': sentinel.password,
'client_id': sentinel.client_id,
'client_secret': sentinel.client_secret,
'ssl_ca_cert': "None",
'project_name': sentinel.prj_name,
'type': 'kubernetes'}
vim = {}
auth_cred = config_param.copy()
auth_cred.pop('project_name')
auth_cred.pop('type')
expected_vim = {'auth_cred': auth_cred,
'vim_project':
{'name': sentinel.prj_name},
'type': 'kubernetes'}
vim_utils.args2body_vim(config_param.copy(), vim)
self.assertEqual(expected_vim, vim)
def test_args2body_kubernetes_vim_extra(self):
extra_param = {
'helm_info': {
'masternode_ip': [
'192.168.10.110'
],
'masternode_username': 'helm_user',
'masternode_password': 'helm_pass'
}}
config_param = {'username': sentinel.usrname1,
'password': sentinel.password1,
'ssl_ca_cert': 'abcxyz',
'project_name': sentinel.prj_name,
'type': 'kubernetes',
'extra': extra_param}
vim = {}
auth_cred = config_param.copy()
auth_cred.pop('project_name')
auth_cred.pop('type')
auth_cred.pop('extra')
expected_vim = {'auth_cred': auth_cred,
'vim_project':
{'name': sentinel.prj_name},
'type': 'kubernetes',
'extra': extra_param}
vim_utils.args2body_vim(config_param.copy(), vim)
self.assertEqual(expected_vim, vim)
def test_args2body_kubernetes_vim_oidc_no_username(self):
config_param = {'oidc_token_url': sentinel.oidc_token_url,
'password': sentinel.password,
'client_id': sentinel.client_id,
'client_secret': sentinel.client_secret,
'ssl_ca_cert': "None",
'project_name': sentinel.prj_name,
'type': 'kubernetes'}
vim = {}
self.assertRaises(exceptions.TackerClientException,
vim_utils.args2body_vim,
config_param, vim)
def test_args2body_vim_no_project(self):
config_param = {'username': sentinel.usrname1,
'password': sentinel.password1,

View File

@@ -16,6 +16,7 @@
#
import logging
import re
import time
import requests
@@ -180,6 +181,8 @@ class ClientBase(object):
self.format = 'json'
self.action_prefix = "/v%s" % (self.version)
self.retry_interval = 1
self.rel = None
self.params = None
def _handle_fault_response(self, status_code, response_body):
# Create exception with HTTP status code and message
@@ -246,6 +249,19 @@ class ClientBase(object):
else:
self.format = 'json'
url = None
rel = None
link = resp.headers.get('Link', None)
if link is not None:
url = re.findall('<(.*)>', link)[0]
rel = re.findall('rel="(.*)"', link)[0]
if rel == 'next':
self.rel = 'next'
query_str = urlparse.urlparse(url).query
self.params = urlparse.parse_qs(query_str)
status_code = resp.status_code
if status_code in (requests.codes.ok,
requests.codes.created,
@@ -379,21 +395,23 @@ class ClientBase(object):
linkrel = 'next'
next = True
while next:
self.rel = None
res = self.get(path, headers=headers, params=params)
yield res
next = False
try:
# TODO(tpatil): Handle pagination for list data type
# once it's supported by tacker.
if type(res) is list:
break
for link in res['%s_links' % collection]:
if link['rel'] == linkrel:
query_str = urlparse.urlparse(link['href']).query
params = urlparse.parse_qs(query_str)
if self.rel == 'next':
params = self.params
next = True
break
else:
for link in res['%s_links' % collection]:
if link['rel'] == linkrel:
query_str = urlparse.urlparse(link['href']).query
params = urlparse.parse_qs(query_str)
next = True
break
except KeyError:
break
@@ -895,6 +913,10 @@ class VnfLCMClient(ClientBase):
'/vnflcm/{}/vnf_lcm_op_occs'.format(sol_api_version))
self.vnf_lcm_op_occs_path = (
'/vnflcm/{}/vnf_lcm_op_occs/%s'.format(sol_api_version))
self.lccn_subscriptions_path = (
'/vnflcm/{}/subscriptions'.format(sol_api_version))
self.lccn_subscription_path = (
'/vnflcm/{}/subscriptions/%s'.format(sol_api_version))
def build_action(self, action):
return action
@@ -951,6 +973,11 @@ class VnfLCMClient(ClientBase):
return self.post((self.vnf_lcm_op_occs_path + "/rollback") % occ_id,
headers=self.headers)
@APIParamsCall
def cancel_vnf_instance(self, occ_id, body):
return self.post((self.vnf_lcm_op_occs_path + "/cancel") % occ_id,
body=body)
@APIParamsCall
def fail_vnf_instance(self, occ_id):
return self.post((self.vnf_lcm_op_occs_path + "/fail") % occ_id,
@@ -961,6 +988,15 @@ class VnfLCMClient(ClientBase):
return self.post((self.vnf_instance_path + "/change_ext_conn") %
vnf_id, body=body, headers=self.headers)
@APIParamsCall
def change_vnfpkg_vnf_instance(self, vnf_id, body):
# NOTE: it is only supported by V2-API.
if self.vnf_instance_path.split('/')[2] == 'v2':
return self.post((self.vnf_instance_path + "/change_vnfpkg") %
vnf_id, body=body, headers=self.headers)
else:
raise exceptions.UnsupportedCommandVersion(version='1')
@APIParamsCall
def retry_vnf_instance(self, occ_id):
return self.post((self.vnf_lcm_op_occs_path + "/retry") % occ_id,
@@ -978,6 +1014,28 @@ class VnfLCMClient(ClientBase):
return self.get(self.vnf_lcm_op_occs_path % occ_id,
headers=self.headers)
@APIParamsCall
def create_lccn_subscription(self, body):
return self.post(self.lccn_subscriptions_path, body=body,
headers=self.headers)
@APIParamsCall
def delete_lccn_subscription(self, subsc_id):
return self.delete(self.lccn_subscription_path % subsc_id,
headers=self.headers)
@APIParamsCall
def list_lccn_subscriptions(self, retrieve_all=True, **_params):
subscriptions = self.list(None, self.lccn_subscriptions_path,
retrieve_all, headers=self.headers,
**_params)
return subscriptions
@APIParamsCall
def show_lccn_subscription(self, subsc_id):
return self.get(self.lccn_subscription_path % subsc_id,
headers=self.headers)
@APIParamsCall
def show_vnf_lcm_versions(self, major_version):
if major_version is None:
@@ -991,6 +1049,101 @@ class VnfLCMClient(ClientBase):
return self.get(path, headers={'Version': '2.0.0'})
class VnfFMClient(ClientBase):
headers = {'Version': '1.3.0'}
vnf_fm_alarms_path = '/vnffm/v1/alarms'
vnf_fm_alarm_path = '/vnffm/v1/alarms/%s'
vnf_fm_subs_path = '/vnffm/v1/subscriptions'
vnf_fm_sub_path = '/vnffm/v1/subscriptions/%s'
def build_action(self, action):
return action
@APIParamsCall
def list_vnf_fm_alarms(self, retrieve_all=True, **_params):
vnf_fm_alarms = self.list(
"vnf_fm_alarms", self.vnf_fm_alarms_path, retrieve_all,
headers=self.headers, **_params)
return vnf_fm_alarms
@APIParamsCall
def show_vnf_fm_alarm(self, vnf_fm_alarm_id):
return self.get(
self.vnf_fm_alarm_path % vnf_fm_alarm_id, headers=self.headers)
@APIParamsCall
def update_vnf_fm_alarm(self, vnf_fm_alarm_id, body):
return self.patch(
self.vnf_fm_alarm_path % vnf_fm_alarm_id, body=body,
headers=self.headers)
@APIParamsCall
def create_vnf_fm_sub(self, body):
return self.post(
self.vnf_fm_subs_path, body=body, headers=self.headers)
@APIParamsCall
def list_vnf_fm_subs(self, retrieve_all=True, **_params):
vnf_fm_subs = self.list("vnf_fm_subs", self.vnf_fm_subs_path,
retrieve_all, headers=self.headers, **_params)
return vnf_fm_subs
@APIParamsCall
def show_vnf_fm_sub(self, vnf_fm_sub_id):
return self.get(
self.vnf_fm_sub_path % vnf_fm_sub_id, headers=self.headers)
@APIParamsCall
def delete_vnf_fm_sub(self, vnf_fm_sub_id):
return self.delete(
self.vnf_fm_sub_path % vnf_fm_sub_id, headers=self.headers)
class VnfPMClient(ClientBase):
headers = {'Version': '2.1.0'}
vnf_pm_jobs_path = '/vnfpm/v2/pm_jobs'
vnf_pm_job_path = '/vnfpm/v2/pm_jobs/%s'
vnf_pm_reports_path = '/vnfpm/v2/pm_jobs/%(job_id)s/reports/%(report_id)s'
def build_action(self, action):
return action
@APIParamsCall
def create_vnf_pm_job(self, body):
return self.post(
self.vnf_pm_jobs_path, body=body, headers=self.headers)
@APIParamsCall
def list_vnf_pm_jobs(self, retrieve_all=True, **_params):
vnf_pm_jobs = self.list(
"vnf_pm_jobs", self.vnf_pm_jobs_path, retrieve_all,
headers=self.headers, **_params)
return vnf_pm_jobs
@APIParamsCall
def show_vnf_pm_job(self, vnf_pm_job_id):
return self.get(
self.vnf_pm_job_path % vnf_pm_job_id, headers=self.headers)
@APIParamsCall
def update_vnf_pm_job(self, vnf_pm_job_id, body):
return self.patch(
self.vnf_pm_job_path % vnf_pm_job_id, body=body,
headers=self.headers)
@APIParamsCall
def delete_vnf_pm_job(self, vnf_pm_job_id):
return self.delete(
self.vnf_pm_job_path % vnf_pm_job_id, headers=self.headers)
@APIParamsCall
def show_vnf_pm_report(self, vnf_pm_job_id, vnf_pm_report_id):
return self.get(
self.vnf_pm_reports_path % {
'job_id': vnf_pm_job_id, 'report_id': vnf_pm_report_id
}, headers=self.headers)
class Client(object):
"""Unified interface to interact with multiple applications of tacker service.
@@ -1013,6 +1166,8 @@ class Client(object):
def __init__(self, **kwargs):
api_version = kwargs.pop('api_version', '1')
self.vnf_lcm_client = VnfLCMClient(api_version, **kwargs)
self.vnf_fm_client = VnfFMClient(**kwargs)
self.vnf_pm_client = VnfPMClient(**kwargs)
self.vnf_package_client = VnfPackageClient(**kwargs)
self.legacy_client = LegacyClient(**kwargs)
@@ -1267,6 +1422,9 @@ class Client(object):
def change_ext_conn_vnf_instance(self, vnf_id, body):
return self.vnf_lcm_client.change_ext_conn_vnf_instance(vnf_id, body)
def change_vnfpkg_vnf_instance(self, vnf_id, body):
return self.vnf_lcm_client.change_vnfpkg_vnf_instance(vnf_id, body)
def delete_vnf_instance(self, vnf_id):
return self.vnf_lcm_client.delete_vnf_instance(vnf_id)
@@ -1276,6 +1434,9 @@ class Client(object):
def rollback_vnf_instance(self, occ_id):
return self.vnf_lcm_client.rollback_vnf_instance(occ_id)
def cancel_vnf_instance(self, occ_id, body):
return self.vnf_lcm_client.cancel_vnf_instance(occ_id, body)
def fail_vnf_instance(self, occ_id):
return self.vnf_lcm_client.fail_vnf_instance(occ_id)
@@ -1304,5 +1465,65 @@ class Client(object):
def show_vnf_lcm_op_occs(self, occ_id):
return self.vnf_lcm_client.show_vnf_lcm_op_occs(occ_id)
def create_lccn_subscription(self, body):
return self.vnf_lcm_client.create_lccn_subscription(body)
def delete_lccn_subscription(self, subsc_id):
return self.vnf_lcm_client.delete_lccn_subscription(subsc_id)
def list_lccn_subscriptions(self, retrieve_all=True, **_params):
return self.vnf_lcm_client.list_lccn_subscriptions(
retrieve_all=retrieve_all, **_params)
def show_lccn_subscription(self, subsc_id):
return self.vnf_lcm_client.show_lccn_subscription(subsc_id)
def show_vnf_lcm_versions(self, major_version):
return self.vnf_lcm_client.show_vnf_lcm_versions(major_version)
# VnfFMClient methods.
def list_vnf_fm_alarms(self, retrieve_all=True, **_params):
return self.vnf_fm_client.list_vnf_fm_alarms(
retrieve_all=retrieve_all, **_params)
def show_vnf_fm_alarm(self, vnf_fm_alarm_id):
return self.vnf_fm_client.show_vnf_fm_alarm(vnf_fm_alarm_id)
def update_vnf_fm_alarm(self, vnf_fm_alarm_id, body):
return self.vnf_fm_client.update_vnf_fm_alarm(vnf_fm_alarm_id, body)
def create_vnf_fm_sub(self, body):
return self.vnf_fm_client.create_vnf_fm_sub(body)
def list_vnf_fm_subs(self, retrieve_all=True, **_params):
return self.vnf_fm_client.list_vnf_fm_subs(
retrieve_all=retrieve_all, **_params)
def show_vnf_fm_sub(self, vnf_fm_sub_id):
return self.vnf_fm_client.show_vnf_fm_sub(vnf_fm_sub_id)
def delete_vnf_fm_sub(self, vnf_fm_sub_id):
return self.vnf_fm_client.delete_vnf_fm_sub(vnf_fm_sub_id)
# VnfPMClient methods.
def create_vnf_pm_job(self, body):
return self.vnf_pm_client.create_vnf_pm_job(body)
def list_vnf_pm_jobs(self, retrieve_all=True, **_params):
return self.vnf_pm_client.list_vnf_pm_jobs(
retrieve_all=retrieve_all, **_params)
def show_vnf_pm_job(self, vnf_pm_job_id):
return self.vnf_pm_client.show_vnf_pm_job(vnf_pm_job_id)
def update_vnf_pm_job(self, vnf_pm_job_id, body):
return self.vnf_pm_client.update_vnf_pm_job(vnf_pm_job_id, body)
def delete_vnf_pm_job(self, vnf_pm_job_id):
return self.vnf_pm_client.delete_vnf_pm_job(vnf_pm_job_id)
def show_vnf_pm_report(self, vnf_pm_job_id, vnf_pm_report_id):
return self.vnf_pm_client.show_vnf_pm_report(
vnf_pm_job_id, vnf_pm_report_id)

View File

@@ -1,5 +1,5 @@
[tox]
envlist = py38,py36,pep8,docs
envlist = py39,py38,py36,pep8,docs
minversion = 3.18.0
skipsdist = True
ignore_basepython_conflict = True
@@ -12,7 +12,7 @@ setenv = VIRTUAL_ENV={envdir}
LC_ALL=C
usedevelop = True
deps =
-c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/xena}
-c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/zed}
-r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
commands = stestr run --slowest {posargs}
@@ -30,7 +30,7 @@ commands = sphinx-build -W -b html doc/source doc/build/html
[testenv:releasenotes]
deps =
-c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/xena}
-c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/zed}
-r{toxinidir}/doc/requirements.txt
commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html