Compare commits

..

113 Commits

Author SHA1 Message Date
Zuul
ad36c1f5b6 Merge "Update TOX/UPPER_CONSTRAINTS_FILE for stable/train" into stable/train 2019-09-23 05:51:35 +00:00
Zuul
ef82c12b25 Merge "Update .gitreview for stable/train" into stable/train 2019-09-23 05:51:14 +00:00
Zuul
8f2bdf326f Merge "Add restFul methods to fix backward compatibility issue" into stable/train 2019-09-23 05:40:21 +00:00
nirajsingh
57692126a0 Add restFul methods to fix backward compatibility issue
Tacker functional tests and applications using old version
of tackerclient might be using post/get/delete/put/list
method interfaces provided by Client in their applications.
These interfaces were removed in the recent changes [1]
because of which tacker tests are failing.

Added post/delete/put/get/list methods back to the Client class.

[1] : https://review.opendev.org/#/c/679956

Closes-Bug: #1844625

Change-Id: I803298dbc16dea9e58ec2a0a5fe3afe431c8532c
(cherry picked from commit 31b7690858)
2019-09-22 19:49:22 +00:00
Hiroya Nakaya
d5a591eec0 fix:osc scale is failed.
parameter which isn't used is removed.
changed Command type.

Closes-Bug:#1834548

Change-Id: Id884f9ebd26f311ebc213bcadff6019aaee5cd3a
(cherry picked from commit 983a2402a4)
2019-09-22 19:49:01 +00:00
Hiroya Nakaya
b98ef94e46 fix:"openstack vnf set" command can't execute
internal function's args is wrong.

Closes-Bug: #1839232

Change-Id: Iea1becdc0ee5dea3a94d843c558f28bce38e4d1c
(cherry picked from commit 9a1020fc84)
2019-09-22 15:40:24 +00:00
be6b25e49a Update TOX/UPPER_CONSTRAINTS_FILE for stable/train
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/train branch, tests will
continue to use the upper-constraints list on master.

Change-Id: I937e88fdb199839fc25122835bee43f63c7e3820
2019-09-20 17:43:22 +00:00
f716570413 Update .gitreview for stable/train
Change-Id: I8a3f66189e172a8517462f3c48ed3eb3fed6cf64
2019-09-20 17:43:20 +00:00
Shubham Potale
edb7dad611 Add documentation for VNF Package commands
Added reference documentation for below vnf package commands.
1)vnf package create
2)vnf package list
3)vnf package show
4)vnf package upload
5)vnf package delete

Change-Id: Iad2536e81a6f419e25a4b6fd98b7b74ef0d965cd
Implements: bp tosca-csar-mgmt-driver
2019-09-13 10:34:41 +00:00
Shubham Potale
ffaf88505a Add command for upload vnf package API
Added support vnf package upload command and their unit test cases.

Please see result here.
http://paste.openstack.org/show/766299/

Change-Id: I88da4e209413a00d52c73fc83983ed2f5fd273c6
Implements: bp tosca-csar-mgmt-driver
2019-09-13 10:34:12 +00:00
Shubham Potale
11e23eed22 Add commands for list, show and delete vnf package API's
Added support for below three commands and their unit test cases.
1.vnf package list
2.vnf package show
3.vnf package delete

Please see results here
http://paste.openstack.org/show/774848/

Change-Id: I47e3e99a42aa36ab286fa01802999deb964a168f
Implements: bp tosca-csar-mgmt-driver
2019-09-13 10:32:19 +00:00
Shubham Potale
9940682bc8 OSC support for VNF package APIs
Added support vnf package create command and their unit test cases.

Please see the results here:
http://paste.openstack.org/show/775422/

Change-Id: Idf59847aaf33e360dae696eae819d217ae63570f
Implements: bp tosca-csar-mgmt-driver
2019-09-13 10:27:48 +00:00
Zuul
0edfda9171 Merge "Add Python 3 Train unit tests" 2019-09-06 08:03:05 +00:00
pengyuesheng
a1667a9f58 Add Python 3 Train unit tests
See the Train python3-updates goal document for details:
https://governance.openstack.org/tc/goals/train/python3-updates.html

Story: #2005924
Task: #34237

Change-Id: I32e05c3b0013dd3cb066f660a43ffd5b7896bd26
2019-08-02 11:12:11 +08:00
dharmendra
1e280fb1fe Adds support force delete for NS.
This patch adds '--force' parameter to NS delete command, to delete NS
forcefully.

blueprint force-delete-resources

Change-Id: I7d8eb7ae63f9e43da37701e3e8312e650ea5bd3c
2019-07-30 11:05:30 +00:00
jacky06
47996394b3 Replace git.openstack.org URLs with opendev.org URLs
1. Replace git.openstack.org URLs with opendev.org URLs
2. Update some urls

Change-Id: Icf9a7857be01056c0967fed0c777f2f46a993b6c
2019-06-19 15:48:59 +08:00
dharmendra
08b8922c49 update sphinx requirement
syncs sphinx dependency with global requirements.
It caps python 2 since sphinx 2.0 no longer supports Python 2.7.

Change-Id: Ic909ffca550329c8ba23e4ffe9f18f601d674a3b
2019-05-16 07:25:42 +00:00
Zuul
4921a75cd2 Merge "Drop py35 jobs" 2019-05-02 09:13:16 +00:00
dharmendra
6fd2d466ca Drop py35 jobs
Python 3.5 was the target runtime for the Rocky release. The current
target py3 runtime for Stein is Python 3.6, so there is no reason to
keep testing against the older version.

https://governance.openstack.org/tc/reference/runtimes/stein.html#python-runtime-for-stein

Change-Id: I136f092985be2d05ce933230add5f78469234b14
2019-04-30 09:24:30 +00:00
OpenDev Sysadmins
11c49bc027 OpenDev Migration Patch
This commit was bulk generated and pushed by the OpenDev sysadmins
as a part of the Git hosting and code review systems migration
detailed in these mailing list posts:

http://lists.openstack.org/pipermail/openstack-discuss/2019-March/003603.html
http://lists.openstack.org/pipermail/openstack-discuss/2019-April/004920.html

Attempts have been made to correct repository namespaces and
hostnames based on simple pattern matching, but it's possible some
were updated incorrectly or missed entirely. Please reach out to us
via the contact information listed at https://opendev.org/ with any
questions you may have.
2019-04-19 19:32:16 +00:00
4469984763 Update master for stable/stein
Add file to the reno documentation build to show release notes for
stable/stein.

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

Change-Id: I33c314ef84413b42ae5572a927a874d508eb27e4
Sem-Ver: feature
2019-03-18 14:55:54 +00:00
Zuul
c62beecc1f Merge "Update hacking version" 2019-03-18 11:22:01 +00:00
Zuul
8f84ee183b Merge "add python 3.7 unit test job" 2019-03-18 11:12:25 +00:00
Zuul
c7d27fc74f Merge "Add upper-constraints.txt to releasenotes tox environment" 2019-03-18 06:48:53 +00:00
jacky06
ab9aa0da69 Update hacking version
Use latest release 1.1.0 and compatible changes w.r.t pep8

Change-Id: I9596666d9bac8efae8ae22e027aca49a432069dc
2019-03-06 22:28:37 +08:00
ZhongShengping
fcd8965f6a add python 3.7 unit test job
This is a mechanically generated patch to add a unit test job running
under Python 3.7.

See ML discussion here [1] for context.

[1] http://lists.openstack.org/pipermail/openstack-dev/2018-October/135626.html

Change-Id: I121b0d1b2c217c14c6589455c0155f975f7793a7
Story: #2004073
Task: #27456
2019-02-19 17:05:51 +08:00
Zuul
f4839f308a Merge "Adds support force delete resources" 2019-02-12 08:35:42 +00:00
98k
6cb5d3c704 Add upper-constraints.txt to releasenotes tox environment
Without these dependencies, the releasenotes build does not actually
work.

Change-Id: Iabae8a5730d934473a36d9e2bc83a7005c1aca17
2019-02-11 05:53:32 +00:00
dharmendra
1fdf521ba9 Parameter changed from mgmt_url to mgmt_ip_address
In continuation of [1], fixing the same in tackerclient.
[1]: https://review.openstack.org/#/c/490471

Change-Id: I45dfe784e20f96cff12e346d34b1cce856a564db
2019-02-04 09:20:48 +00:00
Zuul
b5010b16e3 Merge "Use template for lower-constraints" 2019-01-16 02:16:26 +00:00
Zuul
0454016b6f Merge "Add python 3.6 unit test job" 2019-01-16 02:16:25 +00:00
Zuul
1763a4b392 Merge "Add Python 3.6 classifier to setup.cfg" 2019-01-14 15:29:58 +00:00
Vieri
2b87f41944 add python 3.6 unit test job
This is a mechanically generated patch to add a unit test job running
under Python 3.6 as part of the python3-first goal.

See the python3-first goal document for details:
https://governance.openstack.org/tc/goals/stein/python3-first.html

Change-Id: I6e5e543f2fcbeda881c506a34e6f55bcb3560c5b
Story: #2002586
2019-01-14 13:55:44 +00:00
Andreas Jaeger
417dfa0aa6 Use template for lower-constraints
Small cleanups:

* Use openstack-lower-constraints-jobs template, remove individual
  jobs.
* Sort list of templates

Change-Id: I50707932b24d0935f1cd04c334ff655e5d62a4fa
Needed-By: https://review.openstack.org/623229
2018-12-20 21:53:29 +01:00
prankul mahajan
16454d5e4c Change openstack-dev to openstack-discuss
Mailinglists have been updated. Openstack-discuss replaces
openstack-dev.

Change-Id: Ib23199e1de96ae222e16771dc97ecaefaf5b66bc
2018-12-04 11:37:43 +05:30
qingszhao
023373c934 Add Python 3.6 classifier to setup.cfg
Change-Id: If7af362ce8dca4ae0decacb487501b631567660f
2018-11-30 07:33:38 +00:00
Cong Phuoc Hoang
66794e007b Adds support force delete resources
This patch adds '--force' parameter to vnf delete command,
to force delete VNF instances.

e.g: openstack vnf delete --force VNF1

blueprint force-delete-resources

Change-Id: I20aee4ecb66643dfa5fad490956d898f7e851411
2018-11-28 10:25:44 +09:00
Nguyen Hai Truong
c4e310859b Add python 3.6 unit test job
This is a mechanically generated patch to add a unit test job running
under Python 3.6 as part of the python3-first goal.

See the python3-first goal document for details:
https://governance.openstack.org/tc/goals/stein/python3-first.html

Change-Id: I49e69314bd4e9ca2e4327cce878c1831a95b1df8
2018-11-06 17:42:34 -08:00
Nguyen Hai
71e0b047c8 Remove tacker command guide in docs
Tacker command is deprecated after Rocky release.

Change-Id: Ia71041edb4a3971483200fa3091fc58ea4dd0190
2018-09-04 21:39:52 +09:00
Nguyen Hai
b7023b38d3 switch documentation job to new PTI
This is a mechanically generated patch to switch the documentation
jobs to use the new PTI versions of the jobs as part of the
python3-first goal.

See the python3-first goal document for details:
https://governance.openstack.org/tc/goals/stein/python3-first.html

Change-Id: Id01cdc06dd23fa98dbcb8706c2751315e75fd29f
Story: #2002586
Task: #24324
2018-08-17 20:15:37 +09:00
Nguyen Hai
67081192b7 import zuul job settings from project-config
This is a mechanically generated patch to complete step 1 of moving
the zuul job settings out of project-config and into each project
repository.

Because there will be a separate patch on each branch, the branch
specifiers for branch-specific jobs have been removed.

Because this patch is generated by a script, there may be some
cosmetic changes to the layout of the YAML file(s) as the contents are
normalized.

See the python3-first goal document for details:
https://governance.openstack.org/tc/goals/stein/python3-first.html

Change-Id: I64b476cc4fdb1da25c79341d49e25ab4715062dc
Story: #2002586
Task: #24324
2018-08-17 20:14:58 +09:00
1d6a81f6f9 Update reno for stable/rocky
Change-Id: I1392f1d6ef8ef096595b6973b0b12e37d471cd58
2018-07-26 09:09:55 +00:00
Zuul
71897e549f Merge "Add reno note for updates in NS and VNFFG list commands" 2018-07-25 04:44:11 +00:00
Cong Phuoc Hoang
c80fe1ada6 Add reno note for updates in NS and VNFFG list commands
This patch adds reno note for https://review.openstack.org/#/c/578638

Change-Id: Icaeece0f4832991c99915097d2c2a7a56c289125
2018-07-25 03:56:32 +00:00
Zuul
423c23acf0 Merge "Add reno: Fix the VNFFG update osc command" 2018-07-25 03:41:27 +00:00
Zuul
85358af1cf Merge "Add reno: Fix cannot show the VNFFGD template" 2018-07-25 03:24:24 +00:00
Nguyen Hai
fdb2aba04f Add reno: Fix the VNFFG update osc command
Add reno for this bug:
https://review.openstack.org/#/c/551350/

Change-Id: I32502042b2a9bcd68cea410ca7ee8b77868aad20
2018-07-25 03:23:27 +00:00
Zuul
cef0494c6f Merge "Trivial: Fix a typo in release notes" 2018-07-25 03:19:06 +00:00
Nguyen Hai
2beb75af6e Trivial: Update HACKING.rst with stestr
The HACKING.rst testing section is updated a bit to point out
that we use stestr now instead of testr.

Change-Id: Ia4417994def85c989df315ebc17f5a0b8e0c0e98
2018-07-25 01:48:31 +00:00
Nguyen Hai
626233f668 Trivial: Fix a typo in release notes
Change-Id: I925b2896748c9bd7d025bc4139d82c98124f05a9
2018-07-25 10:32:57 +09:00
Nguyen Hai
2df32b7b59 Add reno: Fix cannot show the VNFFGD template
Add reno for this bug:
https://review.openstack.org/#/c/546703/

Change-Id: I661ca022a701fd0409fc34ae08b5aa5e5f981114
2018-07-25 01:15:55 +00:00
Sean McGinnis
c6f918cc51 Fix errors in README
Pypi now enforces some package checks and will reject uploads if errors
are found, causing problems with new releases. Most projects by default
set their README as the source for the package description, so any RST
formatting errors in the README can be the source of these validation
failures.

The README file for python-tackerclient would fail validation since it
would appear the numbered bullet list was not formatted correcting,
making the second bullet not appear to be part of the same list and
getting the validation error:

warning: check: Enumerated list start value not ordinal-1: "2" (ordinal 2)

To make this section of the README consistent with the following
section, the numbered bullets are just removed, getting rid of the
validation errors.

Change-Id: I653f98b0b5b5c223cb15bfb2b2fe5400c61a1714
2018-07-23 07:51:44 -05:00
wu.chunyang
a88992709c Add release note link in README
Change-Id: I9fc6252d8d4f488bbfb3190ec50b309a24ec792f
2018-07-21 04:31:33 +00:00
Zuul
f4e696dfe0 Merge "Updates in NS and VNFFG list commands" 2018-07-19 02:16:24 +00:00
Cong Phuoc Hoang
ba372bfcfd Updates in NS and VNFFG list commands
This patch will do 2 things:
1. Add 'vnf_ids' and 'vnffg_ids' fields as outputs from network
service list command. Users can know which VNFs or VNFFG, that
belongs to specific NS.
2. Add 'ns_id' fields to VNFFG list command, that shows which
network service the current VNFFG belongs to it.

Partially-implements: blueprint vnffg-ns

Change-Id: If6c5550f94e676fb2062e32ddc069acd5dfb6490
2018-07-11 16:28:56 +09:00
Nguyen Hai
019a846198 Add deprecation note for tacker CLI
Change-Id: I603d1d19630f045d61be6520bb01737c47af1178
2018-07-11 01:32:48 +00:00
dharmendra
ed23c0e7bd Fix: Tackerclient failed to handle yaml load error.
While onboarding a vnfd with infected vnfd file, yaml.load() raised exception.
Due to which instead of giving meaningful error message, tackerclient raised
traces with TypeError.
Fixing this issue with proper error handling.

Change-Id: I7a4150a898fcf99d8ffbc97e16da6a219cade93d
Closes-Bug: #1780214
2018-07-10 00:49:59 +00:00
Doug Hellmann
efb7704778 fix tox python3 overrides
We want to default to running all tox environments under python 3, so
set the basepython value in each environment.

We do not want to specify a minor version number, because we do not
want to have to update the file every time we upgrade python.

We do not want to set the override once in testenv, because that
breaks the more specific versions used in default environments like
py35 and py36.

Co-Authored-By: Nguyen Hai <nguyentrihai93@gmail.com>
Change-Id: I5279f9f798c1a9d198e951c8948db78821c7ed7b
Signed-off-by: Doug Hellmann <doug@doughellmann.com>
2018-06-25 01:11:32 +09:00
Zuul
2156f7924d Merge "Switch to using stestr" 2018-06-24 15:01:00 +00:00
Nguyen Hai
9ff673557e Update homepage link in setup.cfg
This patch also adds a note about deprecation of 'tacker' command line.

This patch also remove redundant section in setup.cfg related to
building docs.
See: http://lists.openstack.org/pipermail/openstack-dev/2018-March/128594.html

Change-Id: Ic8206eeef5c2cdb75834011eede48fa8b998481a
2018-06-19 17:57:14 +09:00
Nguyen Hai
e8b1634d8f Switch to using stestr
stestr is maintained project to which all Openstack projects
should migrate.

[1] https://etherpad.openstack.org/p/YVR-python-pti
[2] https://governance.openstack.org/tc/reference/pti/python.html

Change-Id: Icc11037bd56a75a8d9abbdfda10a832437502565
2018-06-07 17:57:58 +09:00
Zuul
4ebb371e35 Merge "add lower-constraints job" 2018-03-27 07:59:15 +00:00
ShangXiao
42dcd919b7 Fix the old doc links
Fix the old doc links in CONTRIBUTING.rst
and doc/source/index.rst.

Change-Id: I272742d6c344ea9eb6cb8ab18521049cc7e4a9da
2018-03-26 00:44:22 -07:00
Doug Hellmann
6184fd9f27 add lower-constraints job
Create a tox environment for running the unit tests against the lower
bounds of the dependencies.

Create a lower-constraints.txt to be used to enforce the lower bounds
in those tests.

Add openstack-tox-lower-constraints job to the zuul configuration.

See http://lists.openstack.org/pipermail/openstack-dev/2018-March/128352.html
for more details.

Change-Id: Ib1838be4ecd55a070fa30990b25f3135b60c35d4
Depends-On: https://review.openstack.org/555034
Signed-off-by: Doug Hellmann <doug@doughellmann.com>
2018-03-22 18:04:32 -04:00
Zuul
2ddc24c60e Merge "Fix cannot show the VNFFGD template" 2018-03-17 00:43:36 +00:00
rtmdk
f042f53d97 Fix some reST field lists in docstrings
Probably the most common format for documenting arguments is reST field
lists [1]. This change updates some docstrings to comply with the field
lists syntax.

[1] http://sphinx-doc.org/domains.html#info-field-lists

Change-Id: Ic87b30963f99552e57d10e8429a06511744b2489
2018-03-16 09:01:28 +00:00
OpenStack Proposal Bot
99abf33642 Updated from global requirements
Change-Id: I5b03636632009c55b8d2f6047c4d29a5b20d10f1
2018-03-15 08:05:06 +00:00
Zuul
9879a46f58 Merge "Fix the VNFFG update osc command" 2018-03-14 15:13:00 +00:00
Zuul
9cd0fc13f1 Merge "Add reno note for tacker support osc commands" 2018-03-14 15:08:17 +00:00
Nguyen Hai
1a31f11a53 Add reno note for tacker support osc commands
Change-Id: Ie1f7baa51723eb06ab4ed431a99d6a429a0bf234
Implements: blueprint tacker-support-python-openstackclient
2018-03-14 00:54:25 +09:00
Zuul
6cdc4cf084 Merge "Updated from global requirements" 2018-03-13 15:16:23 +00:00
Zuul
4308bf66ad Merge "Deperate tacker command lines" 2018-03-13 14:04:03 +00:00
gongysh
a325233647 Deperate tacker command lines
Since openstack tacker plugins are almost done, it is time
to deprecate the tacker command lines.

test paste log http://paste.openstack.org/show/699593/

Closes-bug: #1755443

Change-Id: I0c233f5f3003a50a6b76d6729c4d228cbba182a7
2018-03-13 21:45:01 +08:00
dharmendra
e40b9c8a47 Complete VNF osc commands
Please see the results here:
http://paste.openstack.org/show/696708/
http://paste.openstack.org/show/699455/ (list commands)

Change-Id: Iea353ef119710660e19fd4d8f209ebe500cd0afe
Implements: blueprint tacker-support-python-openstackclient
Co-Authored-By: Nguyen Hai <nguyentrihai93@gmail.com>
2018-03-13 18:42:20 +09:00
OpenStack Proposal Bot
d5a8170a35 Updated from global requirements
Change-Id: Ia2503828f106a44e80706beeaa32e39356d385fb
2018-03-13 07:29:43 +00:00
Nguyen Hai
d7a59b951d Fix the VNFFG update osc command
VNFFG update osc command misuse create_vnffg function.

Change-Id: If3fbb077936c6c698744210384373b77d5986e64
Implements: blueprint tacker-support-python-openstackclient
Closes-Bug: #1754793
2018-03-13 15:12:00 +09:00
Nguyen Hai
e3b3d7e50c Add documentation for python-tackerclient
This patch also fix "InterpreterNotFound: pypy" error in tox.ini

This patch also follow the new PTI for document build [1][2]

[1] https://governance.openstack.org/tc/reference/project-testing-interface.html
[2] http://lists.openstack.org/pipermail/openstack-dev/2017-December/125710.html

Change-Id: I9fb6637cb95603532b46f89dc4beab185278c833
Closes-bug: #1754556
Closes-bug: #1754926
2018-03-13 12:13:51 +09:00
OpenStack Proposal Bot
ae77851d98 Updated from global requirements
Change-Id: I03936e22744419045a3140a8206011de61180219
2018-03-10 13:51:24 +00:00
Nguyen Hai
30bd8ffd1a Complete VNFFG & related VNFFG osc commands
openstack vnf graph
openstack vnf chain
openstack vnf classifier
openstack vnf network forwarding path

Please see the results here:
http://paste.openstack.org/show/684261/

Change-Id: I02c18a460eb412af76d1aa1c77102ba92e174566
Implements: blueprint tacker-support-python-openstackclient
2018-03-03 22:25:21 +09:00
Zuul
04584a666d Merge "Complete NS osc commands" 2018-03-01 02:17:18 +00:00
Zuul
9b2027930b Merge "Do not have to mention ssl_ca_cert in vim config file (client)" 2018-03-01 02:13:41 +00:00
Nguyen Hai
09c13e1c5d Complete NS osc commands
Please see the results here:
http://paste.openstack.org/show/686780/

Change-Id: If71aa334bc988eba7939b5d55692d80530ba0bba
Implements: blueprint tacker-support-python-openstackclient
2018-03-01 01:48:49 +09:00
Nguyen Hai
7a13c3ce96 Complete Event osc commands
Please see the results here:
http://paste.openstack.org/show/683041/

Change-Id: Ib6b35b5757fb63465e0f587e47c122313b909720
Implements: blueprint tacker-support-python-openstackclient
2018-02-28 23:11:54 +09:00
Nguyen Hai
75b316ae51 Complete NSD osc commands
Please see the results here:
http://paste.openstack.org/show/680416/

Change-Id: I9abbd140cdbe0a8245d7903c5c7e90b61f92eeee
Implements: blueprint tacker-support-python-openstackclient
2018-02-28 15:40:59 +09:00
Trinh Nguyen
17d108e146 Do not have to mention ssl_ca_cert in vim config file (client)
Current tacker client requires us to mention ssl_ca_cert
and set it to None if we don't want to use ssl cert. This
patch along with another patch on the tacker server side
will make ssl_ca_cert a truly optional config. And, only
the correct ssl_ca_cert will be able to authenticate.

Tacker Server changes: https://review.openstack.org/#/c/546580/

Change-Id: Ic87fe3382e100183c685c3b34768a5a5de889982
2018-02-26 16:17:40 +09:00
Zuul
9a36c0e4ea Merge "Add --tenant-id in VIM & VNFD osc commands" 2018-02-26 05:31:36 +00:00
Zuul
68c0c9d0f1 Merge "Fix "F821 undefined name 'unicode' error when run tox pep8" 2018-02-26 02:51:29 +00:00
Zuul
331588ab12 Merge "Complete VNFFGD osc commands" 2018-02-26 02:47:19 +00:00
Trinh Nguyen
a6b721690c Fix "F821 undefined name 'unicode' error when run tox pep8
This patch fix the error when running tox pep8 on the master
branch of python-tackerclient:

./tackerclient/tacker/v1_0/vnfm/vnf.py:166:62: F821 undefined
name 'unicode'
            if isinstance(config, str) or isinstance(config, unicode):
                                                             ^
ERROR: InvocationError:
    '/home/projects/python-tackerclient/.tox/pep8/bin/flake8'

Change-Id: I366923e2759ffd85bb4594b82b55472bbdb7f783
Closes-bug: #1751441
2018-02-24 20:17:29 +09:00
Nguyen Hai
d967a881aa Complete VNFFGD osc commands
Please see the results here:
http://paste.openstack.org/show/680427/

Change-Id: I800c71a2c2cf5d1710ed6b4c9e4d5b63b31ced4f
Implements: blueprint tacker-support-python-openstackclient
2018-02-22 23:08:45 +09:00
Nguyen Hai
a7b17cc238 Add --tenant-id in VIM & VNFD osc commands
Compare to tacker commands, the vim and vnfd in osc commands
do not have --tenant-id (or project id) argument when registering
or creating the vim or vnfd.

Change-Id: I53022ca915d5119668777fcebf2af25199b5c326
Implements: blueprint tacker-support-python-openstackclient
2018-02-22 23:02:10 +09:00
Nguyen Hai
0f64739dda Update README.rst and add CONTRIBUTING.rst to repo
Change-Id: Iad7fb5999ae5d9e92f91f5904916b4aaa892cf21
2018-02-22 17:14:22 +09:00
Nguyen Hai
82c70bd5a1 Fix cannot show the VNFFGD template
There is the wrong index of the vnffgd data in the code.

Change-Id: I72050c8afa8f549a4ac35036ccd969b14d5f3f81
Closes-Bug: #1750865
2018-02-22 02:16:05 +09:00
Zuul
c5eb5c319f Merge "Fix typo" 2018-02-21 04:47:57 +00:00
Zuul
1cb2431782 Merge "Complete VNFD osc commands" 2018-02-16 08:33:58 +00:00
gaofei
c5ed02513c Fix typo
Change-Id: I7d6e3c0dea4a4a37fd39f4b0c3150b655069b0c9
2018-02-09 15:22:25 +08:00
dharmendra
de7efad575 Complete VNFD osc commands
Change-Id: Ifd5c7236dc5b4b92e1488271a1fe79a846995a69
Implements: blueprint tacker-support-python-openstackclient
2018-02-08 07:23:07 +00:00
Zuul
ac8394730f Merge "Revert "Add reno note for classifier name field in its list command" it should be at stable/queens branch This reverts commit df80486f32cf0011faadcbbc73e595e4afd4364c." 2018-02-07 09:28:03 +00:00
Zuul
c097d89ff9 Merge "Revert "Add reno note for cert_verify in vim config file" this should be in queens branch This reverts commit e557ecde7274da77c7aa6fa77227f8eb2e5f9a9e." 2018-02-07 09:28:02 +00:00
gongysh
c3fc53a4f3 Revert "Add reno note for classifier name field in its list command"
it should be at stable/queens branch
This reverts commit df80486f32.

Change-Id: I62ee562814d3795368ef733381bc2440e82856c0
2018-02-07 09:13:36 +00:00
gongysh
f037a0b8ed Revert "Add reno note for cert_verify in vim config file"
this should be in queens branch
This reverts commit e557ecde72.

Change-Id: Ideb1d20623b8ed5fe898a0c29e13562a91832cc7
2018-02-07 09:11:47 +00:00
Zuul
1c4e32d0f6 Merge "Revert "Add reno note for vnffg template updation command"" 2018-02-07 09:06:26 +00:00
gongysh
a23aa68676 Revert "Add reno note for vnffg template updation command"
this is queens feature.

This reverts commit 5ab6421941.

Change-Id: I6e7591a289a297d8e4f9e29f7c3fc945a242f062
2018-02-07 08:54:08 +00:00
Zuul
f76a15d4ec Merge "Update reno for stable/queens" 2018-02-07 08:37:54 +00:00
Zuul
1062eb78bb Merge "Add reno note for vnffg template updation command" 2018-02-07 08:21:33 +00:00
Zuul
75d08f07d9 Merge "Add reno note for cert_verify in vim config file" 2018-02-07 08:17:40 +00:00
Zuul
8fe5966b3e Merge "Add reno note for classifier name field in its list command" 2018-02-07 08:13:36 +00:00
Zuul
5d338b8a75 Merge "Implement Tacker Client to support VNF cluster features" 2018-02-07 08:13:35 +00:00
LongKB
4d0cd3b7f2 Implement Tacker Client to support VNF cluster features
Adding a new CLI commands to support VNF cluster and VNF cluster
member CRUD operations:
      tacker cluster-create
      tacker cluster-show
      tacker cluster-list
      tacker cluster-delete

      tacker cluster-create
      tacker cluster-show
      tacker cluster-list
      tacker cluster-delete

Implements: blueprint policy-based-vnf-cluster
Change-Id: I76537566000f3d1724c9f566910389ede23f49b7
Co-Authored-By: xuan0802 <thespring1989@gmail.com>
2018-02-07 15:57:16 +08:00
gongysh
e557ecde72 Add reno note for cert_verify in vim config file
Change-Id: If5028ed350e90eea62e1a06a9c4ab173bb60e963
2018-02-07 15:37:04 +08:00
gongysh
5ab6421941 Add reno note for vnffg template updation command
Change-Id: I986a7d67d1810553c30436c32ede324cca032310
2018-02-07 15:32:29 +08:00
gongysh
df80486f32 Add reno note for classifier name field in its list command
Change-Id: Id13f67458aba5165706a08ebe2e2651f5631a108
2018-02-07 15:28:12 +08:00
d1c3710c0d Update reno for stable/queens
Change-Id: Ibff9c86b210c70751c78378a6b7156345023f6f5
2018-02-01 16:34:55 +00:00
82 changed files with 6041 additions and 203 deletions

1
.gitignore vendored
View File

@@ -20,6 +20,7 @@ run_tests.log
.idea/
.tox/
.venv/
.stestr/
# Files created by releasenotes build
releasenotes/build

View File

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

3
.stestr.conf Normal file
View File

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

View File

@@ -1,4 +0,0 @@
[DEFAULT]
test_command=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 ${PYTHON:-python} -m subunit.run discover -t ./ ${OS_TEST_PATH:-./tackerclient/tests/unit} $LISTOPT $IDOPTION
test_id_option=--load-list $IDFILE
test_list_option=--list

View File

@@ -1,7 +1,8 @@
- project:
templates:
- openstack-python-jobs
- openstack-python35-jobs-nonvoting
- check-requirements
- publish-openstack-sphinx-docs
- release-notes-jobs
- openstack-lower-constraints-jobs
- openstack-python-jobs
- openstack-python3-train-jobs
- publish-openstack-docs-pti
- release-notes-jobs-python3

16
CONTRIBUTING.rst Normal file
View File

@@ -0,0 +1,16 @@
If you would like to contribute to the development of OpenStack,
you must follow the steps in this page:
https://docs.openstack.org/infra/manual/developers.html
Once those steps have been completed, changes to OpenStack
should be submitted for review via the Gerrit tool, following
the workflow documented at:
https://docs.openstack.org/infra/manual/developers.html#development-workflow
Pull requests submitted through GitHub will be ignored.
Bugs should be filed on Launchpad, not GitHub:
https://bugs.launchpad.net/python-tackerclient

View File

@@ -1,5 +1,5 @@
Tacker Style Commandments
================================
=========================
- Step 1: Read the OpenStack Style Commandments
https://docs.openstack.org/hacking/latest
@@ -8,19 +8,19 @@ Tacker Style Commandments
Running Tests
-------------
The testing system is based on a combination of tox and testr. The canonical
approach to running tests is to simply run the command `tox`. This will
The testing system is based on a combination of tox and stestr. The canonical
approach to running tests is to simply run the command ``tox``. This will
create virtual environments, populate them with dependencies and run all of
the tests that OpenStack CI systems run. Behind the scenes, tox is running
`testr run --parallel`, but is set up such that you can supply any additional
testr arguments that are needed to tox. For example, you can run:
`tox -- --analyze-isolation` to cause tox to tell testr to add
``stestr run``, but is set up such that you can supply any additional
stestr arguments that are needed to tox. For example, you can run:
``tox -- --analyze-isolation`` to cause tox to tell stestr to add
--analyze-isolation to its argument list.
It is also possible to run the tests inside of a virtual environment
you have created, or it is possible that you have all of the dependencies
installed locally already. In this case, you can interact with the testr
command directly. Running `testr run` will run the entire test suite. `testr
run --parallel` will run it in parallel (this is the default incantation tox
uses.) More information about testr can be found at:
https://wiki.openstack.org/wiki/Testr
installed locally already. In this case, you can interact with the stestr
command directly. Running ``stestr run`` will run the entire test suite.
``stestr run --concurrency=1`` will run tests serially (by default, stestr runs
tests in parallel). More information about stestr can be found at:
http://stestr.readthedocs.io/

View File

@@ -7,4 +7,63 @@ Team and repository tags
.. Change things from this point on
This is the client API library for Tacker.
NFV Orchestration (Tacker) Client
=================================
CLI and Client Library for OpenStack Tacker
Installation
============
**Note:** The paths we are using for configuration files in these steps
are with reference to Ubuntu Operating System. The paths may vary for
other Operating Systems.
The branch_name which is used in commands, specify the branch_name
as stable/<branch> for any stable branch installation. For eg:
stable/queens, stable/pike. If unspecified the default will be
master branch.
Using python install
--------------------
Clone python-tackerclient repository.
::
$ cd ~/
$ git clone https://github.com/openstack/python-tackerclient -b <branch_name>
Install python-tackerclient.
::
$ cd python-tackerclient
$ sudo python setup.py install
Using pip
---------
You can also install the latest version by using ``pip`` command:
::
$ pip install python-tackerclient
Or, if it is needed to install ``python-tackerclient`` from master branch,
type
::
$ pip install git+https://github.com/openstack/python-tackerclient.git
More Information
================
* Python-tackerclient documentation: https://docs.openstack.org/python-tackerclient/latest/
* Tacker Documentation: https://docs.openstack.org/tacker/latest/
* Tacker Wiki: https://wiki.openstack.org/wiki/Tacker
* Release Notes: https://docs.openstack.org/releasenotes/python-tackerclient

8
doc/requirements.txt Normal file
View File

@@ -0,0 +1,8 @@
# The order of packages is significant, because pip processes them in the order
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
# These are needed for docs generation
sphinx!=1.6.6,!=1.6.7,>=1.6.2,<2.0.0;python_version=='2.7' # BSD
sphinx!=1.6.6,!=1.6.7,>=1.6.2;python_version>='3.4' # BSD
openstackdocstheme>=1.18.1 # Apache-2.0
reno>=2.5.0 # Apache-2.0

View File

@@ -0,0 +1,85 @@
..
Licensed under the Apache License, Version 2.0 (the "License"); you may
not use this file except in compliance with the License. You may obtain
a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
Convention for heading levels in Neutron devref:
======= Heading 0 (reserved for the title in a document)
------- Heading 1
~~~~~~~ Heading 2
+++++++ Heading 3
''''''' Heading 4
(Avoid deeper levels because they do not render well.)
=============
Command List
=============
The following list covers the extended commands for Tacker services
available in **openstack** command.
These commands can be referenced by doing **openstack help** and the detail
of individual command can be referred by **openstack help <command-name>**.
.. code-block:: console
openstack vnf create Create a VNF.
openstack vnf delete Delete given VNF(s).
openstack vnf list List VNF(s) that belong to a given tenant.
openstack vnf resource list List resources of a VNF like VDU, CP, etc.
openstack vnf scale Scale a VNF.
openstack vnf show Show information of a given VNF.
openstack vnf set Update a given VNF.
openstack vnf descriptor create Create a VNFD.
openstack vnf descriptor delete Delete given VNFD(s).
openstack vnf descriptor list List VNFD(s) that belong to a given tenant.
openstack vnf descriptor show Show information of a given VNFD.
openstack vnf descriptor template show Show template of a given VNFD.
openstack vim list List VIM(s) that belong to a given tenant.
openstack vim register Create a VIM.
openstack vim show Show information of a given VIM.
openstack vim set Update a given VIM.
openstack vim delete Delete given VIM(s).
openstack ns create Create a NS.
openstack ns delete Delete given NS(s).
openstack ns list List NS that belong to a given tenant.
openstack ns show Show information of a given NS.
openstack ns descriptor create Create a NSD.
openstack ns descriptor delete Delete a given NSD.
openstack ns descriptor list List NSD(s) that belong to a given tenant.
openstack ns descriptor show Show information of a given NSD.
openstack ns descriptor template show Show template of a given NSD.
openstack vnf graph create Create a VNFFG.
openstack vnf graph delete Delete a given VNFFG.
openstack vnf graph list List VNFFG(s) that belong to a given tenant.
openstack vnf graph show Show information of a given VNFFG.
openstack vnf graph set Update a given VNFFG.
openstack vnf graph descriptor create Create a VNFFGD.
openstack vnf graph descriptor delete Delete a given VNFFGD.
openstack vnf graph descriptor list List VNFFGD(s) that belong to a given tenant.
openstack vnf graph descriptor show Show information of a given VNFFGD.
openstack vnf graph descriptor template show Show template of a given VNFFGD.
openstack vnf chain list List SFC(s) that belong to a given tenant.
openstack vnf chain show Show information of a given SFC.
openstack vnf classifier list List FC(s) that belong to a given tenant.
openstack vnf classifier show Show information of a given FC.
openstack vnf network forwarding path list List NFP(s) that belong to a given tenant.
openstack vnf network forwarding path show Show information of a given NFP.
openstack nfv event show Show event given the event id.
openstack nfv event list List events of resources.
openstack vnf package create Create a new individual VNF package resource.
openstack vnf package list List all VNF packages.
openstack vnf package show Show package details.
openstack vnf package upload Upload a VNF package by providing the address information
of the VNF package.
openstack vnf package delete Delete given VNF package(s).

View File

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

View File

@@ -0,0 +1,9 @@
====================
VNF Package commands
====================
VNF Package commands are CLI interface of VNF Package Management Interface in
`ETSI NFV-SOL 005 <https://www.etsi.org/deliver/etsi_gs/NFV-SOL/001_099/005/02.06.01_60/gs_NFV-SOL005v020601p.pdf>`_.
.. autoprogram-cliff:: openstack.tackerclient.v1
:command: vnf package *

View File

@@ -1,7 +1,29 @@
# -*- coding: utf-8 -*-
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# -- General configuration ---------------------------------------------
# python-tackerclient documentation build configuration file
import os
import sys
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
# sys.path.append(os.path.abspath('.'))
sys.path.insert(0, os.path.abspath('../..'))
# -- General configuration ----------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
@@ -12,12 +34,6 @@ extensions = [
'cliff.sphinxext',
]
# openstackdocstheme options
repository_name = 'openstack/python-tackerclient'
bug_project = 'python-tackerclient'
bug_tag = 'doc'
html_last_updated_fmt = '%Y-%m-%d %H:%M'
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@@ -28,7 +44,8 @@ source_suffix = '.rst'
master_doc = 'index'
# General information about the project.
copyright = u'OpenStack Foundation'
project = 'python-tackerclient'
copyright = 'OpenStack Contributors'
# If true, '()' will be appended to :func: etc. cross-reference text.
add_function_parentheses = True
@@ -40,7 +57,7 @@ add_module_names = True
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# -- Options for HTML output ---------------------------------------------
# -- Options for HTML output --------------------------------------------------
# The theme to use for HTML and HTML Help pages. Major themes that come with
# Sphinx are currently 'default' and 'sphinxdoc'.
@@ -49,6 +66,23 @@ html_theme = 'openstackdocs'
# Output file base name for HTML help builder.
htmlhelp_basename = 'tackerclientdoc'
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
html_last_updated_fmt = '%Y-%m-%d %H:%M'
# -- Options for manual page output -------------------------------------------
man_pages = [
('cli/index', 'tacker', u'Client for Tacker API',
[u'OpenStack Contributors'], 1),
]
# -- Options for openstackdocstheme -------------------------------------------
repository_name = 'openstack/python-tackerclient'
bug_project = 'python-tackerclient'
bug_tag = 'doc'
# -- Options for cliff.sphinxext plugin ---------------------------------------
autoprogram_cliff_application = 'openstack'
autoprogram_cliff_application = 'openstack'

View File

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

View File

@@ -0,0 +1,195 @@
..
Licensed under the Apache License, Version 2.0 (the "License"); you may
not use this file except in compliance with the License. You may obtain
a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
Convention for heading levels in Neutron devref:
======= Heading 0 (reserved for the title in a document)
------- Heading 1
~~~~~~~ Heading 2
+++++++ Heading 3
''''''' Heading 4
(Avoid deeper levels because they do not render well.)
===================================
Developing with Python-TackerClient
===================================
Project Info
============
* **Free software:** under the `Apache license <http://www.apache.org/licenses/LICENSE-2.0>`_
* **Tacker Service:** https://opendev.org/openstack/tacker
* **Tacker Client Library:** https://opendev.org/openstack/python-tackerclient
* **Tacker Service Bugs:** https://bugs.launchpad.net/tacker
* **Client Bugs:** https://bugs.launchpad.net/python-tackerclient
* **Blueprints:** https://blueprints.launchpad.net/tacker
Meetings
========
For details please refer to the `OpenStack IRC meetings`_ page.
.. _`OpenStack IRC meetings`: http://eavesdrop.openstack.org/#Tacker_(NFV_Orchestrator_and_VNF_Manager)_Team_Meeting
Testing
=======
Install the prerequisites for Tox:
* On Ubuntu or Debian:
.. code-block:: bash
$ apt-get install gcc gettext python-dev libxml2-dev libxslt1-dev \
zlib1g-dev
You may need to use pip install for some packages.
* On RHEL or CentOS including Fedora:
.. code-block:: bash
$ yum install gcc python-devel libxml2-devel libxslt-devel
* On openSUSE or SUSE linux Enterprise:
.. code-block:: bash
$ zypper install gcc python-devel libxml2-devel libxslt-devel
Install python-tox:
.. code-block:: bash
$ pip install tox
To run the full suite of tests maintained within TackerClient.
.. code-block:: bash
$ tox
.. NOTE::
The first time you run ``tox``, it will take additional time to build
virtualenvs. You can later use the ``-r`` option with ``tox`` to rebuild
your virtualenv in a similar manner.
To run tests for one or more specific test environments(for example, the
most common configuration of Python 2.7, Python 3.5 and PEP-8), list the
environments with the ``-e`` option, separated by spaces:
.. code-block:: bash
$ tox -e py27,py35,pep8
See ``tox.ini`` for the full list of available test environments.
Building the Documentation
==========================
The documentation is generated with Sphinx using the ``tox`` command. To
create HTML docs, run the commands:
.. code-block:: bash
$ tox -e docs
The resultant HTML will be in the ``doc/build/html`` directory.
Release Notes
=============
The release notes for a patch should be included in the patch. See the
`Project Team Guide`_ for more information on using reno in OpenStack.
.. _`Project Team Guide`: http://docs.openstack.org/project-team-guide/release-management.html#managing-release-notes
If any of the following applies to the patch, a release note is required:
* The deployer needs to take an action when upgrading
* The plugin interface changes
* A new feature is implemented
* A command or option is removed
* Current behavior is changed
* A security bug is fixed
Reno is used to generate release notes. Use the commands:
.. code-block:: bash
$ tox -e venv -- reno new <bug-,bp-,whatever>
Then edit the sample file that was created and push it with your change.
To run the commands and see results:
.. code-block:: bash
$ git commit # Commit the change because reno scans git log.
$ tox -e releasenotes
At last, look at the generated release notes
files in ``releasenotes/build/html`` in your browser.
Testing new code
================
If a developer wants to test new code (feature, command or option) that
they have written, Python-TackerClient may be installed from source by running
the following commands in the base directory of the project:
.. code-block:: bash
$ python setup.py install
or
.. code-block:: bash
$ pip install -e .
Standardize Import Format
=========================
.. _`Import Order Guide`: https://docs.openstack.org/hacking/latest/user/hacking.html#imports
The import order shows below:
* {{stdlib imports in human alphabetical order}}
* \n
* {{third-party lib imports in human alphabetical order}}
* \n
* {{project imports in human alphabetical order}}
* \n
* \n
* {{begin your code}}
Example
~~~~~~~
.. code-block:: python
import copy
import fixtures
import mock
import os
from osc_lib.api import auth
from osc_lib import utils
import six
from openstackclient import shell
from openstackclient.tests import utils

View File

@@ -11,7 +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
@@ -28,4 +27,10 @@ In the Contributor Guide, you will find information on tackerclient's
lower level programming details or APIs as well as the transition to
OpenStack client.
#TODO
.. toctree::
:maxdepth: 2
contributing.rst
developing.rst

View File

@@ -19,39 +19,38 @@
''''''' Heading 4
(Avoid deeper levels because they do not render well.)
==================================
python-tackerclient documentation
==================================
=================================
Python-TackerClient Documentation
=================================
This is a client for OpenStack NFV MANO API. It provides
This is a client for OpenStack NFV MANO (Tacker) API. It provides
:doc:`Python API bindings <reference/index>` (the tackerclient module) and
:doc:`command-line interface (CLI) <cli/index>`.
User Documentation
------------------
Contents
--------
.. toctree::
:maxdepth: 2
install/index
cli/index
contributor/index
reference/index
Contributor Guide
-----------------
In the :doc:`Contributor Guide <contributor/index>`, you will find
information on tackerclient's lower level programming details or APIs
as well as the transition to OpenStack client.
Release Notes
-------------
.. toctree::
:maxdepth: 2
:maxdepth: 1
contributor/index
Release Notes <https://docs.openstack.org/releasenotes/python-tackerclient/>
History
-------
Indices and Tables
------------------
Release notes is available at
http://docs.openstack.org/releasenotes/python-tackerclient/.
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

View File

@@ -11,7 +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
@@ -20,19 +19,51 @@
''''''' Heading 4
(Avoid deeper levels because they do not render well.)
=================
Install Guide
=================
============
Installation
============
To install ``python-tackerclient``, it is required to have ``pip``
(in most cases). Make sure that ``pip`` is installed. Then type::
**Note:** The paths we are using for configuration files in these steps
are with reference to Ubuntu Operating System. The paths may vary for
other Operating Systems.
The branch_name which is used in commands, specify the branch_name
as stable/<branch> for any stable branch installation. For eg:
stable/queens, stable/pike. If unspecified the default will be
master branch.
Using python install
====================
1. Clone python-tackerclient repository.
::
$ cd ~/
$ git clone https://github.com/openstack/python-tackerclient -b <branch_name>
2. Install python-tackerclient.
::
$ cd python-tackerclient
$ sudo python setup.py install
Using pip
=========
You can also install the latest version by using ``pip`` command:
::
$ pip install python-tackerclient
Or, if it is needed to install ``python-tackerclient`` from master branch,
type::
type
::
$ pip install git+https://github.com/openstack/python-tackerclient.git
After ``python-tackerclient`` is installed you will see command ``tacker``
in your environment.

View File

@@ -11,7 +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
@@ -20,10 +19,8 @@
''''''' Heading 4
(Avoid deeper levels because they do not render well.)
tackerclient Python API
========================
=========
Reference
=========
Basic Usage
-----------
#TODO
(To be updated)

63
lower-constraints.txt Normal file
View File

@@ -0,0 +1,63 @@
appdirs==1.3.0
Babel==2.3.4
cliff==2.8.0
cmd2==0.8.0
coverage==4.0
ddt==1.0.1
debtcollector==1.2.0
decorator==3.4.0
deprecation==1.0
dogpile.cache==0.6.2
extras==1.0.0
fixtures==3.0.0
flake8==2.5.5
hacking==0.12.0
iso8601==0.1.11
jmespath==0.9.0
jsonpatch==1.16
jsonpointer==1.13
keystoneauth1==3.4.0
linecache2==1.0.0
mccabe==0.2.1
mock==2.0.0
monotonic==0.6
msgpack-python==0.4.0
munch==2.1.0
netaddr==0.7.18
netifaces==0.10.4
openstacksdk==0.11.2
os-client-config==1.28.0
os-service-types==1.2.0
osc-lib==1.8.0
oslo.config==5.2.0
oslo.context==2.19.2
oslo.i18n==3.15.3
oslo.log==3.36.0
oslo.serialization==2.18.0
oslo.utils==3.33.0
pbr==2.0.0
pep8==1.5.7
positional==1.2.1
prettytable==0.7.2
pyflakes==0.8.1
pyinotify==0.9.6
pyparsing==2.1.0
pyperclip==1.5.27
python-dateutil==2.5.3
python-keystoneclient==3.8.0
python-mimeparse==1.6.0
python-subunit==1.0.0
pytz==2013.6
PyYAML==3.12
requests==2.14.2
requests-mock==1.2.0
requestsexceptions==1.2.0
rfc3986==0.3.1
simplejson==3.5.1
six==1.10.0
stestr==2.0.0
stevedore==1.20.0
testtools==2.2.0
traceback2==1.4.0
unittest2==1.1.0
wrapt==1.7.0

View File

@@ -0,0 +1,9 @@
---
features:
- |
Add 'vnf_ids' and 'vnffg_ids' fields in outputs from network
service list command. Users can know which VNFs or VNFFGs,
belongs to specific NS.
Add 'ns_id' field to VNFFG list command, that shows the
network service the current VNFFG belongs to.

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +0,0 @@
---
features:
- |
Add name field of classifiers in classifier list command.

View File

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

View File

@@ -0,0 +1,7 @@
---
deprecations:
- |
tacker CLI is deprecated, will be deleted after Rocky release.
Use `openstack CLI`_ instead.
.. _openstack CLI: https://docs.openstack.org/python-tackerclient/latest/cli/index.html

View File

@@ -1,4 +0,0 @@
---
features:
- |
Support cert_verify key in vim config file.

View File

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

View File

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

View File

@@ -1,4 +0,0 @@
---
features:
- |
add VIM osc commands support.

View File

@@ -1,4 +0,0 @@
---
features:
- |
Support update vnffg template of a given vnffg.

View File

@@ -36,9 +36,8 @@
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = [
'oslosphinx',
'reno.sphinxext',
'openstackdocstheme'
'openstackdocstheme',
]
# Add any paths that contain templates here, relative to this directory.

View File

@@ -7,6 +7,9 @@ Contents:
:maxdepth: 2
unreleased
stein
rocky
queens
pike
ocata
newton

View File

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

View File

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

View File

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

View File

@@ -11,7 +11,6 @@ simplejson>=3.5.1 # MIT
six>=1.10.0 # MIT
stevedore>=1.20.0 # Apache-2.0
Babel!=2.4.0,>=2.3.4 # BSD
oslo.i18n>=3.15.3 # Apache-2.0
osc-lib>=1.8.0 # Apache-2.0
oslo.log>=3.36.0 # Apache-2.0

View File

@@ -4,8 +4,8 @@ summary = CLI and Client Library for OpenStack Tacker
description-file =
README.rst
author = OpenStack
author-email = openstack-dev@lists.openstack.org
home-page = https://docs.openstack.org/tacker/latest
author-email = openstack-discuss@lists.openstack.org
home-page = https://docs.openstack.org/python-tackerclient/
classifier =
Environment :: OpenStack
Intended Audience :: Developers
@@ -17,7 +17,8 @@ classifier =
Programming Language :: Python :: 2
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
Programming Language :: Python :: 3.5
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
[files]
packages =
@@ -40,13 +41,52 @@ openstack.tackerclient.v1 =
vim_set = tackerclient.osc.v1.nfvo.vim:UpdateVIM
vim_delete = tackerclient.osc.v1.nfvo.vim:DeleteVIM
vim_show = tackerclient.osc.v1.nfvo.vim:ShowVIM
vnf_descriptor_create = tackerclient.osc.v1.vnfm.vnfd:CreateVNFD
vnf_descriptor_delete = tackerclient.osc.v1.vnfm.vnfd:DeleteVNFD
vnf_descriptor_list = tackerclient.osc.v1.vnfm.vnfd:ListVNFD
vnf_descriptor_show = tackerclient.osc.v1.vnfm.vnfd:ShowVNFD
vnf_descriptor_template_show = tackerclient.osc.v1.vnfm.vnfd:ShowTemplateVNFD
vnf_create = tackerclient.osc.v1.vnfm.vnf:CreateVNF
vnf_delete = tackerclient.osc.v1.vnfm.vnf:DeleteVNF
vnf_list = tackerclient.osc.v1.vnfm.vnf:ListVNF
vnf_show = tackerclient.osc.v1.vnfm.vnf:ShowVNF
vnf_resource_list = tackerclient.osc.v1.vnfm.vnf:ListVNFResources
vnf_set = tackerclient.osc.v1.vnfm.vnf:UpdateVNF
vnf_scale = tackerclient.osc.v1.vnfm.vnf:ScaleVNF
vnf_graph_descriptor_create = tackerclient.osc.v1.nfvo.vnffgd:CreateVNFFGD
vnf_graph_descriptor_delete = tackerclient.osc.v1.nfvo.vnffgd:DeleteVNFFGD
vnf_graph_descriptor_list = tackerclient.osc.v1.nfvo.vnffgd:ListVNFFGD
vnf_graph_descriptor_show = tackerclient.osc.v1.nfvo.vnffgd:ShowVNFFGD
vnf_graph_descriptor_template_show = tackerclient.osc.v1.nfvo.vnffgd:ShowTemplateVNFFGD
ns_descriptor_create = tackerclient.osc.v1.nfvo.nsd:CreateNSD
ns_descriptor_delete = tackerclient.osc.v1.nfvo.nsd:DeleteNSD
ns_descriptor_list = tackerclient.osc.v1.nfvo.nsd:ListNSD
ns_descriptor_show = tackerclient.osc.v1.nfvo.nsd:ShowNSD
ns_descriptor_template_show = tackerclient.osc.v1.nfvo.nsd:ShowTemplateNSD
nfv_event_show = tackerclient.osc.v1.events.events:ShowEvent
nfv_event_list = tackerclient.osc.v1.events.events:ListEvent
ns_create = tackerclient.osc.v1.nfvo.ns:CreateNS
ns_delete = tackerclient.osc.v1.nfvo.ns:DeleteNS
ns_list = tackerclient.osc.v1.nfvo.ns:ListNS
ns_show = tackerclient.osc.v1.nfvo.ns:ShowNS
vnf_graph_create = tackerclient.osc.v1.nfvo.vnffg:CreateVNFFG
vnf_graph_delete = tackerclient.osc.v1.nfvo.vnffg:DeleteVNFFG
vnf_graph_set = tackerclient.osc.v1.nfvo.vnffg:UpdateVNFFG
vnf_graph_list = tackerclient.osc.v1.nfvo.vnffg:ListVNFFG
vnf_graph_show = tackerclient.osc.v1.nfvo.vnffg:ShowVNFFG
vnf_network_forwarding_path_list = tackerclient.osc.v1.nfvo.vnffg:ListNFP
vnf_network_forwarding_path_show = tackerclient.osc.v1.nfvo.vnffg:ShowNFP
vnf_classifier_list = tackerclient.osc.v1.nfvo.vnffg:ListFC
vnf_classifier_show = tackerclient.osc.v1.nfvo.vnffg:ShowFC
vnf_chain_list = tackerclient.osc.v1.nfvo.vnffg:ListSFC
vnf_chain_show = tackerclient.osc.v1.nfvo.vnffg:ShowSFC
vnf_package_create = tackerclient.osc.v1.vnfpkgm.vnf_package:CreateVnfPackage
vnf_package_list = tackerclient.osc.v1.vnfpkgm.vnf_package:ListVnfPackage
vnf_package_show = tackerclient.osc.v1.vnfpkgm.vnf_package:ShowVnfPackage
vnf_package_upload = tackerclient.osc.v1.vnfpkgm.vnf_package:UploadVnfPackage
vnf_package_delete = tackerclient.osc.v1.vnfpkgm.vnf_package:DeleteVnfPackage
[build_sphinx]
all_files = 1
build-dir = doc/build
source-dir = doc/source
[build_releasenotes]
all_files = 1
build-dir = releasenotes/build

View File

@@ -14,6 +14,8 @@
# under the License.
#
from sys import stderr
from cliff import command
@@ -23,6 +25,8 @@ class OpenStackCommand(command.Command):
api = None
def run(self, parsed_args):
stderr.write("Deprecated: tacker command line is deprecated, "
"will be deleted after Rocky is released.\n")
if not self.api:
return
else:

View File

View File

@@ -0,0 +1,116 @@
# Copyright 2018 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
from osc_lib.command import command
from osc_lib import utils
from tackerclient.i18n import _
from tackerclient.osc import sdk_utils
from tackerclient.osc import utils as tacker_osc_utils
from tackerclient.tacker import v1_0 as tackerV10
_attr_map = (
('id', 'ID', tacker_osc_utils.LIST_BOTH),
('resource_type', 'Resource Type', tacker_osc_utils.LIST_BOTH),
('resource_id', 'Resource ID', tacker_osc_utils.LIST_BOTH),
('resource_state', 'Resource State', tacker_osc_utils.LIST_BOTH),
('event_type', 'Event Type', tacker_osc_utils.LIST_BOTH),
('timestamp', 'Timestamp', tacker_osc_utils.LIST_BOTH),
('event_details', 'Event Details', tacker_osc_utils.LIST_LONG_ONLY),
)
_EVENT = "event"
events_path = '/events'
def _get_columns(item):
column_map = {}
return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map)
class ShowEvent(command.ShowOne):
_description = _("Show event given the event id.")
def get_parser(self, prog_name):
parser = super(ShowEvent, self).get_parser(prog_name)
parser.add_argument(
_EVENT,
metavar="ID",
help=_("ID of event to display")
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
obj_id = tackerV10.find_resourceid_by_name_or_id(
client, _EVENT, parsed_args.event)
obj = client.show_event(obj_id)
display_columns, columns = _get_columns(obj[_EVENT])
data = utils.get_item_properties(
sdk_utils.DictModel(obj[_EVENT]),
columns,)
return (display_columns, data)
class ListEvent(command.Lister):
_description = _("List events of resources.")
def get_parser(self, prog_name):
parser = super(ListEvent, self).get_parser(prog_name)
parser.add_argument(
'--id',
help=_("id of the event to look up."))
parser.add_argument(
'--resource-type',
help=_("resource type of the events to look up."))
parser.add_argument(
'--resource-id',
help=_("resource id of the events to look up."))
parser.add_argument(
'--resource-state',
help=_("resource state of the events to look up."))
parser.add_argument(
'--event-type',
help=_("event type of the events to look up."))
parser.add_argument(
'--long',
action='store_true',
help=_("List additional fields in output"))
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
_params = {}
if parsed_args.id:
_params['id'] = parsed_args.id
if parsed_args.resource_id:
_params['resource_id'] = parsed_args.resource_id
if parsed_args.resource_state:
_params['resource_state'] = parsed_args.resource_id
if parsed_args.event_type:
_params['event_type'] = parsed_args.event_type
if parsed_args.resource_type:
_params['resource_type'] = parsed_args.resource_type
events = client.list('events', events_path, True, **_params)
data = {}
data['events'] = events['events']
headers, columns = tacker_osc_utils.get_column_definitions(
_attr_map, long_listing=parsed_args.long)
return (headers,
(utils.get_dict_properties(
s, columns,
) for s in data[_EVENT + 's']))

View File

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

View File

@@ -0,0 +1,223 @@
# Copyright 2018 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
import yaml
from osc_lib.command import command
from osc_lib import utils
from tackerclient.common import exceptions
from tackerclient.i18n import _
from tackerclient.osc import sdk_utils
from tackerclient.osc import utils as tacker_osc_utils
from tackerclient.tacker import v1_0 as tackerV10
_attr_map = (
('id', 'ID', tacker_osc_utils.LIST_BOTH),
('name', 'Name', tacker_osc_utils.LIST_BOTH),
('template_source', 'Template_Source',
tacker_osc_utils.LIST_BOTH),
('description', 'Description', tacker_osc_utils.LIST_BOTH),
)
_NSD = 'nsd'
_formatters = {
'attributes': tacker_osc_utils.format_dict_with_indention,
}
def _get_columns(item):
column_map = {
'tenant_id': 'project_id',
}
return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map)
class CreateNSD(command.ShowOne):
_description = _("Create a new NSD.")
def get_parser(self, prog_name):
parser = super(CreateNSD, self).get_parser(prog_name)
parser.add_argument(
'name', metavar='NAME',
help=_('Name for NSD'))
parser.add_argument(
'--tenant-id', metavar='TENANT_ID',
help=_('The owner tenant ID or project ID'))
parser.add_argument(
'--nsd-file',
required=True,
help=_('YAML file with NSD parameters'))
parser.add_argument(
'--description',
help=_('Set a description for the NSD'))
return parser
def args2body(self, parsed_args):
body = {_NSD: {}}
nsd = None
if not parsed_args.nsd_file:
raise exceptions.InvalidInput("Invalid input for nsd file")
with open(parsed_args.nsd_file) as f:
nsd = f.read()
try:
nsd = yaml.load(nsd, Loader=yaml.SafeLoader)
except yaml.YAMLError as e:
raise exceptions.InvalidInput(e)
if not nsd:
raise exceptions.InvalidInput("nsd file is empty")
body[_NSD]['attributes'] = {'nsd': nsd}
tackerV10.update_dict(parsed_args, body[_NSD],
['tenant_id', 'name', 'description'])
return body
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
nsd = client.create_nsd(self.args2body(parsed_args))
display_columns, columns = _get_columns(nsd[_NSD])
nsd[_NSD]['attributes']['nsd'] = yaml.load(
nsd[_NSD]['attributes']['nsd'])
data = utils.get_item_properties(
sdk_utils.DictModel(nsd[_NSD]),
columns, formatters=_formatters)
return (display_columns, data)
class DeleteNSD(command.Command):
_description = _("Delete NSD(s).")
def get_parser(self, prog_name):
parser = super(DeleteNSD, self).get_parser(prog_name)
parser.add_argument(
_NSD,
metavar="<NSD>",
nargs="+",
help=_("NSD(s) to delete (name or ID)")
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
failure = False
deleted_ids = []
failed_items = {}
for resource_id in parsed_args.nsd:
try:
obj = tackerV10.find_resourceid_by_name_or_id(
client, _NSD, resource_id)
client.delete_nsd(obj)
deleted_ids.append(resource_id)
except Exception as e:
failure = True
failed_items[resource_id] = e
if failure:
msg = ''
if deleted_ids:
msg = (_('Successfully deleted %(resource)s(s):'
' %(deleted_list)s') % {'deleted_list':
', '.join(deleted_ids),
'resource': _NSD})
err_msg = _("\n\nUnable to delete the below"
" %s(s):") % _NSD
for failed_id, error in failed_items.iteritems():
err_msg += (_('\n Cannot delete %(failed_id)s: %(error)s')
% {'failed_id': failed_id,
'error': error})
msg += err_msg
raise exceptions.CommandError(msg)
else:
print((_('All specified %(resource)s(s) deleted successfully')
% {'resource': _NSD}))
return
class ListNSD(command.Lister):
_description = ("List (NSD)s that belong to a given tenant.")
def get_parser(self, prog_name):
parser = super(ListNSD, self).get_parser(prog_name)
parser.add_argument(
'--template-source',
help=_("List NSD with specified template source. Available \
options are 'onboared' (default), 'inline' or 'all'"),
action='store',
default='onboarded')
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
data = client.list_nsds()
headers, columns = tacker_osc_utils.get_column_definitions(
_attr_map, long_listing=None)
return (headers,
(utils.get_dict_properties(
s, columns,
) for s in data[_NSD + 's']))
class ShowNSD(command.ShowOne):
_description = _("Display NSD details")
def get_parser(self, prog_name):
parser = super(ShowNSD, self).get_parser(prog_name)
parser.add_argument(
_NSD,
metavar="<NSD>",
help=_("NSD to display (name or ID)")
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
obj_id = tackerV10.find_resourceid_by_name_or_id(
client, _NSD, parsed_args.nsd)
obj = client.show_nsd(obj_id)
obj[_NSD]['attributes']['nsd'] = yaml.load(
obj[_NSD]['attributes']['nsd'])
display_columns, columns = _get_columns(obj[_NSD])
data = utils.get_item_properties(
sdk_utils.DictModel(obj[_NSD]),
columns,
formatters=_formatters)
return (display_columns, data)
class ShowTemplateNSD(command.ShowOne):
_description = _("Display NSD Template details")
def get_parser(self, prog_name):
parser = super(ShowTemplateNSD, self).get_parser(prog_name)
parser.add_argument(
_NSD,
metavar="<NSD>",
help=_("NSD to display (name or ID)")
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
obj_id = tackerV10.find_resourceid_by_name_or_id(
client, _NSD, parsed_args.nsd)
obj = client.show_nsd(obj_id)
obj[_NSD]['attributes']['nsd'] = yaml.load(
obj[_NSD]['attributes']['nsd'])
data = utils.get_item_properties(
sdk_utils.DictModel(obj[_NSD]),
(u'attributes',),
formatters=_formatters)
data = (data or _('Unable to display NSD template!'))
return ((u'attributes',), data)

View File

@@ -98,6 +98,9 @@ class CreateVIM(command.ShowOne):
parser.add_argument(
'name', metavar='NAME',
help=_('Set a name for the VIM'))
parser.add_argument(
'--tenant-id', metavar='TENANT_ID',
help=_('The owner tenant ID or project ID'))
parser.add_argument(
'--config-file',
required=True,

View File

@@ -0,0 +1,541 @@
# Copyright 2018 OpenStack Foundation.
# All Rights Reserved
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
import yaml
from osc_lib.command import command
from osc_lib import utils
from tackerclient.common import exceptions
from tackerclient.i18n import _
from tackerclient.osc import sdk_utils
from tackerclient.osc import utils as tacker_osc_utils
from tackerclient.tacker import v1_0 as tackerV10
_VNFFG = 'vnffg' # VNF Forwarding Graph
_NFP = 'nfp' # Network Forwarding Path
_SFC = 'sfc' # Service Function Chain
_FC = 'classifier' # Flow Classifier
nfps_path = '/nfps'
fcs_path = '/classifiers'
sfcs_path = '/sfcs'
DEFAULT_ERROR_REASON_LENGTH = 100
_attr_map_vnffg = (
('id', 'ID', tacker_osc_utils.LIST_BOTH),
('name', 'Name', tacker_osc_utils.LIST_BOTH),
('ns_id', 'NS ID', tacker_osc_utils.LIST_BOTH),
('vnffgd_id', 'VNFFGD ID', tacker_osc_utils.LIST_BOTH),
('status', 'Status', tacker_osc_utils.LIST_BOTH),
('description', 'Description', tacker_osc_utils.LIST_LONG_ONLY),
)
_attr_map_nfp = (
('id', 'ID', tacker_osc_utils.LIST_BOTH),
('name', 'Name', tacker_osc_utils.LIST_BOTH),
('status', 'Status', tacker_osc_utils.LIST_BOTH),
('vnffg_id', 'VNFFG ID', tacker_osc_utils.LIST_BOTH),
('path_id', 'Path ID', tacker_osc_utils.LIST_BOTH),
)
_attr_map_sfc = (
('id', 'ID', tacker_osc_utils.LIST_BOTH),
('status', 'Status', tacker_osc_utils.LIST_BOTH),
('nfp_id', 'NFP ID', tacker_osc_utils.LIST_BOTH),
)
_attr_map_fc = (
('id', 'ID', tacker_osc_utils.LIST_BOTH),
('name', 'Name', tacker_osc_utils.LIST_BOTH),
('status', 'Status', tacker_osc_utils.LIST_BOTH),
('nfp_id', 'NFP ID', tacker_osc_utils.LIST_BOTH),
('chain_id', 'Chain ID', tacker_osc_utils.LIST_BOTH),
)
_formatters = {
'attributes': tacker_osc_utils.format_dict_with_indention,
'match': tacker_osc_utils.format_dict_with_indention,
'chain': tacker_osc_utils.format_dict_with_indention,
}
def _get_columns(item):
column_map = {
'tenant_id': 'project_id',
}
return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map)
class CreateVNFFG(command.ShowOne):
_description = _("Create a new VNFFG.")
def get_parser(self, prog_name):
parser = super(CreateVNFFG, self).get_parser(prog_name)
parser.add_argument(
'name', metavar='NAME',
help=_('Set a name for the VNFFG'))
parser.add_argument(
'--tenant-id', metavar='TENANT_ID',
help=_('The owner tenant ID'))
vnffgd_group = parser.add_mutually_exclusive_group(required=True)
vnffgd_group.add_argument(
'--vnffgd-id',
help=_('VNFFGD ID to use as template to create VNFFG'))
vnffgd_group.add_argument(
'--vnffgd-name',
help=_('VNFFGD Name to use as template to create VNFFG'))
vnffgd_group.add_argument(
'--vnffgd-template',
help=_('VNFFGD file to create VNFFG'))
parser.add_argument(
'--vnf-mapping',
help=_('List of logical VNFD name to VNF instance name mapping. '
'Example: VNF1:my_vnf1,VNF2:my_vnf2'))
parser.add_argument(
'--symmetrical',
action='store_true',
default=False,
help=_('Should a reverse path be created for the NFP '
'(True or False)'))
parser.add_argument(
'--param-file',
help=_('YAML file with specific VNFFG parameters'))
parser.add_argument(
'--description',
help=_('Set a description for the VNFFG'))
return parser
def args2body(self, parsed_args):
body = {_VNFFG: {}}
body[_VNFFG]['attributes'] = {}
client = self.app.client_manager.tackerclient
if parsed_args.vnf_mapping:
_vnf_mapping = dict()
_vnf_mappings = parsed_args.vnf_mapping.split(",")
for mapping in _vnf_mappings:
vnfd_name, vnf = mapping.split(":", 1)
_vnf_mapping[vnfd_name] = \
tackerV10.find_resourceid_by_name_or_id(
client, 'vnf', vnf)
parsed_args.vnf_mapping = _vnf_mapping
if parsed_args.vnffgd_name:
_id = tackerV10.find_resourceid_by_name_or_id(
client, 'vnffgd', parsed_args.vnffgd_name)
parsed_args.vnffgd_id = _id
elif parsed_args.vnffgd_template:
with open(parsed_args.vnffgd_template) as f:
template = f.read()
try:
template = yaml.load(template, Loader=yaml.SafeLoader)
except yaml.YAMLError as e:
raise exceptions.InvalidInput(e)
if not template:
raise exceptions.InvalidInput('The vnffgd file is empty')
body[_VNFFG]['vnffgd_template'] = template
if parsed_args.param_file:
with open(parsed_args.param_file) as f:
param_yaml = f.read()
try:
param_yaml = yaml.load(
param_yaml, Loader=yaml.SafeLoader)
except yaml.YAMLError as e:
raise exceptions.InvalidInput(e)
if not param_yaml:
raise exceptions.InvalidInput('The parameter file is empty')
body[_VNFFG]['attributes'] = {'param_values': param_yaml}
tackerV10.update_dict(parsed_args, body[_VNFFG],
['tenant_id', 'name', 'vnffgd_id',
'symmetrical', 'vnf_mapping', 'description'])
return body
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
vnffg = client.create_vnffg(self.args2body(parsed_args))
display_columns, columns = _get_columns(vnffg[_VNFFG])
data = utils.get_item_properties(
sdk_utils.DictModel(vnffg[_VNFFG]),
columns,
formatters=_formatters)
return (display_columns, data)
class DeleteVNFFG(command.Command):
_description = _("Delete VNFFG(s).")
def get_parser(self, prog_name):
parser = super(DeleteVNFFG, self).get_parser(prog_name)
parser.add_argument(
_VNFFG,
metavar="<VNFFG>",
nargs="+",
help=_("VNFFG(s) to delete (name or ID)")
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
failure = False
deleted_ids = []
failed_items = {}
for resource_id in parsed_args.vnffg:
try:
obj = tackerV10.find_resourceid_by_name_or_id(
client, _VNFFG, resource_id)
client.delete_vnffg(obj)
deleted_ids.append(resource_id)
except Exception as e:
failure = True
failed_items[resource_id] = e
if failure:
msg = ''
if deleted_ids:
msg = (_('Successfully deleted %(resource)s(s):'
' %(deleted_list)s') % {'deleted_list':
', '.join(deleted_ids),
'resource': _VNFFG})
err_msg = _("\n\nUnable to delete the below"
" %s(s):") % _VNFFG
for failed_id, error in failed_items.iteritems():
err_msg += (_('\n Cannot delete %(failed_id)s: %(error)s')
% {'failed_id': failed_id,
'error': error})
msg += err_msg
raise exceptions.CommandError(msg)
else:
print((_('All specified %(resource)s(s) deleted successfully')
% {'resource': _VNFFG}))
return
class UpdateVNFFG(command.ShowOne):
_description = _("Update VNFFG.")
def get_parser(self, prog_name):
parser = super(UpdateVNFFG, self).get_parser(prog_name)
parser.add_argument(
_VNFFG,
metavar="<VNFFG>",
help=_('VNFFG to update (name or ID)'))
parser.add_argument(
'--vnffgd-template',
help=_('VNFFGD file to update VNFFG'))
parser.add_argument(
'--vnf-mapping',
help=_('List of logical VNFD name to VNF instance name mapping. '
'Example: VNF1:my_vnf1,VNF2:my_vnf2'))
parser.add_argument(
'--symmetrical',
action='store_true',
default=False,
help=_('Should a reverse path be created for the NFP'))
parser.add_argument(
'--description',
help=_('Set a description for the VNFFG'))
return parser
def args2body(self, parsed_args):
body = {_VNFFG: {}}
body[_VNFFG]['attributes'] = {}
client = self.app.client_manager.tackerclient
if parsed_args.vnf_mapping:
_vnf_mapping = dict()
_vnf_mappings = parsed_args.vnf_mapping.split(",")
for mapping in _vnf_mappings:
vnfd_name, vnf = mapping.split(":", 1)
_vnf_mapping[vnfd_name] = \
tackerV10.find_resourceid_by_name_or_id(
client, 'vnf', vnf)
parsed_args.vnf_mapping = _vnf_mapping
if parsed_args.vnffgd_template:
with open(parsed_args.vnffgd_template) as f:
template = f.read()
try:
template = yaml.load(
template, Loader=yaml.SafeLoader)
except yaml.YAMLError as e:
raise exceptions.InvalidInput(e)
if not template:
raise exceptions.InvalidInput('The vnffgd file is empty')
body[_VNFFG]['vnffgd_template'] = template
if parsed_args.param_file:
with open(parsed_args.param_file) as f:
param_yaml = f.read()
try:
param_yaml = yaml.load(
param_yaml, Loader=yaml.SafeLoader)
except yaml.YAMLError as e:
raise exceptions.InvalidInput(e)
if not param_yaml:
raise exceptions.InvalidInput('The parameter file is empty')
body[_VNFFG]['attributes'] = {'param_values': param_yaml}
tackerV10.update_dict(parsed_args, body[self.resource],
['vnf_mapping', 'symmetrical', 'description'])
return body
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
obj_id = tackerV10.find_resourceid_by_name_or_id(
client, _VNFFG, parsed_args.vnffg)
vnffg = client.update_vnffg(obj_id, self.args2body(parsed_args))
display_columns, columns = _get_columns(vnffg[_VNFFG])
data = utils.get_item_properties(
sdk_utils.DictModel(vnffg[_VNFFG]),
columns,
formatters=_formatters)
return (display_columns, data)
class ListVNFFG(command.Lister):
_description = ("List VNFFG(s) that belong to a given tenant.")
def get_parser(self, prog_name):
parser = super(ListVNFFG, self).get_parser(prog_name)
parser.add_argument(
'--long',
action='store_true',
help=_('List additional fields in output')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
data = client.list_vnffgs()
headers, columns = tacker_osc_utils.get_column_definitions(
_attr_map_vnffg, long_listing=parsed_args.long)
return (headers,
(utils.get_dict_properties(
s, columns,
) for s in data[_VNFFG + 's']))
class ShowVNFFG(command.ShowOne):
_description = _("Display VNFFG details")
def get_parser(self, prog_name):
parser = super(ShowVNFFG, self).get_parser(prog_name)
parser.add_argument(
_VNFFG,
metavar="<VNFFG>",
help=_('VNFFG to display (name or ID)')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
obj_id = tackerV10.find_resourceid_by_name_or_id(
client, _VNFFG, parsed_args.vnffg)
obj = client.show_vnffg(obj_id)
display_columns, columns = _get_columns(obj[_VNFFG])
data = utils.get_item_properties(
sdk_utils.DictModel(obj[_VNFFG]),
columns,
formatters=_formatters)
return (display_columns, data)
class ListNFP(command.Lister):
_description = ("List NFP(s) that belong to a given tenant.")
def get_parser(self, prog_name):
parser = super(ListNFP, self).get_parser(prog_name)
parser.add_argument(
'--vnffg-id',
help=_('List NFP(s) with specific VNFFG ID'))
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
_params = {}
if parsed_args.vnffg_id:
_params['vnffg_id'] = parsed_args.vnffg_id
nfps = client.list('nfps', nfps_path, True, **_params)
for nfp in nfps['nfps']:
error_reason = nfp.get('error_reason', None)
if error_reason and \
len(error_reason) > DEFAULT_ERROR_REASON_LENGTH:
nfp['error_reason'] = error_reason[
:DEFAULT_ERROR_REASON_LENGTH]
nfp['error_reason'] += '...'
data = {}
data['nfps'] = nfps['nfps']
data = client.list_nfps()
headers, columns = tacker_osc_utils.get_column_definitions(
_attr_map_nfp, long_listing=None)
return (headers,
(utils.get_dict_properties(
s, columns,
) for s in data[_NFP + 's']))
class ShowNFP(command.ShowOne):
_description = _("Display NFP details")
def get_parser(self, prog_name):
parser = super(ShowNFP, self).get_parser(prog_name)
parser.add_argument(
_NFP,
metavar="<NFP>",
help=_('NFP to display (name or ID)')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
obj_id = tackerV10.find_resourceid_by_name_or_id(
client, _NFP, parsed_args.nfp)
obj = client.show_nfp(obj_id)
display_columns, columns = _get_columns(obj[_NFP])
data = utils.get_item_properties(
sdk_utils.DictModel(obj[_NFP]),
columns)
return (display_columns, data)
class ListFC(command.Lister):
_description = ("List flow classifier(s) that belong to a given tenant.")
def get_parser(self, prog_name):
parser = super(ListFC, self).get_parser(prog_name)
parser.add_argument(
'--nfp-id',
help=_('List flow classifier(s) with specific nfp id'))
parser.add_argument(
'--tenant-id', metavar='TENANT_ID',
help=_('The owner tenant ID or project ID'))
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
_params = {}
if parsed_args.nfp_id:
_params['nfp_id'] = parsed_args.nfp_id
if parsed_args.tenant_id:
_params['tenant_id'] = parsed_args.tenant_id
classifiers = client.list('classifiers', fcs_path, True,
**_params)
for classifier in classifiers['classifiers']:
error_reason = classifier.get('error_reason', None)
if error_reason and \
len(error_reason) > DEFAULT_ERROR_REASON_LENGTH:
classifier['error_reason'] = error_reason[
:DEFAULT_ERROR_REASON_LENGTH]
classifier['error_reason'] += '...'
data = {}
data['classifiers'] = classifiers['classifiers']
data = client.list_classifiers()
headers, columns = tacker_osc_utils.get_column_definitions(
_attr_map_fc, long_listing=None)
return (headers,
(utils.get_dict_properties(
s, columns,
) for s in data[_FC + 's']))
class ShowFC(command.ShowOne):
_description = _("Display flow classifier details")
def get_parser(self, prog_name):
parser = super(ShowFC, self).get_parser(prog_name)
parser.add_argument(
_FC,
metavar="<Classifier ID>",
help=_('Flow Classifier to display (name or ID)')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
obj_id = tackerV10.find_resourceid_by_name_or_id(
client, _FC, parsed_args.classifier)
obj = client.show_classifier(obj_id)
display_columns, columns = _get_columns(obj[_FC])
data = utils.get_item_properties(
sdk_utils.DictModel(obj[_FC]),
columns,
formatters=_formatters)
return (display_columns, data)
class ListSFC(command.Lister):
_description = ("List SFC(s) that belong to a given tenant.")
def get_parser(self, prog_name):
parser = super(ListSFC, self).get_parser(prog_name)
parser.add_argument(
'--nfp-id',
help=_('List SFC(s) with specific nfp id'))
parser.add_argument(
'--tenant-id', metavar='TENANT_ID',
help=_('The owner tenant ID or project ID'))
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
_params = {}
if parsed_args.nfp_id:
_params['nfp_id'] = parsed_args.nfp_id
if parsed_args.tenant_id:
_params['tenant_id'] = parsed_args.tenant_id
sfcs = client.list('sfcs', sfcs_path, True, **_params)
for chain in sfcs['sfcs']:
error_reason = chain.get('error_reason', None)
if error_reason and \
len(error_reason) > DEFAULT_ERROR_REASON_LENGTH:
chain['error_reason'] = error_reason[
:DEFAULT_ERROR_REASON_LENGTH]
chain['error_reason'] += '...'
data = {}
data['sfcs'] = sfcs['sfcs']
headers, columns = tacker_osc_utils.get_column_definitions(
_attr_map_sfc, long_listing=None)
return (headers,
(utils.get_dict_properties(
s, columns,
) for s in data[_SFC + 's']))
class ShowSFC(command.ShowOne):
_description = _("Display SFC details")
def get_parser(self, prog_name):
parser = super(ShowSFC, self).get_parser(prog_name)
parser.add_argument(
_SFC,
metavar="<SFC>",
help=_('SFC to display (name or ID)')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
obj_id = tackerV10.find_resourceid_by_name_or_id(
client, _SFC, parsed_args.sfc)
obj = client.show_sfc(obj_id)
display_columns, columns = _get_columns(obj[_SFC])
data = utils.get_item_properties(
sdk_utils.DictModel(obj[_SFC]),
columns,
formatters=_formatters)
return (display_columns, data)

View File

@@ -0,0 +1,216 @@
# Copyright 2018 OpenStack Foundation.
# All Rights Reserved
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
import yaml
from osc_lib.command import command
from osc_lib import utils
from tackerclient.common import exceptions
from tackerclient.i18n import _
from tackerclient.osc import sdk_utils
from tackerclient.osc import utils as tacker_osc_utils
from tackerclient.tacker import v1_0 as tackerV10
_attr_map = (
('id', 'ID', tacker_osc_utils.LIST_BOTH),
('name', 'Name', tacker_osc_utils.LIST_BOTH),
('template_source', 'Template_Source',
tacker_osc_utils.LIST_BOTH),
('description', 'Description', tacker_osc_utils.LIST_BOTH),
)
_VNFFGD = "vnffgd"
_formatters = {
'template': tacker_osc_utils.format_dict_with_indention,
}
def _get_columns(item):
column_map = {
'tenant_id': 'project_id',
}
return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map)
class CreateVNFFGD(command.ShowOne):
_description = _("Create a new VNFFGD")
def get_parser(self, prog_name):
parser = super(CreateVNFFGD, self).get_parser(prog_name)
parser.add_argument(
'name', metavar='NAME',
help=_('Name for VNFFGD'))
parser.add_argument(
'--tenant-id', metavar='TENANT_ID',
help=_('The owner tenant ID or project ID'))
parser.add_argument(
'--vnffgd-file',
required=True,
help=_('YAML file with VNFFGD parameters'))
parser.add_argument(
'--description',
help=_('Set a description for the VNFFGD'))
return parser
def args2body(self, parsed_args):
body = {_VNFFGD: {}}
vnffgd = None
if not parsed_args.vnffgd_file:
raise exceptions.InvalidInput("Invalid input for vnffgd file")
with open(parsed_args.vnffgd_file) as f:
vnffgd = f.read()
try:
vnffgd = yaml.load(vnffgd, Loader=yaml.SafeLoader)
except yaml.YAMLError as e:
raise exceptions.InvalidInput(e)
if not vnffgd:
raise exceptions.InvalidInput("vnffgd file is empty")
body[_VNFFGD]['template'] = {'vnffgd': vnffgd}
tackerV10.update_dict(parsed_args, body[_VNFFGD],
['tenant_id', 'name', 'description'])
return body
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
vnffgd = client.create_vnffgd(self.args2body(parsed_args))
display_columns, columns = _get_columns(vnffgd[_VNFFGD])
data = utils.get_item_properties(
sdk_utils.DictModel(vnffgd[_VNFFGD]),
columns, formatters=_formatters)
return (display_columns, data)
class DeleteVNFFGD(command.Command):
_description = _("Delete VNFFGD(s).")
def get_parser(self, prog_name):
parser = super(DeleteVNFFGD, self).get_parser(prog_name)
parser.add_argument(
_VNFFGD,
metavar="<VNFFGD>",
nargs="+",
help=_("VNFFGD(s) to delete (name or ID)")
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
failure = False
deleted_ids = []
failed_items = {}
for resource_id in parsed_args.vnffgd:
try:
obj = tackerV10.find_resourceid_by_name_or_id(
client, _VNFFGD, resource_id)
client.delete_vnffgd(obj)
deleted_ids.append(resource_id)
except Exception as e:
failure = True
failed_items[resource_id] = e
if failure:
msg = ''
if deleted_ids:
msg = (_('Successfully deleted %(resource)s(s):'
' %(deleted_list)s') % {'deleted_list':
', '.join(deleted_ids),
'resource': _VNFFGD})
err_msg = _("\n\nUnable to delete the below"
" %s(s):") % _VNFFGD
for failed_id, error in failed_items.iteritems():
err_msg += (_('\n Cannot delete %(failed_id)s: %(error)s')
% {'failed_id': failed_id,
'error': error})
msg += err_msg
raise exceptions.CommandError(msg)
else:
print((_('All specified %(resource)s(s) deleted successfully')
% {'resource': _VNFFGD}))
return
class ListVNFFGD(command.Lister):
_description = ("List (VNFFGD)s that belong to a given tenant.")
def get_parser(self, prog_name):
parser = super(ListVNFFGD, self).get_parser(prog_name)
parser.add_argument(
'--template-source',
help=_("List VNFFGD with specified template source. Available \
options are 'onboarded' (default), 'inline' or 'all'"),
action='store',
default='onboarded')
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
data = client.list_vnffgds()
headers, columns = tacker_osc_utils.get_column_definitions(
_attr_map, long_listing=None)
return (headers,
(utils.get_dict_properties(
s, columns,
) for s in data[_VNFFGD + 's']))
class ShowVNFFGD(command.ShowOne):
_description = _("Display VNFFGD details")
def get_parser(self, prog_name):
parser = super(ShowVNFFGD, self).get_parser(prog_name)
parser.add_argument(
_VNFFGD,
metavar="<VNFFGD>",
help=_("VNFFGD to display (name or ID)")
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
obj_id = tackerV10.find_resourceid_by_name_or_id(
client, _VNFFGD, parsed_args.vnffgd)
obj = client.show_vnffgd(obj_id)
display_columns, columns = _get_columns(obj[_VNFFGD])
data = utils.get_item_properties(
sdk_utils.DictModel(obj[_VNFFGD]),
columns, formatters=_formatters)
return (display_columns, data)
class ShowTemplateVNFFGD(command.ShowOne):
_description = _("Display VNFFGD Template details")
def get_parser(self, prog_name):
parser = super(ShowTemplateVNFFGD, self).get_parser(prog_name)
parser.add_argument(
_VNFFGD,
metavar="<VNFFGD>",
help=_("VNFFGD to display (name or ID)")
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
obj_id = tackerV10.find_resourceid_by_name_or_id(
client, _VNFFGD, parsed_args.vnffgd)
obj = client.show_vnffgd(obj_id)
data = utils.get_item_properties(
sdk_utils.DictModel(obj[_VNFFGD]),
(u'template',),
formatters=_formatters)
data = (data or _('Unable to display VNFFGD template!'))
return ((u'template',), data)

View File

View File

@@ -0,0 +1,442 @@
# Copyright 2018 OpenStack Foundation.
# All Rights Reserved
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
import yaml
from osc_lib.command import command
from osc_lib import utils
from tackerclient.common import exceptions
from tackerclient.i18n import _
from tackerclient.osc import sdk_utils
from tackerclient.osc import utils as tacker_osc_utils
from tackerclient.tacker import v1_0 as tackerV10
_attr_map = (
('id', 'ID', tacker_osc_utils.LIST_BOTH),
('name', 'Name', tacker_osc_utils.LIST_BOTH),
('mgmt_ip_address', 'Mgmt Ip Address',
tacker_osc_utils.LIST_BOTH),
('status', 'Status', tacker_osc_utils.LIST_BOTH),
('vim_id', 'VIM ID', tacker_osc_utils.LIST_BOTH),
('vnfd_id', 'VNFD ID', tacker_osc_utils.LIST_BOTH),
('tenant_id', 'Project ID', tacker_osc_utils.LIST_LONG_ONLY),
)
_attr_map_rsc = (
('id', 'ID', tacker_osc_utils.LIST_BOTH),
('name', 'Name', tacker_osc_utils.LIST_BOTH),
('type', 'Type', tacker_osc_utils.LIST_BOTH),
)
_VNF = "vnf"
def _get_columns(item):
column_map = {
'tenant_id': 'project_id',
}
return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map)
def _break_string(vnf_monitoring_policy):
count_space = 0
monitoring_policy = "\n"
for i in range(0, len(vnf_monitoring_policy)):
monitoring_policy += vnf_monitoring_policy[i]
if vnf_monitoring_policy[i] == ' ':
count_space += 1
if count_space == 9:
monitoring_policy += "\n"
count_space = 0
return monitoring_policy
class CreateVNF(command.ShowOne):
_description = _("Create a new VNF")
def get_parser(self, prog_name):
parser = super(CreateVNF, self).get_parser(prog_name)
parser.add_argument(
'name', metavar='NAME',
help=_('Set a name for the VNF'))
parser.add_argument(
'--tenant-id', metavar='TENANT_ID',
help=_('The owner tenant ID or project ID'))
vnfd_group = parser.add_mutually_exclusive_group(required=True)
vnfd_group.add_argument(
'--vnfd-id',
help=_('VNFD ID to use as template to create VNF'))
vnfd_group.add_argument(
'--vnfd-name',
help=_('VNFD Name to use as template to create VNF'))
vnfd_group.add_argument(
'--vnfd-template',
help=_("VNFD file to create VNF"))
vim_group = parser.add_mutually_exclusive_group()
vim_group.add_argument(
'--vim-id',
help=_('VIM ID to deploy VNF on specified VIM'))
vim_group.add_argument(
'--vim-name',
help=_('VIM name to deploy VNF on specified VIM'))
parser.add_argument(
'--vim-region-name',
help=_('VIM Region to deploy VNF on specified VIM'))
parser.add_argument(
'--config-file',
help=_('YAML file with VNF configuration'))
parser.add_argument(
'--param-file',
help=_('Specify parameter yaml file'))
parser.add_argument(
'--description',
help=_('Set description for the VNF'))
return parser
def args2body(self, parsed_args):
body = {_VNF: {}}
body[_VNF]['attributes'] = {}
config = None
if parsed_args.config_file:
with open(parsed_args.config_file) as f:
config_yaml = f.read()
try:
config = yaml.load(
config_yaml, Loader=yaml.SafeLoader)
except yaml.YAMLError as e:
raise exceptions.InvalidInput(e)
if config:
body[_VNF]['attributes'] = {'config': config}
if parsed_args.vim_region_name:
body[_VNF].setdefault('placement_attr', {})['region_name'] = \
parsed_args.vim_region_name
client = self.app.client_manager.tackerclient
if parsed_args.vim_name:
_id = tackerV10.find_resourceid_by_name_or_id(client, 'vim',
parsed_args.vim_name)
parsed_args.vim_id = _id
if parsed_args.vnfd_name:
_id = tackerV10.find_resourceid_by_name_or_id(
client, 'vnfd',
parsed_args.vnfd_name
)
parsed_args.vnfd_id = _id
elif parsed_args.vnfd_template:
with open(parsed_args.vnfd_template) as f:
template = f.read()
try:
template = yaml.load(
template, Loader=yaml.SafeLoader)
except yaml.YAMLError as e:
raise exceptions.InvalidInput(e)
if not template:
raise exceptions.InvalidInput('The vnfd file is empty')
body[_VNF]['vnfd_template'] = template
if parsed_args.param_file:
with open(parsed_args.param_file) as f:
param_yaml = f.read()
try:
param_yaml = yaml.load(
param_yaml, Loader=yaml.SafeLoader)
except yaml.YAMLError as e:
raise exceptions.InvalidInput(e)
if not param_yaml:
raise exceptions.InvalidInput('The parameter file is empty')
body[_VNF]['attributes'] = {'param_values': param_yaml}
tackerV10.update_dict(parsed_args, body[_VNF],
['tenant_id', 'name', 'description',
'vnfd_id', 'vim_id'])
return body
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
vnf = client.create_vnf(self.args2body(parsed_args))
display_columns, columns = _get_columns(vnf[_VNF])
if vnf[_VNF]['attributes'].get('monitoring_policy'):
vnf[_VNF]['attributes']['monitoring_policy'] =\
_break_string(vnf[_VNF]['attributes']['monitoring_policy'])
data = utils.get_item_properties(
sdk_utils.DictModel(vnf[_VNF]),
columns)
return (display_columns, data)
class DeleteVNF(command.Command):
_description = _("Delete VNF(s).")
def get_parser(self, prog_name):
parser = super(DeleteVNF, self).get_parser(prog_name)
parser.add_argument(
_VNF,
metavar="<VNF>",
nargs="+",
help=_("VNF(s) to delete (name or ID)"))
parser.add_argument(
'--force',
default=False,
action='store_true',
help=_('Force delete VNF instance'))
return parser
def args2body(self, parsed_args):
body = dict()
if parsed_args.force:
body[_VNF] = dict()
body[_VNF]['attributes'] = dict()
body[_VNF]['attributes']['force'] = True
return body
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
failure = False
deleted_ids = []
failed_items = {}
body = self.args2body(parsed_args)
for resource_id in parsed_args.vnf:
try:
obj = tackerV10.find_resourceid_by_name_or_id(
client, _VNF, resource_id)
client.delete_vnf(obj, body)
deleted_ids.append(resource_id)
except Exception as e:
failure = True
failed_items[resource_id] = e
if failure:
msg = ''
if deleted_ids:
msg = (_('Successfully deleted %(resource)s(s):'
' %(deleted_list)s') % {'deleted_list':
', '.join(deleted_ids),
'resource': _VNF})
err_msg = _("\n\nUnable to delete the below"
" %s(s):") % _VNF
for failed_id, error in failed_items.iteritems():
err_msg += (_('\n Cannot delete %(failed_id)s: %(error)s')
% {'failed_id': failed_id,
'error': error})
msg += err_msg
raise exceptions.CommandError(msg)
else:
print((_('All specified %(resource)s(s) deleted successfully')
% {'resource': _VNF}))
return
class ListVNF(command.Lister):
_description = _("List VNF(s) that belong to a given tenant.")
def get_parser(self, prog_name):
parser = super(ListVNF, self).get_parser(prog_name)
parser.add_argument(
'--template-source',
help=_("List VNF with specified template source. Available \
options are 'onboarded' (default), 'inline' or 'all'"),
action='store',
default='onboarded')
vim_group = parser.add_mutually_exclusive_group()
vim_group.add_argument(
'--vim-id',
help=_('List VNF(s) that belong to a given VIM ID'))
vim_group.add_argument(
'--vim-name',
help=_('List VNF(s) that belong to a given VIM Name'))
vnfd_group = parser.add_mutually_exclusive_group()
vnfd_group.add_argument(
'--vnfd-id',
help=_('List VNF(s) that belong to a given VNFD ID'))
vnfd_group.add_argument(
'--vnfd-name',
help=_('List VNF(s) that belong to a given VNFD Name'))
parser.add_argument(
'--tenant-id', metavar='TENANT_ID',
help=_('The owner tenant ID or project ID'))
parser.add_argument(
'--long',
action='store_true',
help=_('List additional fields in output'))
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
_params = {}
if parsed_args.vim_id:
_params['vim_id'] = parsed_args.vim_id
if parsed_args.vim_name:
vim_id = tackerV10.find_resourceid_by_name_or_id(
client, 'vim', parsed_args.vim_name
)
_params['vim_id'] = vim_id
if parsed_args.vnfd_id:
_params['vnfd_id'] = parsed_args.vnfd_id
if parsed_args.vnfd_name:
vim_id = tackerV10.find_resourceid_by_name_or_id(
client, 'vnfd', parsed_args.vnfd_name
)
_params['vnfd_id'] = vim_id
if parsed_args.tenant_id:
_params['tenant_id'] = parsed_args.tenant_id
data = client.list_vnfs(**_params)
headers, columns = tacker_osc_utils.get_column_definitions(
_attr_map, long_listing=parsed_args.long)
return (headers,
(utils.get_dict_properties(
s, columns,
) for s in data[_VNF + 's']))
class ShowVNF(command.ShowOne):
_description = _("Display VNF details")
def get_parser(self, prog_name):
parser = super(ShowVNF, self).get_parser(prog_name)
parser.add_argument(
_VNF,
metavar="<VNF>",
help=_("VNF to display (name or ID)"))
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
obj_id = tackerV10.find_resourceid_by_name_or_id(
client, _VNF, parsed_args.vnf)
obj = client.show_vnf(obj_id)
if obj[_VNF]['attributes'].get('monitoring_policy'):
obj[_VNF]['attributes']['monitoring_policy'] =\
_break_string(obj[_VNF]['attributes']['monitoring_policy'])
display_columns, columns = _get_columns(obj[_VNF])
data = utils.get_item_properties(
sdk_utils.DictModel(obj[_VNF]),
columns)
return (display_columns, data)
class ListVNFResources(command.Lister):
_description = _("List resources of a VNF like VDU, CP, etc.")
def get_parser(self, prog_name):
parser = super(ListVNFResources, self).get_parser(prog_name)
parser.add_argument(
_VNF,
metavar="<VNF>",
help=_("VNF to display (name or ID)"))
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
obj_id = tackerV10.find_resourceid_by_name_or_id(
client, _VNF, parsed_args.vnf)
data = client.list_vnf_resources(obj_id)
headers, columns = tacker_osc_utils.get_column_definitions(
_attr_map_rsc, long_listing=None)
return (headers,
(utils.get_dict_properties(
s, columns,
) for s in data['resources']))
class UpdateVNF(command.ShowOne):
_description = _("Update a given VNF.")
def get_parser(self, prog_name):
parser = super(UpdateVNF, self).get_parser(prog_name)
config_group = parser.add_mutually_exclusive_group(required=True)
config_group.add_argument(
'--config-file',
help=_('YAML file with VNF configuration'))
config_group.add_argument(
'--config',
help=_('Specify config YAML data'))
parser.add_argument(
_VNF,
metavar="<VNF>",
help=_("VNF to update (name or ID)"))
return parser
def args2body(self, parsed_args):
body = {_VNF: {}}
body[_VNF]['attributes'] = {}
config = None
if parsed_args.config_file:
with open(parsed_args.config_file) as f:
config_yaml = f.read()
try:
config = yaml.load(config_yaml, Loader=yaml.SafeLoader)
except yaml.YAMLError as e:
raise exceptions.InvalidInput(e)
if parsed_args.config:
config = parsed_args.config
if isinstance(config, str) or isinstance(config, unicode):
config_str = parsed_args.config.decode('unicode_escape')
try:
config = yaml.load(config_str, Loader=yaml.SafeLoader)
except yaml.YAMLError as e:
raise exceptions.InvalidInput(e)
if config:
body[_VNF]['attributes'] = {'config': config}
tackerV10.update_dict(parsed_args, body[_VNF], ['tenant_id'])
return body
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
obj_id = tackerV10.find_resourceid_by_name_or_id(
client, _VNF, parsed_args.vnf)
vnf = client.update_vnf(obj_id, self.args2body(parsed_args))
if vnf[_VNF]['attributes'].get('monitoring_policy'):
vnf[_VNF]['attributes']['monitoring_policy'] =\
_break_string(vnf[_VNF]['attributes']['monitoring_policy'])
display_columns, columns = _get_columns(vnf[_VNF])
data = utils.get_item_properties(
sdk_utils.DictModel(vnf[_VNF]),
columns)
return (display_columns, data)
class ScaleVNF(command.Command):
_description = _("Scale a VNF.")
def get_parser(self, prog_name):
parser = super(ScaleVNF, self).get_parser(prog_name)
parser.add_argument(
'--scaling-policy-name',
help=_('VNF policy name used to scale'))
parser.add_argument(
'--scaling-type',
help=_('VNF scaling type, it could be either "out" or "in"'))
parser.add_argument(
_VNF,
metavar="<VNF>",
help=_("VNF to scale (name or ID)"))
return parser
def args2body(self, parsed_args):
args = {}
body = {"scale": args}
args['type'] = parsed_args.scaling_type
args['policy'] = parsed_args.scaling_policy_name
return body
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
obj_id = tackerV10.find_resourceid_by_name_or_id(
client, _VNF, parsed_args.vnf)
client.scale_vnf(obj_id, self.args2body(parsed_args))
return

View File

@@ -0,0 +1,224 @@
# Copyright 2016 NEC Corporation.
# All Rights Reserved
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
import yaml
from osc_lib.command import command
from osc_lib import utils
from tackerclient.common import exceptions
from tackerclient.i18n import _
from tackerclient.osc import sdk_utils
from tackerclient.osc import utils as tacker_osc_utils
from tackerclient.tacker import v1_0 as tackerV10
_attr_map = (
('id', 'ID', tacker_osc_utils.LIST_BOTH),
('name', 'Name', tacker_osc_utils.LIST_BOTH),
('template_source', 'Template_Source',
tacker_osc_utils.LIST_BOTH),
('description', 'Description', tacker_osc_utils.LIST_BOTH),
)
_VNFD = "vnfd"
_formatters = {
'attributes': tacker_osc_utils.format_dict_with_indention,
}
def _get_columns(item):
column_map = {
'tenant_id': 'project_id',
}
return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map)
class CreateVNFD(command.ShowOne):
_description = _("Create a new VNFD")
def get_parser(self, prog_name):
parser = super(CreateVNFD, self).get_parser(prog_name)
parser.add_argument(
'name', metavar='NAME',
help=_('Name for VNFD'))
parser.add_argument(
'--tenant-id', metavar='TENANT_ID',
help=_('The owner tenant ID or project ID'))
parser.add_argument(
'--vnfd-file',
required=True,
help=_('YAML file with VNFD parameters'))
parser.add_argument(
'--description',
help=_('Set a description for the VNFD'))
return parser
def args2body(self, parsed_args):
body = {_VNFD: {}}
vnfd = None
if not parsed_args.vnfd_file:
raise exceptions.InvalidInput("Invalid input for vnfd file")
with open(parsed_args.vnfd_file) as f:
vnfd = f.read()
try:
vnfd = yaml.load(vnfd, Loader=yaml.SafeLoader)
except yaml.YAMLError as e:
msg = _("yaml failed to load vnfd file. %s") % e
raise exceptions.InvalidInput(msg)
if not vnfd:
raise exceptions.InvalidInput("vnfd file is empty")
body[_VNFD]['attributes'] = {'vnfd': vnfd}
tackerV10.update_dict(parsed_args, body[_VNFD],
['tenant_id', 'name', 'description'])
return body
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
vnfd = client.create_vnfd(self.args2body(parsed_args))
display_columns, columns = _get_columns(vnfd[_VNFD])
vnfd[_VNFD]['attributes']['vnfd'] = yaml.load(
vnfd[_VNFD]['attributes']['vnfd'])
data = utils.get_item_properties(
sdk_utils.DictModel(vnfd[_VNFD]),
columns, formatters=_formatters)
return (display_columns, data)
class DeleteVNFD(command.Command):
_description = _("Delete VNFD(s).")
def get_parser(self, prog_name):
parser = super(DeleteVNFD, self).get_parser(prog_name)
parser.add_argument(
_VNFD,
metavar="<VNFD>",
nargs="+",
help=_("VNFD(s) to delete (name or ID)")
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
failure = False
deleted_ids = []
failed_items = {}
for resource_id in parsed_args.vnfd:
try:
obj = tackerV10.find_resourceid_by_name_or_id(
client, _VNFD, resource_id)
client.delete_vnfd(obj)
deleted_ids.append(resource_id)
except Exception as e:
failure = True
failed_items[resource_id] = e
if failure:
msg = ''
if deleted_ids:
msg = (_('Successfully deleted %(resource)s(s):'
' %(deleted_list)s') % {'deleted_list':
', '.join(deleted_ids),
'resource': _VNFD})
err_msg = _("\n\nUnable to delete the below"
" %s(s):") % _VNFD
for failed_id, error in failed_items.iteritems():
err_msg += (_('\n Cannot delete %(failed_id)s: %(error)s')
% {'failed_id': failed_id,
'error': error})
msg += err_msg
raise exceptions.CommandError(msg)
else:
print((_('All specified %(resource)s(s) deleted successfully')
% {'resource': _VNFD}))
return
class ListVNFD(command.Lister):
_description = ("List (VNFD)s that belong to a given tenant.")
def get_parser(self, prog_name):
parser = super(ListVNFD, self).get_parser(prog_name)
parser.add_argument(
'--template-source',
help=_("List VNFD with specified template source. Available \
options are 'onboarded' (default), 'inline' or 'all'"),
action='store',
default='onboarded')
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
data = client.list_vnfds()
headers, columns = tacker_osc_utils.get_column_definitions(
_attr_map, long_listing=None)
return (headers,
(utils.get_dict_properties(
s, columns,
) for s in data[_VNFD + 's']))
class ShowVNFD(command.ShowOne):
_description = _("Display VNFD details")
def get_parser(self, prog_name):
parser = super(ShowVNFD, self).get_parser(prog_name)
parser.add_argument(
_VNFD,
metavar="<VNFD>",
help=_("VNFD to display (name or ID)")
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
obj_id = tackerV10.find_resourceid_by_name_or_id(
client, _VNFD, parsed_args.vnfd)
obj = client.show_vnfd(obj_id)
obj[_VNFD]['attributes']['vnfd'] = yaml.load(
obj[_VNFD]['attributes']['vnfd'])
display_columns, columns = _get_columns(obj[_VNFD])
data = utils.get_item_properties(
sdk_utils.DictModel(obj[_VNFD]),
columns,
formatters=_formatters)
return (display_columns, data)
class ShowTemplateVNFD(command.ShowOne):
_description = _("Display VNFD Template details")
def get_parser(self, prog_name):
parser = super(ShowTemplateVNFD, self).get_parser(prog_name)
parser.add_argument(
_VNFD,
metavar="<VNFD>",
help=_("VNFD to display (name or ID)")
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
obj_id = tackerV10.find_resourceid_by_name_or_id(
client, _VNFD, parsed_args.vnfd)
obj = client.show_vnfd(obj_id)
obj[_VNFD]['attributes']['vnfd'] = yaml.load(
obj[_VNFD]['attributes']['vnfd'])
data = utils.get_item_properties(
sdk_utils.DictModel(obj[_VNFD]),
(u'attributes',),
formatters=_formatters)
data = (data or _('Unable to display VNFD template!'))
return ((u'attributes',), data)

View File

View File

@@ -0,0 +1,258 @@
# Copyright (C) 2019 NTT DATA
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import logging
from osc_lib.cli import parseractions
from osc_lib.command import command
from osc_lib import utils
from tackerclient.common import exceptions
from tackerclient.i18n import _
from tackerclient.osc import sdk_utils
from tackerclient.osc import utils as tacker_osc_utils
LOG = logging.getLogger(__name__)
_attr_map = (
('id', 'ID', tacker_osc_utils.LIST_BOTH),
('vnfProductName', 'VNF Product Name', tacker_osc_utils.LIST_BOTH),
('onboardingState', 'Onboarding State', tacker_osc_utils.LIST_BOTH),
('usageState', 'Usage State', tacker_osc_utils.LIST_BOTH),
('operationalState', 'Operational State', tacker_osc_utils.LIST_BOTH),
('userDefinedData', 'User Defined Data', tacker_osc_utils.LIST_BOTH)
)
_mixed_case_fields = ('usageState', 'onboardingState', 'operationalState',
'vnfProductName', 'softwareImages', 'userDefinedData',
'vnfdId', 'vnfdVersion', 'vnfSoftwareVersion',
'vnfProvider', 'artifactPath', 'imagePath',
'diskFormat', 'userMetadata')
def _get_columns(vnf_package_obj):
column_map = {
'_links': 'Links',
'onboardingState': 'Onboarding State',
'operationalState': 'Operational State',
'usageState': 'Usage State',
'userDefinedData': 'User Defined Data',
'id': 'ID'
}
if vnf_package_obj['onboardingState'] == 'ONBOARDED':
column_map.update({
'softwareImages': 'Software Images',
'vnfProvider': 'VNF Provider',
'vnfSoftwareVersion': 'VNF Software Version',
'vnfProductName': 'VNF Product Name',
'vnfdId': 'VNFD ID',
'vnfdVersion': 'VNFD Version'
})
return sdk_utils.get_osc_show_columns_for_sdk_resource(vnf_package_obj,
column_map)
class CreateVnfPackage(command.ShowOne):
_description = _("Create a new VNF Package")
def get_parser(self, prog_name):
LOG.debug('get_parser(%s)', prog_name)
parser = super(CreateVnfPackage, self).get_parser(prog_name)
parser.add_argument(
'--user-data',
metavar='<key=value>',
action=parseractions.KeyValueAction,
help=_('User defined data for the VNF package '
'(repeat option to set multiple user defined data)'),
)
return parser
def args2body(self, parsed_args):
body = {}
if parsed_args.user_data:
body["userDefinedData"] = parsed_args.user_data
return body
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
vnf_package = client.create_vnf_package(self.args2body(parsed_args))
display_columns, columns = _get_columns(vnf_package)
data = utils.get_item_properties(
sdk_utils.DictModel(vnf_package),
columns, mixed_case_fields=_mixed_case_fields)
return (display_columns, data)
class ListVnfPackage(command.Lister):
_description = _("List VNF Package")
def get_parser(self, prog_name):
LOG.debug('get_parser(%s)', prog_name)
parser = super(ListVnfPackage, self).get_parser(prog_name)
return parser
def take_action(self, parsed_args):
_params = {}
client = self.app.client_manager.tackerclient
data = client.list_vnf_packages(**_params)
headers, columns = tacker_osc_utils.get_column_definitions(
_attr_map, long_listing=True)
return (headers,
(utils.get_dict_properties(
s, columns, mixed_case_fields=_mixed_case_fields,
) for s in data['vnf_packages']))
class ShowVnfPackage(command.ShowOne):
_description = _("Show VNF Package Details")
def get_parser(self, prog_name):
LOG.debug('get_parser(%s)', prog_name)
parser = super(ShowVnfPackage, self).get_parser(prog_name)
parser.add_argument(
'vnf_package',
metavar="<vnf-package>",
help=_("VNF package ID")
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
vnf_package = client.show_vnf_package(parsed_args.vnf_package)
display_columns, columns = _get_columns(vnf_package)
data = utils.get_item_properties(
sdk_utils.DictModel(vnf_package),
columns, mixed_case_fields=_mixed_case_fields)
return (display_columns, data)
class UploadVnfPackage(command.Command):
_description = _("Upload VNF Package")
def get_parser(self, prog_name):
LOG.debug('get_parser(%s)', prog_name)
parser = super(UploadVnfPackage, self).get_parser(prog_name)
parser.add_argument(
'vnf_package',
metavar="<vnf-package>",
help=_("VNF package ID")
)
file_source = parser.add_mutually_exclusive_group(required=True)
file_source.add_argument(
"--path",
metavar="<file>",
help=_("Upload VNF CSAR package from local file"),
)
file_source.add_argument(
"--url",
metavar="<Uri>",
help=_("Uri of the VNF package content"),
)
parser.add_argument(
"--user-name",
metavar="<user-name>",
help=_("User name for authentication"),
)
parser.add_argument(
"--password",
metavar="<password>",
help=_("Password for authentication"),
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
attrs = {}
if parsed_args.user_name:
attrs['userName'] = parsed_args.user_name
if parsed_args.password:
attrs['password'] = parsed_args.password
if parsed_args.url:
attrs['url'] = parsed_args.url
file_data = None
try:
if parsed_args.path:
file_data = open(parsed_args.path, 'rb')
result = client.upload_vnf_package(parsed_args.vnf_package,
file_data, **attrs)
if not result:
print((_('Upload request for VNF package %(id)s has been'
' accepted.') % {'id': parsed_args.vnf_package}))
finally:
if file_data:
file_data.close()
class DeleteVnfPackage(command.Command):
"""Vnf package delete
Delete class supports bulk deletion of vnf packages, and error
handling.
"""
_description = _("Delete VNF Package")
resource = 'vnf-package'
def get_parser(self, prog_name):
LOG.debug('get_parser(%s)', prog_name)
parser = super(DeleteVnfPackage, self).get_parser(prog_name)
parser.add_argument(
'vnf-package',
metavar="<vnf-package>",
nargs="+",
help=_("Vnf package(s) ID to delete")
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
failure = False
deleted_ids = []
failed_items = {}
resources = getattr(parsed_args, self.resource, [])
for resource_id in resources:
try:
vnf_package = client.show_vnf_package(resource_id)
client.delete_vnf_package(vnf_package['id'])
deleted_ids.append(resource_id)
except Exception as e:
failure = True
failed_items[resource_id] = e
if failure:
msg = ''
if deleted_ids:
msg = (_('Successfully deleted %(resource)s(s):'
' %(deleted_list)s') % {'deleted_list':
', '.join(deleted_ids),
'resource': self.resource})
err_msg = _("\n\nUnable to delete the below"
" 'vnf_package'(s):")
for failed_id, error in failed_items.items():
err_msg += (_('\n Cannot delete %(failed_id)s: %(error)s')
% {'failed_id': failed_id,
'error': error})
msg += err_msg
raise exceptions.CommandError(msg)
else:
print((_('All specified %(resource)s(s) deleted successfully')
% {'resource': self.resource}))
return

View File

@@ -639,8 +639,8 @@ class TackerShell(app.App):
self.options.os_project_domain_id)) or
self.options.os_project_id)
if (not self.options.os_username
and not self.options.os_user_id):
if (not self.options.os_username and
not self.options.os_user_id):
raise exc.CommandError(
_("You must provide a username or user ID via"
" --os-username, env[OS_USERNAME] or"

View File

@@ -520,6 +520,7 @@ class DeleteCommand(TackerCommand):
'ids', nargs='+',
metavar=self.resource.upper(),
help=help_str % self.resource)
self.add_known_arguments(parser)
return parser
def run(self, parsed_args):
@@ -530,6 +531,8 @@ class DeleteCommand(TackerCommand):
tacker_client.format = parsed_args.request_format
obj_deleter = getattr(tacker_client,
"delete_%s" % self.resource)
body = self.args2body(parsed_args)
for resource_id in parsed_args.ids:
try:
if self.allow_names:
@@ -537,7 +540,10 @@ class DeleteCommand(TackerCommand):
tacker_client, self.resource, resource_id)
else:
_id = resource_id
obj_deleter(_id)
if body:
obj_deleter(_id, body)
else:
obj_deleter(_id)
deleted_ids.append(resource_id)
except Exception as e:
failure = True

View File

@@ -25,7 +25,8 @@ class ListNS(tackerV10.ListCommand):
"""List NS that belong to a given tenant."""
resource = _NS
list_columns = ['id', 'name', 'nsd_id', 'mgmt_urls', 'status']
list_columns = ['id', 'name', 'nsd_id', 'vnf_ids', 'vnffg_ids',
'mgmt_ip_addresses', 'status']
class ShowNS(tackerV10.ShowCommand):
@@ -122,3 +123,17 @@ class DeleteNS(tackerV10.DeleteCommand):
resource = _NS
deleted_msg = {'ns': 'delete initiated'}
def add_known_arguments(self, parser):
parser.add_argument(
'--force',
default=False,
action='store_true',
help=_('Force delete Network Service'))
def args2body(self, parsed_args):
if parsed_args.force:
body = {self.resource: {'attributes': {'force': True}}}
else:
body = dict()
return body

View File

@@ -74,16 +74,9 @@ def args2body_vim(config_param, vim):
message='username and password or bearer_token must be'
'provided',
status_code=404)
if 'ssl_ca_cert' in config_param:
ssl_ca_cert = config_param.pop('ssl_ca_cert', '')
if ssl_ca_cert == 'None':
vim['auth_cred']['ssl_ca_cert'] = None
else:
vim['auth_cred']['ssl_ca_cert'] = ssl_ca_cert
else:
raise exceptions.TackerClientException(
message='ssl_ca_cert must be provided or leave it with None',
status_code=404)
ssl_ca_cert = config_param.pop('ssl_ca_cert', '')
if ssl_ca_cert:
vim['auth_cred']['ssl_ca_cert'] = ssl_ca_cert
def validate_auth_url(url):

View File

@@ -176,7 +176,7 @@ class ListClusterMember(tackerV10.ListCommand):
return body
list_columns = ['id', 'name', 'cluster_id', 'role', 'vnf_id',
'vim_id', 'mgmt_url', 'lb_member_id']
'vim_id', 'mgmt_ip_address', 'lb_member_id']
class DeleteClusterMember(tackerV10.DeleteCommand):

View File

@@ -78,7 +78,8 @@ class ListVNFFG(tackerV10.ListCommand):
"""List VNFFGs that belong to a given tenant."""
resource = _VNFFG
list_columns = ['id', 'name', 'description', 'status', 'vnffgd_id']
list_columns = ['id', 'name', 'ns_id',
'description', 'status', 'vnffgd_id']
class ShowVNFFG(tackerV10.ShowCommand):

View File

@@ -91,7 +91,7 @@ class ShowTemplateVNFFGD(tackerV10.ShowCommand):
template = None
data = self.get_data(parsed_args)
try:
attributes_index = data[0].index('attributes')
attributes_index = data[0].index('template')
attributes_json = data[1][attributes_index]
template = jsonutils.loads(attributes_json).get('vnffgd', None)
except (IndexError, TypeError, ValueError) as e:

View File

@@ -30,7 +30,7 @@ class ListVNF(tackerV10.ListCommand):
"""List VNF that belong to a given tenant."""
resource = _VNF
list_columns = ['id', 'name', 'mgmt_url', 'status',
list_columns = ['id', 'name', 'mgmt_ip_address', 'status',
'vim_id', 'vnfd_id']
@@ -179,8 +179,23 @@ class DeleteVNF(tackerV10.DeleteCommand):
"""Delete given VNF(s)."""
resource = _VNF
remove_output_fields = ["attributes"]
deleted_msg = {'vnf': 'delete initiated'}
def add_known_arguments(self, parser):
parser.add_argument(
'--force',
default=False,
action='store_true',
help=_('Force delete VNF instance'))
def args2body(self, parsed_args):
body = dict()
if parsed_args.force:
body[self.resource] = dict()
body[self.resource]['attributes'] = {'force': True}
return body
class ListVNFResources(tackerV10.ListCommand):
"""List resources of a VNF like VDU, CP, etc."""

View File

View File

@@ -0,0 +1,56 @@
# Copyright (C) 2019 NTT DATA
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import mock
from requests_mock.contrib import fixture as requests_mock_fixture
import testtools
class FixturedTestCase(testtools.TestCase):
client_fixture_class = None
def setUp(self):
super(FixturedTestCase, self).setUp()
self.app = mock.MagicMock()
if self.client_fixture_class:
self.requests_mock = self.useFixture(requests_mock_fixture.
Fixture())
fix = self.client_fixture_class(self.requests_mock)
self.cs = self.useFixture(fix).client
def check_parser(self, cmd, args, verify_args):
cmd_parser = cmd.get_parser('check_parser')
try:
parsed_args = cmd_parser.parse_args(args)
except SystemExit:
raise ParserException
for av in verify_args:
attr, value = av
if attr:
self.assertIn(attr, parsed_args)
self.assertEqual(getattr(parsed_args, attr), value)
return parsed_args
def assertNotCalled(self, m, msg=None):
"""Assert a function was not called"""
if m.called:
if not msg:
msg = 'method %s should not have been called' % m
self.fail(msg)
class ParserException(Exception):
pass

View File

@@ -0,0 +1,60 @@
# Copyright 2019 NTT DATA
#
# 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 fixtures
from keystoneauth1 import fixture
from keystoneauth1 import loading
from keystoneauth1 import session
from tackerclient.v1_0 import client as proxy_client
IDENTITY_URL = 'http://identityserver:5000/v3'
TACKER_URL = 'http://nfv-orchestration'
class ClientFixture(fixtures.Fixture):
def __init__(self, requests_mock, identity_url=IDENTITY_URL):
super(ClientFixture, self).__init__()
self.identity_url = identity_url
self.client = None
self.token = fixture.V2Token()
self.token.set_scope()
self.requests_mock = requests_mock
self.discovery = fixture.V2Discovery(href=self.identity_url)
s = self.token.add_service('nfv-orchestration')
s.add_endpoint(TACKER_URL)
def setUp(self):
super(ClientFixture, self).setUp()
auth_url = '%s/tokens' % self.identity_url
headers = {'X-Content-Type': 'application/json'}
self.requests_mock.post(auth_url, json=self.token, headers=headers)
self.requests_mock.get(self.identity_url, json=self.discovery,
headers=headers)
self.client = self.new_client()
def new_client(self):
self.session = session.Session()
loader = loading.get_plugin_loader('password')
self.session.auth = loader.load_from_options(
auth_url=self.identity_url, username='xx', password='xx')
return proxy_client.Client(service_type='nfv-orchestration',
interface='public',
endpoint_type='public',
region_name='RegionOne',
auth_url=self.identity_url,
token=self.token.token_id,
endpoint_url=TACKER_URL)

View File

@@ -0,0 +1,204 @@
# TODO:Manually change from version 1.2 to 1.0
tosca_definitions_version: tosca_simple_yaml_1_0
#tosca_definitions_version: tosca_simple_yaml_1_2
description: ETSI NFV SOL 001 common types definitions version 2.6.1
metadata:
template_name: etsi_nfv_sol001_common_types
template_author: ETSI_NFV
template_version: 2.6.1
data_types:
tosca.datatypes.nfv.L2AddressData:
derived_from: tosca.datatypes.Root
description: Describes the information on the MAC addresses to be assigned to a connection point.
properties:
mac_address_assignment:
type: boolean
description: Specifies if the address assignment is the responsibility of management and orchestration function or not. If it is set to True, it is the management and orchestration function responsibility
required: true
tosca.datatypes.nfv.L3AddressData:
derived_from: tosca.datatypes.Root
description: Provides information about Layer 3 level addressing scheme and parameters applicable to a CP
properties:
ip_address_assignment:
type: boolean
description: Specifies if the address assignment is the responsibility of management and orchestration function or not. If it is set to True, it is the management and orchestration function responsibility
required: true
floating_ip_activated:
type: boolean
description: Specifies if the floating IP scheme is activated on the Connection Point or not
required: true
ip_address_type:
type: string
description: Defines address type. The address type should be aligned with the address type supported by the layer_protocols properties of the parent VnfExtCp
required: false
constraints:
- valid_values: [ ipv4, ipv6 ]
number_of_ip_address:
type: integer
description: Minimum number of IP addresses to be assigned
required: false
constraints:
- greater_than: 0
tosca.datatypes.nfv.AddressData:
derived_from: tosca.datatypes.Root
description: Describes information about the addressing scheme and parameters applicable to a CP
properties:
address_type:
type: string
description: Describes the type of the address to be assigned to a connection point. The content type shall be aligned with the address type supported by the layerProtocol property of the connection point
required: true
constraints:
- valid_values: [ mac_address, ip_address ]
l2_address_data:
type: tosca.datatypes.nfv.L2AddressData
description: Provides the information on the MAC addresses to be assigned to a connection point.
required: false
l3_address_data:
type: tosca.datatypes.nfv.L3AddressData
description: Provides the information on the IP addresses to be assigned to a connection point
required: false
tosca.datatypes.nfv.ConnectivityType:
derived_from: tosca.datatypes.Root
description: describes additional connectivity information of a virtualLink
properties:
layer_protocols:
type: list
description: Identifies the protocol a virtualLink gives access to (ethernet, mpls, odu2, ipv4, ipv6, pseudo-wire).The top layer protocol of the virtualLink protocol stack shall always be provided. The lower layer protocols may be included when there are specific requirements on these layers.
required: true
entry_schema:
type: string
constraints:
- valid_values: [ ethernet, mpls, odu2, ipv4, ipv6, pseudo-wire ]
flow_pattern:
type: string
description: Identifies the flow pattern of the connectivity
required: false
constraints:
- valid_values: [ line, tree, mesh ]
tosca.datatypes.nfv.LinkBitrateRequirements:
derived_from: tosca.datatypes.Root
description: describes the requirements in terms of bitrate for a virtual link
properties:
root:
type: integer # in bits per second
description: Specifies the throughput requirement in bits per second of the link (e.g. bitrate of E-Line, root bitrate of E-Tree, aggregate capacity of E-LAN).
required: true
constraints:
- greater_or_equal: 0
leaf:
type: integer # in bits per second
description: Specifies the throughput requirement in bits per second of leaf connections to the link when applicable to the connectivity type (e.g. for E-Tree and E LAN branches).
required: false
constraints:
- greater_or_equal: 0
tosca.datatypes.nfv.CpProtocolData:
derived_from: tosca.datatypes.Root
description: Describes and associates the protocol layer that a CP uses together with other protocol and connection point information
properties:
associated_layer_protocol:
type: string
required: true
description: One of the values of the property layer_protocols of the CP
constraints:
- valid_values: [ ethernet, mpls, odu2, ipv4, ipv6, pseudo-wire ]
address_data:
type: list
description: Provides information on the addresses to be assigned to the CP
entry_schema:
type: tosca.datatypes.nfv.AddressData
required: false
tosca.datatypes.nfv.VnfProfile:
derived_from: tosca.datatypes.Root
description: describes a profile for instantiating VNFs of a particular NS DF according to a specific VNFD and VNF DF.
properties:
instantiation_level:
type: string
description: Identifier of the instantiation level of the VNF DF to be used for instantiation. If not present, the default instantiation level as declared in the VNFD shall be used.
required: false
min_number_of_instances:
type: integer
description: Minimum number of instances of the VNF based on this VNFD that is permitted to exist for this VnfProfile.
required: true
constraints:
- greater_or_equal: 0
max_number_of_instances:
type: integer
description: Maximum number of instances of the VNF based on this VNFD that is permitted to exist for this VnfProfile.
required: true
constraints:
- greater_or_equal: 0
tosca.datatypes.nfv.Qos:
derived_from: tosca.datatypes.Root
description: describes QoS data for a given VL used in a VNF deployment flavour
properties:
latency:
type: scalar-unit.time #Number
description: Specifies the maximum latency
required: true
constraints:
- greater_than: 0 s
packet_delay_variation:
type: scalar-unit.time #Number
description: Specifies the maximum jitter
required: true
constraints:
- greater_or_equal: 0 s
packet_loss_ratio:
type: float
description: Specifies the maximum packet loss ratio
required: false
constraints:
- in_range: [ 0.0, 1.0 ]
capability_types:
tosca.capabilities.nfv.VirtualLinkable:
derived_from: tosca.capabilities.Node
description: A node type that includes the VirtualLinkable capability indicates that it can be pointed by tosca.relationships.nfv.VirtualLinksTo relationship type
relationship_types:
tosca.relationships.nfv.VirtualLinksTo:
derived_from: tosca.relationships.DependsOn
description: Represents an association relationship between the VduCp and VnfVirtualLink node types
valid_target_types: [ tosca.capabilities.nfv.VirtualLinkable ]
node_types:
tosca.nodes.nfv.Cp:
derived_from: tosca.nodes.Root
description: Provides information regarding the purpose of the connection point
properties:
layer_protocols:
type: list
description: Identifies which protocol the connection point uses for connectivity purposes
required: true
entry_schema:
type: string
constraints:
- valid_values: [ ethernet, mpls, odu2, ipv4, ipv6, pseudo-wire ]
role: #Name in ETSI NFV IFA011 v0.7.3: cpRole
type: string
description: Identifies the role of the port in the context of the traffic flow patterns in the VNF or parent NS
required: false
constraints:
- valid_values: [ root, leaf ]
description:
type: string
description: Provides human-readable information on the purpose of the connection point
required: false
protocol:
type: list
description: Provides information on the addresses to be assigned to the connection point(s) instantiated from this Connection Point Descriptor
required: false
entry_schema:
type: tosca.datatypes.nfv.CpProtocolData
trunk_mode:
type: boolean
description: Provides information about whether the CP instantiated from this Cp is in Trunk mode (802.1Q or other), When operating in "trunk mode", the Cp is capable of carrying traffic for several VLANs. Absence of this property implies that trunkMode is not configured for the Cp i.e. It is equivalent to boolean value "false".
required: false

View File

@@ -0,0 +1,277 @@
tosca_definitions_version: tosca_simple_yaml_1_2
description: Simple deployment flavour for Sample VNF
imports:
- etsi_nfv_sol001_common_types.yaml
- etsi_nfv_sol001_vnfd_types.yaml
- helloworld3_types.yaml
topology_template:
inputs:
descriptor_id:
type: string
descriptor_version:
type: string
provider:
type: string
product_name:
type: string
software_version:
type: string
vnfm_info:
type: list
entry_schema:
type: string
flavour_id:
type: string
flavour_description:
type: string
substitution_mappings:
node_type: ntt.nslab.VNF
properties:
flavour_id: simple
requirements:
virtual_link_external: [ CP1, virtual_link ]
node_templates:
VNF:
type: ntt.nslab.VNF
properties:
flavour_description: A simple flavour
interfaces:
Vnflcm:
# supporting only 'instantiate', 'terminate', 'modify'
# not supporting LCM script, supporting only default LCM
instantiate: []
instantiate_start: []
instantiate_end: []
terminate: []
terminate_start: []
terminate_end: []
modify_information: []
modify_information_start: []
modify_information_end: []
# change_flavour: []
# change_flavour_start: []
# change_flavour_end: []
# change_external_connectivity: []
# change_external_connectivity_start: []
# change_external_connectivity_end: []
# operate: []
# operate_start: []
# operate_end: []
# heal: []
# heal_start: []
# heal_end: []
# scale: []
# scale_start: []
# scale_end: []
# scale_to_level: []
# scale_to_level_start: []
# scale_to_level_end: []
VDU1:
type: tosca.nodes.nfv.Vdu.Compute
properties:
name: VDU1
description: VDU1 compute node
vdu_profile:
min_number_of_instances: 1
max_number_of_instances: 1
sw_image_data:
name: Software of VDU1
version: '0.4.0'
checksum:
algorithm: sha-256
hash: b9c3036539fd7a5f87a1bf38eb05fdde8b556a1a7e664dbeda90ed3cd74b4f9d
container_format: bare
disk_format: qcow2
min_disk: 1 GB
size: 1 GB
artifacts:
sw_image:
type: tosca.artifacts.nfv.SwImage
file: cirros-0.4.0-x86_64-disk.img
repository: http://download.cirros-cloud.net/0.4.0/
capabilities:
virtual_compute:
properties:
virtual_memory:
virtual_mem_size: 512 MB
virtual_cpu:
num_virtual_cpu: 1
virtual_local_storage:
- size_of_storage: 1 GB
VDU2:
type: tosca.nodes.nfv.Vdu.Compute
properties:
name: VDU2
description: VDU2 compute node
vdu_profile:
min_number_of_instances: 1
max_number_of_instances: 3
capabilities:
virtual_compute:
properties:
virtual_memory:
virtual_mem_size: 512 MB
virtual_cpu:
num_virtual_cpu: 1
virtual_local_storage:
- size_of_storage: 1 GB
requirements:
- virtual_storage: VirtualStorage
VirtualStorage:
type: tosca.nodes.nfv.Vdu.VirtualBlockStorage
properties:
virtual_block_storage_data:
size_of_storage: 30 GB
rdma_enabled: true
sw_image_data:
name: VrtualStorage
version: '0.4.0'
checksum:
algorithm: sha-256
hash: b9c3036539fd7a5f87a1bf38eb05fdde8b556a1a7e664dbeda90ed3cd74b4f9d
container_format: bare
disk_format: qcow2
min_disk: 2 GB
min_ram: 8192 MB
size: 2 GB
artifacts:
sw_image:
type: tosca.artifacts.nfv.SwImage
file: cirros-0.4.0-x86_64-disk.img
repository: http://download.cirros-cloud.net/0.4.0/
CP1:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 0
vnic_type: direct-physical
requirements:
- virtual_binding: VDU1
#- virtual_link: # the target node is determined in the NSD
CP2:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 1
requirements:
- virtual_binding: VDU1
- virtual_link: internalVL2
CP3:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 2
requirements:
- virtual_binding: VDU2
- virtual_link: internalVL2
internalVL2:
type: tosca.nodes.nfv.VnfVirtualLink
properties:
connectivity_type:
layer_protocols: [ ipv4 ]
description: Internal Virtual link in the VNF
vl_profile:
max_bitrate_requirements:
root: 1048576
leaf: 1048576
min_bitrate_requirements:
root: 1048576
leaf: 1048576
virtual_link_protocol_data:
- associated_layer_protocol: ipv4
l3_protocol_data:
ip_version: ipv4
cidr: 11.11.0.0/24
policies:
- scaling_aspects:
type: tosca.policies.nfv.ScalingAspects
properties:
aspects:
worker_instance:
name: worker_instance_aspect
description: worker_instance scaling aspect
max_scale_level: 2
step_deltas:
- delta_1
- VDU2_initial_delta:
type: tosca.policies.nfv.VduInitialDelta
properties:
initial_delta:
number_of_instances: 1
targets: [ VDU2 ]
- VDU2_scaling_aspect_deltas:
type: tosca.policies.nfv.VduScalingAspectDeltas
properties:
aspect: worker_instance
deltas:
delta_2:
number_of_instances: 1
targets: [ VDU2 ]
- instantiation_levels:
type: tosca.policies.nfv.InstantiationLevels
properties:
levels:
instantiation_level_1:
description: Smallest size
scale_info:
worker_instance:
scale_level: 0
instantiation_level_2:
description: Largest size
scale_info:
worker_instance:
scale_level: 2
default_level: instantiation_level_1
- VDU1_instantiation_levels:
type: tosca.policies.nfv.VduInstantiationLevels
properties:
levels:
instantiation_level_1:
number_of_instances: 1
instantiation_level_2:
number_of_instances: 3
targets: [ VDU1 ]
- VDU2_instantiation_levels:
type: tosca.policies.nfv.VduInstantiationLevels
properties:
levels:
instantiation_level_1:
number_of_instances: 1
instantiation_level_2:
number_of_instances: 1
targets: [ VDU2 ]
- internalVL2_instantiation_levels:
type: tosca.policies.nfv.VirtualLinkInstantiationLevels
properties:
levels:
instantiation_level_1:
bitrate_requirements:
root: 1048576
leaf: 1048576
instantiation_level_2:
bitrate_requirements:
root: 1048576
leaf: 1048576
targets: [ internalVL2 ]

View File

@@ -0,0 +1,32 @@
tosca_definitions_version: tosca_simple_yaml_1_0
description: Sample VNF of NTT NS lab.
imports:
- etsi_nfv_sol001_common_types.yaml
- etsi_nfv_sol001_vnfd_types.yaml
- helloworld3_types.yaml
- helloworld3_df_simple.yaml
# - helloworld3_df_complex.yaml
topology_template:
inputs:
selected_flavour:
type: string
description: VNF deployment flavour selected by the consumer. It is provided in the API
node_templates:
VNF:
type: ntt.nslab.VNF
properties:
flavour_id: { get_input: selected_flavour }
descriptor_id: b1bb0ce7-ebca-4fa7-95ed-4840d70a1177
provider: NTT NS lab
product_name: Sample VNF
software_version: '1.0'
descriptor_version: '1.0'
vnfm_info:
- Tacker
requirements:
#- virtual_link_external # mapped in lower-level templates
#- virtual_link_internal # mapped in lower-level templates

View File

@@ -0,0 +1,53 @@
tosca_definitions_version: tosca_simple_yaml_1_0
description: ntt.nslab.VNF type definition
imports:
- etsi_nfv_sol001_common_types.yaml
- etsi_nfv_sol001_vnfd_types.yaml
node_types:
ntt.nslab.VNF:
derived_from: tosca.nodes.nfv.VNF
properties:
descriptor_id:
type: string
constraints: [ valid_values: [ b1bb0ce7-ebca-4fa7-95ed-4840d70a1177 ] ]
default: b1bb0ce7-ebca-4fa7-95ed-4840d70a1177
descriptor_version:
type: string
constraints: [ valid_values: [ '1.0' ] ]
default: '1.0'
provider:
type: string
constraints: [ valid_values: [ 'NTT NS lab' ] ]
default: 'NTT NS lab'
product_name:
type: string
constraints: [ valid_values: [ 'Sample VNF' ] ]
default: 'Sample VNF'
software_version:
type: string
constraints: [ valid_values: [ '1.0' ] ]
default: '1.0'
vnfm_info:
type: list
entry_schema:
type: string
constraints: [ valid_values: [ Tacker ] ]
default: [ Tacker ]
flavour_id:
type: string
constraints: [ valid_values: [ simple ] ]
default: simple
flavour_description:
type: string
default: ""
requirements:
- virtual_link_external:
capability: tosca.capabilities.nfv.VirtualLinkable
- virtual_link_internal:
capability: tosca.capabilities.nfv.VirtualLinkable
interfaces:
Vnflcm:
type: tosca.interfaces.nfv.Vnflcm

View File

@@ -0,0 +1,4 @@
TOSCA-Meta-File-Version: 1.0
Created-by: Hiroyuki JO
CSAR-Version: 1.1
Entry-Definitions: Definitions/helloworld3_top.vnfd.yaml

View File

@@ -0,0 +1,328 @@
# Copyright (C) 2019 NTT DATA
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os
import shutil
import tempfile
import ddt
import mock
from tackerclient.common import exceptions
from tackerclient.osc import utils as tacker_osc_utils
from tackerclient.osc.v1.vnfpkgm import vnf_package
from tackerclient.tests.unit.osc import base
from tackerclient.tests.unit.osc.v1.fixture_data import client
from tackerclient.tests.unit.osc.v1 import vnf_package_fakes
from tackerclient.v1_0 import client as proxy_client
def _create_zip():
vnf_package_path = ('./tackerclient/tests//unit/osc/v1/fixture_data/'
'sample_vnf_package')
tmpdir = tempfile.mkdtemp()
tmparchive = os.path.join(tmpdir, 'sample_vnf_package')
zip_file = shutil.make_archive(tmparchive, 'zip', vnf_package_path)
return zip_file, tmpdir
def _get_columns_vnf_package(action='list', vnf_package_obj=None):
columns = ['ID', 'Onboarding State', 'Operational State', 'Usage State',
'User Defined Data', 'VNF Product Name']
if action in ['show', 'create']:
if vnf_package_obj and vnf_package_obj[
'onboardingState'] == 'ONBOARDED':
columns.extend(['Links', 'VNFD ID', 'VNF Provider',
'VNF Software Version', 'VNFD Version',
'Software Images'])
else:
columns.extend(['Links'])
columns.remove('VNF Product Name')
return columns
class TestVnfPackage(base.FixturedTestCase):
client_fixture_class = client.ClientFixture
def setUp(self):
super(TestVnfPackage, self).setUp()
self.url = client.TACKER_URL
self.header = {'content-type': 'application/json'}
self.app = mock.Mock()
self.app_args = mock.Mock()
self.client_manager = self.cs
self.app.client_manager.tackerclient = self.client_manager
@ddt.ddt
class TestCreateVnfPackage(TestVnfPackage):
def setUp(self):
super(TestCreateVnfPackage, self).setUp()
self.create_vnf_package = vnf_package.CreateVnfPackage(
self.app, self.app_args, cmd_name='vnf package create')
@ddt.data((["--user-data", 'Test_key=Test_value'],
[('user_data', {'Test_key': 'Test_value'})]),
([], []))
@ddt.unpack
def test_take_action(self, arglist, verifylist):
# command param
parsed_args = self.check_parser(self.create_vnf_package, arglist,
verifylist)
if arglist:
json = vnf_package_fakes.vnf_package_obj(
attrs={'userDefinedData': {'Test_key': 'Test_value'}})
else:
json = vnf_package_fakes.vnf_package_obj()
self.requests_mock.register_uri(
'POST', self.url + '/vnfpkgm/v1/vnf_packages',
json=json, headers=self.header)
columns, data = (self.create_vnf_package.take_action(parsed_args))
self.assertItemsEqual(_get_columns_vnf_package(action='create'),
columns)
self.assertItemsEqual(vnf_package_fakes.get_vnf_package_data(json),
data)
class TestListVnfPackage(TestVnfPackage):
_vnf_packages = vnf_package_fakes.create_vnf_packages(count=3)
def setUp(self):
super(TestListVnfPackage, self).setUp()
self.list_vnf_package = vnf_package.ListVnfPackage(
self.app, self.app_args, cmd_name='vnf package list')
def test_take_action(self):
parsed_args = self.check_parser(self.list_vnf_package, [], [])
self.requests_mock.register_uri(
'GET', self.url + '/vnfpkgm/v1/vnf_packages',
json=self._vnf_packages, headers=self.header)
actual_columns, data = self.list_vnf_package.take_action(parsed_args)
expected_data = []
headers, columns = tacker_osc_utils.get_column_definitions(
vnf_package._attr_map, long_listing=True)
for vnf_package_obj in self._vnf_packages['vnf_packages']:
expected_data.append(vnf_package_fakes.get_vnf_package_data(
vnf_package_obj, columns=columns, list_action=True))
self.assertItemsEqual(_get_columns_vnf_package(), actual_columns)
self.assertItemsEqual(expected_data, list(data))
@ddt.ddt
class TestShowVnfPackage(TestVnfPackage):
def setUp(self):
super(TestShowVnfPackage, self).setUp()
self.show_vnf_package = vnf_package.ShowVnfPackage(
self.app, self.app_args, cmd_name='vnf package show')
@ddt.data(True, False)
def test_take_action(self, onboarded):
vnf_package_obj = vnf_package_fakes.vnf_package_obj(
onboarded_state=onboarded)
arglist = [vnf_package_obj['id']]
verifylist = [('vnf_package', vnf_package_obj['id'])]
parsed_args = self.check_parser(self.show_vnf_package, arglist,
verifylist)
url = self.url + '/vnfpkgm/v1/vnf_packages/' + vnf_package_obj['id']
self.requests_mock.register_uri('GET', url, json=vnf_package_obj,
headers=self.header)
columns, data = (self.show_vnf_package.take_action(parsed_args))
self.assertItemsEqual(_get_columns_vnf_package(
vnf_package_obj=vnf_package_obj, action='show'), columns)
self.assertItemsEqual(
vnf_package_fakes.get_vnf_package_data(vnf_package_obj), data)
def test_show_no_options(self):
self.assertRaises(base.ParserException, self.check_parser,
self.show_vnf_package, [], [])
class TestDeleteVnfPackage(TestVnfPackage):
def setUp(self):
super(TestDeleteVnfPackage, self).setUp()
self.delete_vnf_package = vnf_package.DeleteVnfPackage(
self.app, self.app_args, cmd_name='vnf package delete')
# The Vnf Package to delete
self._vnf_package = vnf_package_fakes.create_vnf_packages(count=3)
def _mock_request_url_for_delete(self, vnf_pkg_index):
url = (self.url + '/vnfpkgm/v1/vnf_packages/' +
self._vnf_package['vnf_packages'][vnf_pkg_index]['id'])
json = self._vnf_package['vnf_packages'][vnf_pkg_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_vnf_package(self):
arglist = [self._vnf_package['vnf_packages'][0]['id']]
verifylist = [('vnf-package', [self._vnf_package['vnf_packages']
[0]['id']])]
parsed_args = self.check_parser(self.delete_vnf_package, arglist,
verifylist)
self._mock_request_url_for_delete(0)
result = self.delete_vnf_package.take_action(parsed_args)
self.assertIsNone(result)
def test_delete_multiple_vnf_package(self):
arglist = []
for vnf_pkg in self._vnf_package['vnf_packages']:
arglist.append(vnf_pkg['id'])
verifylist = [('vnf-package', arglist)]
parsed_args = self.check_parser(self.delete_vnf_package, arglist,
verifylist)
for i in range(0, 3):
self._mock_request_url_for_delete(i)
result = self.delete_vnf_package.take_action(parsed_args)
self.assertIsNone(result)
def test_delete_multiple_vnf_package_exception(self):
arglist = [
self._vnf_package['vnf_packages'][0]['id'],
'xxxx-yyyy-zzzz',
self._vnf_package['vnf_packages'][1]['id'],
]
verifylist = [
('vnf-package', arglist),
]
parsed_args = self.check_parser(self.delete_vnf_package,
arglist, verifylist)
self._mock_request_url_for_delete(0)
url = (self.url + '/vnfpkgm/v1/vnf_packages/' + 'xxxx-yyyy-zzzz')
body = {"error": exceptions.NotFound('404')}
self.requests_mock.register_uri('GET', url, body=body,
status_code=404, headers=self.header)
self._mock_request_url_for_delete(1)
self.assertRaises(exceptions.CommandError,
self.delete_vnf_package.take_action,
parsed_args)
@ddt.ddt
class TestUploadVnfPackage(TestVnfPackage):
# The new vnf package created.
_vnf_package = vnf_package_fakes.vnf_package_obj(
attrs={'userDefinedData': {'Test_key': 'Test_value'}})
def setUp(self):
super(TestUploadVnfPackage, self).setUp()
self.upload_vnf_package = vnf_package.UploadVnfPackage(
self.app, self.app_args, cmd_name='vnf package upload')
def test_upload_no_options(self):
self.assertRaises(base.ParserException, self.check_parser,
self.upload_vnf_package, [], [])
def _mock_request_url_for_upload(self, method, status_code=202, body={}):
if method == 'PUT':
self.header = {'content-type': 'application/zip'}
url = (self.url + '/vnfpkgm/v1/vnf_packages/' +
self._vnf_package['id'] + '/package_content')
else:
url = (self.url + '/vnfpkgm/v1/vnf_packages/' +
self._vnf_package['id'] + '/package_content/'
'upload_from_uri')
self.requests_mock.register_uri(method, url, json=body,
headers=self.header,
status_code=status_code)
def _get_arglist_and_verifylist(self, method, path):
if method == 'path':
arglist = [
self._vnf_package['id'],
"--path", path
]
verifylist = [
('path', path),
('vnf_package', self._vnf_package['id'])
]
else:
arglist = [
self._vnf_package['id'],
"--url", "http://uri:8000/test.zip",
"--user-name", "Test_username",
"--password", "Test_passoword",
]
verifylist = [
('url', "http://uri:8000/test.zip"),
('user_name', 'Test_username'),
('password', 'Test_passoword'),
('vnf_package', self._vnf_package['id'])
]
return arglist, verifylist
@ddt.data('path', 'url')
def test_upload_vnf_package(self, method):
path = None
if method == 'path':
zip_file, temp_dir = _create_zip()
path = zip_file
arglist, verifylist = self._get_arglist_and_verifylist(method,
path)
parsed_args = self.check_parser(self.upload_vnf_package, arglist,
verifylist)
with mock.patch.object(proxy_client.ClientBase,
'_handle_fault_response') as m:
if method == 'url':
self._mock_request_url_for_upload('POST')
self.upload_vnf_package.take_action(parsed_args)
else:
self._mock_request_url_for_upload('PUT')
self.upload_vnf_package.take_action(parsed_args)
# Delete temporary folder
shutil.rmtree(temp_dir)
# check no fault response is received
self.assertNotCalled(m)
def test_upload_vnf_package_with_conflict_error(self):
# Scenario in which vnf package is already in on-boarded state
zip_file, temp_dir = _create_zip()
arglist, verifylist = self._get_arglist_and_verifylist('path',
zip_file)
parsed_args = self.check_parser(self.upload_vnf_package, arglist,
verifylist)
body = {"conflictingRequest": {
"message": "VNF Package " + self._vnf_package['id'] +
" onboarding state is not CREATED", "code": 409}}
self._mock_request_url_for_upload('PUT', status_code=409, body=body)
self.assertRaises(exceptions.TackerClientException,
self.upload_vnf_package.take_action, parsed_args)
# Delete temporary folder
shutil.rmtree(temp_dir)

View File

@@ -0,0 +1,128 @@
# Copyright (C) 2019 NTT DATA
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_utils import uuidutils
def vnf_package_obj(attrs=None, onboarded_state=False):
"""Create a fake vnf package.
:param Dictionary attrs:
A dictionary with all attributes
:return:
A FakeVnfPackage dict
"""
attrs = attrs or {}
# Set default attributes.
fake_vnf_package = {"id": "60a6ac16-b50d-4e92-964b-b3cf98c7cf5c",
"_links": {"self": {"href": "string"},
"packageContent": {"href": "string"}
},
"onboardingState": "CREATED",
"operationalState": "DISABLED",
"usageState": "NOT_IN_USE",
"userDefinedData": None}
if onboarded_state:
fake_vnf_package = {"id": "60a6ac16-b50d-4e92-964b-b3cf98c7cf5c",
"vnfdId": "string",
"vnfProvider": "string",
"vnfProductName": "string",
"vnfSoftwareVersion": "string",
"vnfdVersion": "string",
"softwareImages": [
{
"id": "string",
"name": "string",
"provider": "string",
"version": "string",
"checksum": {
"algorithm": "string",
"hash": "string"
},
"containerFormat": "AKI",
"diskFormat": "AKI",
"createdAt": "2015-06-03T18:49:19.000000",
"minDisk": '0',
"minRam": '0',
"size": '0',
"userMetadata": {},
"imagePath": "string"
}
],
"onboardingState": "ONBOARDED",
"operationalState": "ENABLED",
"usageState": "IN_USE",
"userDefinedData": None,
"_links": {
"self": {
"href": "string"
},
"vnfd": {
"href": "string"
},
"packageContent": {
"href": "string"
}
}}
# Overwrite default attributes.
fake_vnf_package.update(attrs)
return fake_vnf_package
def get_vnf_package_data(vnf_package, list_action=False, columns=None):
"""Get the vnf package data from a FakeVnfPackage dict object.
:param vnf_package:
A FakeVnfPackage dict object
:return:
A list which may include the following values:
[{'packageContent': {'href': 'string'}, 'self': {'href': 'string'},
'vnfd': {'href': 'string'}}, '60a6ac16-b50d-4e92-964b-b3cf98c7cf5c',
'CREATED', 'DISABLED', 'NOT_IN_USE', {'Test_key': 'Test_value'}]
"""
if list_action:
vnf_package.pop('_links')
# In case of List VNF packages we get empty string as data for
# 'vnfProductName' if onboardingState is CREATED. Hence to match
# up with actual data we are adding here empty string.
if not vnf_package.get('vnfProductName'):
vnf_package['vnfProductName'] = ''
# return the list of data as per column order
if columns:
return tuple([vnf_package[key] for key in columns])
return tuple([vnf_package[key] for key in sorted(vnf_package.keys())])
def create_vnf_packages(count=2):
"""Create multiple fake vnf packages.
:param Dictionary attrs:
A dictionary with all attributes
:param int count:
The number of vnf_packages to fake
:return:
A list of fake vnf packages dictionary
"""
vnf_packages = []
for i in range(0, count):
unique_id = uuidutils.generate_uuid()
vnf_packages.append(vnf_package_obj(attrs={'id': unique_id}))
return {'vnf_packages': vnf_packages}

View File

@@ -163,7 +163,7 @@ class CLITestAuthKeystone(testtools.TestCase):
self.client.auth_token = TOKEN
self.client.endpoint_url = ENDPOINT_URL
# If a token is expired, tacker server retruns 401
# If a token is expired, tacker server returns 401
mock_request.return_value = (resp_401, '')
self.assertRaises(exceptions.Unauthorized,
self.client.do_request,
@@ -184,7 +184,7 @@ class CLITestAuthKeystone(testtools.TestCase):
self.client.auth_token = TOKEN
self.client.endpoint_url = ENDPOINT_URL
# If a token is expired, tacker server retruns 401
# If a token is expired, tacker server returns 401
mock_request.return_value = (resp_401, '')
self.assertRaises(exceptions.NoAuthURLProvided,
self.client.do_request,

View File

@@ -192,9 +192,9 @@ class CLITestV10Base(testtools.TestCase):
def setUp(self, plurals={}):
"""Prepare the test environment."""
super(CLITestV10Base, self).setUp()
client.Client.EXTED_PLURALS.update(constants.PLURALS)
client.Client.EXTED_PLURALS.update(plurals)
self.metadata = {'plurals': client.Client.EXTED_PLURALS,
client.LegacyClient.EXTED_PLURALS.update(constants.PLURALS)
client.LegacyClient.EXTED_PLURALS.update(plurals)
self.metadata = {'plurals': client.LegacyClient.EXTED_PLURALS,
'xmlns': constants.XML_NS_V10,
constants.EXT_NS: {'prefix':
'http://xxxx.yy.com'}}
@@ -207,7 +207,8 @@ class CLITestV10Base(testtools.TestCase):
self.useFixture(fixtures.MonkeyPatch(
'tackerclient.tacker.v1_0.find_resourceid_by_id',
self._find_resourceid))
self.client = client.Client(token=TOKEN, endpoint_url=self.endurl)
self.client = client.LegacyClient(token=TOKEN,
endpoint_url=self.endurl)
@mock.patch.object(TackerCommand, 'get_client')
def _test_create_resource(self, resource, cmd,
@@ -611,10 +612,18 @@ class CLITestV10Base(testtools.TestCase):
args.extend(['--request-format', self.format])
cmd_parser = cmd.get_parser("delete_" + resource)
shell.run_command(cmd, cmd_parser, args)
mock_req.assert_called_once_with(
end_url(path % myid, format=self.format), 'DELETE',
body=None,
headers=test_utils.ContainsKeyValue('X-Auth-Token', TOKEN))
if '--force' in args:
body_str = '{"' + resource + \
'": {"attributes": {"force": true}}}'
mock_req.assert_called_once_with(
end_url(path % myid, format=self.format), 'DELETE',
body=body_str,
headers=test_utils.ContainsKeyValue('X-Auth-Token', TOKEN))
else:
mock_req.assert_called_once_with(
end_url(path % myid, format=self.format), 'DELETE',
body=None,
headers=test_utils.ContainsKeyValue('X-Auth-Token', TOKEN))
mock_get.assert_called_once_with()
_str = self.fake_stdout.make_string()
msg = 'All specified %(resource)s(s) %(msg)s successfully\n' % {
@@ -677,7 +686,7 @@ class ClientV1TestJson(CLITestV10Base):
error = self.assertRaises(exceptions.TackerClientException,
self.client.do_request, 'PUT', '/test',
body='', params=params)
self.assertEqual("An error", str(error))
self.assertEqual("400-tackerFault", str(error))
class CLITestV10ExceptionHandler(CLITestV10Base):
@@ -771,11 +780,11 @@ class CLITestV10ExceptionHandler(CLITestV10Base):
error_content = {'message': 'This is an error message'}
self._test_exception_handler_v10(
exceptions.TackerClientException, 500,
expected_msg='This is an error message',
expected_msg='500-tackerFault',
error_content=error_content)
def test_exception_handler_v10_error_dict_not_contain_message(self):
error_content = {'error': 'This is an error message'}
error_content = 'tackerFault'
expected_msg = '%s-%s' % (500, error_content)
self._test_exception_handler_v10(
exceptions.TackerClientException, 500,

View File

@@ -35,7 +35,7 @@ class CLITestV10VIMJSON(test_cli10.CLITestV10Base):
plurals = {'vims': 'vim'}
super(CLITestV10VIMJSON, self).setUp(plurals=plurals)
self.vim_project = {'name': 'default'}
self.auth_cred = {'bearer_token': 'xyz', 'ssl_ca_cert': None}
self.auth_cred = {'bearer_token': 'xyz', 'ssl_ca_cert': "None"}
self.auth_url = 'https://1.2.3.4:6443'
self.type = 'kubernetes'

View File

@@ -187,12 +187,18 @@ class CLITestV10VmVNFJSON(test_cli10.CLITestV10Base):
[my_id, '--%s' % key, value],
{key: value})
def test_delete_vnf(self):
def test_delete_vnf_without_force(self):
cmd = vnf.DeleteVNF(test_cli10.MyApp(sys.stdout), None)
my_id = 'my-id'
args = [my_id]
self._test_delete_resource(self._RESOURCE, cmd, my_id, args)
def test_delete_vnf_with_force(self):
cmd = vnf.DeleteVNF(test_cli10.MyApp(sys.stdout), None)
my_id = 'my-id'
args = [my_id, '--force']
self._test_delete_resource(self._RESOURCE, cmd, my_id, args)
def test_list_vnf_resources(self):
cmd = vnf.ListVNFResources(test_cli10.MyApp(sys.stdout), None)
base_args = [self.test_id]

View File

@@ -63,7 +63,7 @@ class TestVIMUtils(testtools.TestCase):
def test_args2body_kubernetes_vim_bearer(self):
config_param = {'bearer_token': sentinel.bearer_token,
'ssl_ca_cert': None,
'ssl_ca_cert': "None",
'project_name': sentinel.prj_name,
'type': 'kubernetes'}
vim = {}

View File

@@ -31,6 +31,18 @@ from tackerclient.i18n import _
_logger = logging.getLogger(__name__)
DEFAULT_DESC_LENGTH = 25
DEFAULT_ERROR_REASON_LENGTH = 100
STATUS_CODE_MAP = {
400: "badRequest",
401: "unauthorized",
403: "forbidden",
404: "itemNotFound",
405: "badMethod",
409: "conflictingRequest",
413: "overLimit",
415: "badMediaType",
429: "overLimit",
501: "notImplemented",
503: "serviceUnavailable"}
def exception_handler_v10(status_code, error_content):
@@ -45,6 +57,9 @@ def exception_handler_v10(status_code, error_content):
error_dict = None
if isinstance(error_content, dict):
error_dict = error_content.get('TackerError')
if not error_dict:
error_content = error_content.get(STATUS_CODE_MAP.get(status_code),
'tackerFault')
# Find real error type
bad_tacker_error_flag = False
if error_dict:
@@ -141,17 +156,6 @@ class ClientBase(object):
:param session: Keystone client auth session to use. (optional)
:param auth: Keystone auth plugin to use. (optional)
Example::
from tackerclient.v1_0 import client
tacker = client.Client(username=USER,
password=PASS,
tenant_name=TENANT_NAME,
auth_url=KEYSTONE_URL)
nets = tacker.list_networks()
...
"""
# API has no way to report plurals, so we have to hard code them
@@ -182,15 +186,19 @@ class ClientBase(object):
# Raise the appropriate exception
exception_handler_v10(status_code, des_error_body)
def do_request(self, method, action, body=None, headers=None, params=None):
# Add format and tenant_id
def build_action(self, action):
action += ".%s" % self.format
action = self.action_prefix + action
return action
def do_request(self, method, action, body=None, headers=None, params=None):
action = self.build_action(action)
# Add format and tenant_id
if type(params) is dict and params:
params = utils.safe_encode_dict(params)
action += '?' + urlparse.urlencode(params, doseq=1)
if body:
if body or body == {}:
body = self.serialize(body)
resp, replybody = self.httpclient.do_request(
@@ -219,6 +227,8 @@ class ClientBase(object):
"""
if data is None:
return None
elif self.format == 'zip':
return data
elif type(data) is dict:
return serializer.Serializer(
self.get_attr_metadata()).serialize(data, self.content_type())
@@ -228,7 +238,7 @@ class ClientBase(object):
def deserialize(self, data, status_code):
"""Deserializes an XML or JSON string into a dictionary."""
if status_code == 204:
if status_code in (204, 202):
return data
return serializer.Serializer(self.get_attr_metadata()).deserialize(
data, self.content_type())['body']
@@ -259,7 +269,7 @@ class ClientBase(object):
"""Call do_request with the default retry configuration.
Only idempotent requests should retry failed connection attempts.
:raises: ConnectionFailed if the maximum # of retries is exceeded
:raises ConnectionFailed: if the maximum # of retries is exceeded
"""
max_attempts = self.retries + 1
for i in range(max_attempts):
@@ -329,7 +339,7 @@ class ClientBase(object):
break
class Client(ClientBase):
class LegacyClient(ClientBase):
extensions_path = "/extensions"
extension_path = "/extensions/%s"
@@ -438,8 +448,8 @@ class Client(ClientBase):
return self.post(self.vnfs_path, body=body)
@APIParamsCall
def delete_vnf(self, vnf):
return self.delete(self.vnf_path % vnf)
def delete_vnf(self, vnf, body=None):
return self.delete(self.vnf_path % vnf, body=body)
@APIParamsCall
def update_vnf(self, vnf, body):
@@ -665,8 +675,8 @@ class Client(ClientBase):
return self.post(self.nss_path, body=body)
@APIParamsCall
def delete_ns(self, ns):
return self.delete(self.ns_path % ns)
def delete_ns(self, ns, body=None):
return self.delete(self.ns_path % ns, body=body)
@APIParamsCall
def create_cluster(self, body=None):
@@ -708,3 +718,299 @@ class Client(ClientBase):
@APIParamsCall
def delete_clustermember(self, clustermember):
return self.delete(self.cluster_member_path % clustermember)
class VnfPackageClient(ClientBase):
"""Client for vnfpackage APIs.
Purpose of this class is to create required request url for vnfpackage
APIs.
"""
vnfpackages_path = '/vnfpkgm/v1/vnf_packages'
vnfpackage_path = '/vnfpkgm/v1/vnf_packages/%s'
def build_action(self, action):
return action
@APIParamsCall
def create_vnf_package(self, body):
return self.post(self.vnfpackages_path, body=body)
@APIParamsCall
def list_vnf_packages(self, retrieve_all=True, **_params):
vnf_packages = self.list("vnf_packages", self.vnfpackages_path,
retrieve_all, **_params)
return vnf_packages
@APIParamsCall
def show_vnf_package(self, vnf_package, **_params):
return self.get(self.vnfpackage_path % vnf_package, params=_params)
@APIParamsCall
def delete_vnf_package(self, vnf_package):
return self.delete(self.vnfpackage_path % vnf_package)
@APIParamsCall
def upload_vnf_package(self, vnf_package, file_data=None, **attrs):
if attrs.get('url'):
json = {'addressInformation': attrs.get('url')}
for key in ['userName', 'password']:
if attrs.get(key):
json.update({key: attrs.get(key)})
return self.post(
'{base_path}/{id}/package_content/upload_from_uri'.format(
id=vnf_package, base_path=self.vnfpackages_path),
body=json)
else:
self.format = 'zip'
return self.put('{base_path}/{id}/package_content'.format(
id=vnf_package,
base_path=self.vnfpackages_path),
body=file_data)
class Client(object):
"""Unified interface to interact with multiple applications of tacker service.
This class is a single entry point to interact with legacy tacker apis and
vnf packages apis.
Example::
from tackerclient.v1_0 import client
tacker = client.Client(username=USER,
password=PASS,
tenant_name=TENANT_NAME,
auth_url=KEYSTONE_URL)
vnf_package = tacker.create_vnf_package(...)
nsd = tacker.create_nsd(...)
"""
def __init__(self, **kwargs):
self.vnf_package_client = VnfPackageClient(**kwargs)
self.legacy_client = LegacyClient(**kwargs)
# LegacyClient methods
def delete(self, action, body=None, headers=None, params=None):
return self.legacy_client.delete(action, body=body, headers=headers,
params=params)
def get(self, action, body=None, headers=None, params=None):
return self.legacy_client.get(action, body=body, headers=headers,
params=params)
def post(self, action, body=None, headers=None, params=None):
return self.legacy_client.post(action, body=body, headers=headers,
params=params)
def put(self, action, body=None, headers=None, params=None):
return self.legacy_client.put(action, body=body, headers=headers,
params=params)
def list(self, collection, path, retrieve_all=True, **params):
return self.legacy_client.list(collection, path,
retrieve_all=retrieve_all, **params)
def list_extensions(self, **_params):
return self.legacy_client.list_extensions(**_params)
def show_extension(self, ext_alias, **_params):
"""Fetch a list of all exts on server side."""
return self.legacy_client.show_extension(ext_alias, **_params)
def list_vnfds(self, retrieve_all=True, **_params):
return self.legacy_client.list_vnfds(retrieve_all=retrieve_all,
**_params)
def show_vnfd(self, vnfd, **_params):
return self.legacy_client.show_vnfd(vnfd, **_params)
def create_vnfd(self, body):
return self.legacy_client.create_vnfd(body)
def delete_vnfd(self, vnfd):
return self.legacy_client.delete_vnfd(vnfd)
def list_vnfs(self, retrieve_all=True, **_params):
return self.legacy_client.list_vnfs(retrieve_all=retrieve_all,
**_params)
def show_vnf(self, vnf, **_params):
return self.legacy_client.show_vnf(vnf, **_params)
def create_vnf(self, body):
return self.legacy_client.create_vnf(body)
def delete_vnf(self, vnf, body=None):
return self.legacy_client.delete_vnf(vnf, body=body)
def update_vnf(self, vnf, body):
return self.legacy_client.update_vnf(vnf, body)
def list_vnf_resources(self, vnf, retrieve_all=True, **_params):
return self.legacy_client.list_vnf_resources(
vnf, retrieve_all=retrieve_all, **_params)
def scale_vnf(self, vnf, body=None):
return self.legacy_client.scale_vnf(vnf, body=body)
def show_vim(self, vim, **_params):
return self.legacy_client.show_vim(vim, **_params)
def create_vim(self, body):
return self.legacy_client.create_vim(body)
def delete_vim(self, vim):
return self.legacy_client.delete_vim(vim)
def update_vim(self, vim, body):
return self.legacy_client.update_vim(vim, body)
def list_vims(self, retrieve_all=True, **_params):
return self.legacy_client.list_vims(retrieve_all=retrieve_all,
**_params)
def list_events(self, retrieve_all=True, **_params):
return self.legacy_client.list_events(retrieve_all=retrieve_all,
**_params)
def list_vnf_events(self, retrieve_all=True, **_params):
return self.legacy_client.list_vnf_events(
retrieve_all=retrieve_all, **_params)
def list_vnfd_events(self, retrieve_all=True, **_params):
return self.legacy_client.list_vnfd_events(
retrieve_all=retrieve_all, **_params)
def list_vim_events(self, retrieve_all=True, **_params):
return self.legacy_client.list_vim_events(
retrieve_all=retrieve_all, **_params)
def show_event(self, event_id, **_params):
return self.legacy_client.show_event(event_id, **_params)
def create_vnffgd(self, body):
return self.legacy_client.create_vnffgd(body)
def list_vnffgds(self, retrieve_all=True, **_params):
return self.legacy_client.list_vnffgds(retrieve_all=retrieve_all,
**_params)
def show_vnffgd(self, vnffgd, **_params):
return self.legacy_client.show_vnffgd(vnffgd, **_params)
def delete_vnffgd(self, vnffgd):
return self.legacy_client.delete_vnffgd(vnffgd)
def list_vnffgs(self, retrieve_all=True, **_params):
return self.legacy_client.list_vnffgs(retrieve_all=retrieve_all,
**_params)
def show_vnffg(self, vnffg, **_params):
return self.legacy_client.show_vnffg(vnffg, **_params)
def create_vnffg(self, body):
return self.legacy_client.create_vnffg(body)
def delete_vnffg(self, vnffg):
return self.legacy_client.delete_vnffg(vnffg)
def update_vnffg(self, vnffg, body):
return self.legacy_client.update_vnffg(vnffg, body)
def list_sfcs(self, retrieve_all=True, **_params):
return self.legacy_client.list_sfcs(retrieve_all=retrieve_all,
**_params)
def show_sfc(self, chain, **_params):
return self.legacy_client.show_sfc(chain, **_params)
def list_nfps(self, retrieve_all=True, **_params):
return self.legacy_client.list_nfps(retrieve_all=retrieve_all,
**_params)
def show_nfp(self, nfp, **_params):
return self.legacy_client.show_nfp(nfp, **_params)
def list_classifiers(self, retrieve_all=True, **_params):
return self.legacy_client.list_classifiers(
retrieve_all=retrieve_all, **_params)
def show_classifier(self, classifier, **_params):
return self.legacy_client.show_classifier(classifier, **_params)
def list_nsds(self, retrieve_all=True, **_params):
return self.legacy_client.list_nsds(retrieve_all=retrieve_all,
**_params)
def show_nsd(self, nsd, **_params):
return self.legacy_client.show_nsd(nsd, **_params)
def create_nsd(self, body):
return self.legacy_client.create_nsd(body)
def delete_nsd(self, nsd):
return self.legacy_client.delete_nsd(nsd)
def list_nss(self, retrieve_all=True, **_params):
return self.legacy_client.list_nss(retrieve_all=retrieve_all,
**_params)
def show_ns(self, ns, **_params):
return self.legacy_client.show_ns(ns, **_params)
def create_ns(self, body):
return self.legacy_client.create_ns(body)
def delete_ns(self, ns):
return self.legacy_client.delete_ns(ns)
def create_cluster(self, body=None):
return self.legacy_client.create_cluster(body=body)
def list_clusters(self, retrieve_all=True, **_params):
return self.legacy_client.list_clusters(retrieve_all=retrieve_all,
**_params)
def show_cluster(self, cluster, **_params):
return self.legacy_client.show_cluster(cluster, **_params)
def delete_cluster(self, cluster):
return self.legacy_client.delete_cluster(cluster)
def create_clustermember(self, body=None):
return self.legacy_client.create_clustermember(body=body)
def list_clustermembers(self, retrieve_all=True, **_params):
return self.legacy_client.list_clustermembers(
retrieve_all=retrieve_all, **_params)
def show_clustermember(self, clustermember, **_params):
return self.legacy_client.show_clustermember(clustermember,
**_params)
def delete_clustermember(self, clustermember):
return self.legacy_client.delete_clustermember(clustermember)
# VnfPackageClient methods
def create_vnf_package(self, body):
return self.vnf_package_client.create_vnf_package(body)
def list_vnf_packages(self, retrieve_all=True, **_params):
return self.vnf_package_client.list_vnf_packages(
retrieve_all=retrieve_all, **_params)
def show_vnf_package(self, vnf_package, **_params):
return self.vnf_package_client.show_vnf_package(vnf_package, **_params)
def upload_vnf_package(self, vnf_package, file_data=None, **_params):
return self.vnf_package_client.upload_vnf_package(
vnf_package, file_data=file_data, **_params)
def delete_vnf_package(self, vnf_package):
return self.vnf_package_client.delete_vnf_package(vnf_package)

View File

@@ -1,16 +1,13 @@
# The order of packages is significant, because pip processes them in the order
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
hacking>=1.1.0,<1.2.0 # Apache-2.0
coverage!=4.4,>=4.0 # Apache-2.0
ddt>=1.0.1 # MIT
fixtures>=3.0.0 # Apache-2.0/BSD
python-subunit>=1.0.0 # Apache-2.0/BSD
sphinx!=1.6.6,>=1.6.2 # BSD
testrepository>=0.0.18 # Apache-2.0/BSD
requests-mock>=1.2.0 # Apache-2.0
stestr>=2.0.0 # Apache-2.0
testtools>=2.2.0 # MIT
oslosphinx>=4.7.0 # Apache-2.0
openstackdocstheme>=1.18.1 # Apache-2.0
# releasenotes
reno>=2.5.0 # Apache-2.0
mock>=2.0.0 # BSD

37
tox.ini
View File

@@ -1,5 +1,5 @@
[tox]
envlist = py35,py27,pypy,pep8
envlist = py37,py36,py27,pep8,docs
minversion = 2.0
skipsdist = True
@@ -11,29 +11,54 @@ setenv = VIRTUAL_ENV={envdir}
usedevelop = True
install_command = pip install {opts} {packages}
deps =
-c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=stable/queens}
-r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
commands = python setup.py testr --testr-args='{posargs}'
-c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/train}
-r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
commands = stestr run --slowest {posargs}
[testenv:pep8]
basepython = python3
commands = flake8
distribute = false
[testenv:venv]
basepython = python3
commands = {posargs}
[testenv:docs]
basepython = python3
deps = -r{toxinidir}/doc/requirements.txt
commands = sphinx-build -W -b html doc/source doc/build/html
[testenv:releasenotes]
basepython = python3
deps =
-c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/train}
-r{toxinidir}/doc/requirements.txt
commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html
[testenv:cover]
commands = python setup.py testr --coverage --testr-args='{posargs}'
basepython = python3
setenv =
PYTHON=coverage run --source tackerclient --parallel-mode
commands =
stestr run {posargs}
coverage combine
coverage html -d cover
coverage xml -o cover/coverage.xml
[flake8]
# E125 continuation line does not distinguish itself from next logical line
ignore = E125
show-source = true
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,tools
# F821 undefined name 'unicode'
# if isinstance(config, str) or isinstance(config, unicode):
builtins = unicode
[testenv:lower-constraints]
basepython = python3
deps =
-c{toxinidir}/lower-constraints.txt
-r{toxinidir}/test-requirements.txt
-r{toxinidir}/requirements.txt