Compare commits

..

99 Commits
0.2.1 ... 0.5.0

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

Change-Id: Ieb5612c966b2110930faac0401f5dabde064ffc1
Partial-Bug: #1603851
2016-07-28 14:06:48 +08:00
Jenkins
e88619e985 Merge "Transition default VIM to API and DB operation" 2016-07-25 18:05:44 +00:00
Jenkins
602c06373d Merge "Remove the mask password logic in vim list and vim show" 2016-07-23 02:31:44 +00:00
Kawaguchi Kentaro
c9ac53a722 Remove the mask password logic in vim list and vim show
In vim list and vim show command,
since hashed password masking on server-side,
the "mask_dict_password" modules are removed on client-side.

Change-Id: I463cb04696dc09157fbbc4b0bd64e66850feac84
Depends-On: Ice5c51b6a66cd27f21c144d3a672cf790e4cec41
Closes-bug: #1594495
2016-07-22 14:03:33 +09:00
Saju Madhavan
00d87bde7e Transition default VIM to API and DB operation
Set the default-vim in the tacker-db and provide
API to manage it.

Change-Id: Ie447f13c1d30e3cf5c1756c424fe60882082c211
Partial-Bug: 1592957
2016-07-20 18:27:39 +05:30
xu-haiwei
9910cbc746 Remove unused import library
TrivialFix

Change-Id: I3f75282a0b9b0493f8d0ead8092cee76824290a8
2016-07-20 01:11:11 +00:00
Jenkins
431a79ba3d Merge "remove unused LOG" 2016-07-15 13:14:13 +00:00
Jenkins
3c6f82dfaf Merge "Display Health status of VIM" 2016-07-11 19:30:22 +00:00
Jenkins
af6db3f107 Merge "Updated from global requirements" 2016-07-11 04:15:41 +00:00
Jenkins
6fd6b66466 Merge "Add .idea/ to python-tackerclient .gitignore" 2016-07-11 04:06:18 +00:00
ji-xuepeng
654ad24e3b remove unused LOG
This is to remove unused LOG to keep code clean

Change-Id: Iba4e29892f3011356ce927f73ce9f418365df609
2016-07-10 14:59:47 +08:00
OpenStack Proposal Bot
b35672be34 Updated from global requirements
Change-Id: Icfa8e264b51ffdf10e6891f40b025c5d61933002
2016-07-09 19:27:25 +00:00
Neeldhwaj Pathak
04298efa2d Add .idea/ to python-tackerclient .gitignore
Change-Id: I03c4403d6256825f6d82e81256b60d03b42c52de
2016-07-08 21:23:13 +05:30
xu-haiwei
a5ad0bc3d3 Fix ext-show command error
Currently ext-show command can not be ran correctly, because
there are duplicated arguments added to command options.

Change-Id: I36fdb3608f9976c633902bba04d61c3cd63b1b39
Closes-bug: #1595036
2016-07-08 01:14:20 +00:00
OpenStack Proposal Bot
f42187050f Updated from global requirements
Change-Id: I155f111afd011f42146b24a7591ad0452cd8ed76
2016-06-30 18:50:03 +00:00
OpenStack Proposal Bot
a4dcd7634a Updated from global requirements
Change-Id: Id797401c8b23fba00ac9c20737b5f51a51c2309f
2016-06-24 08:45:56 +00:00
Jenkins
885b53b6d0 Merge "Change the initial letter to capitals in Tacker CLI help texts" 2016-06-24 04:36:32 +00:00
Kawaguchi Kentaro
805ca417b8 Change the initial letter to capitals in Tacker CLI help texts
Change-Id: I24f87e65d963b5e425e4b49337d05830bcccc130
Closes-bug: #1593661
2016-06-22 12:55:32 +09:00
OpenStack Proposal Bot
ffa34ca3d4 Updated from global requirements
Change-Id: I221812239ce4369374902dc7bdf663dc5c7023c8
2016-06-21 18:05:55 +00:00
Jenkins
8605e2d254 Merge "Add client side support for error_reason" 2016-06-18 13:39:28 +00:00
Bharath Thiruveedula
fc363ee49a Add client side support for error_reason
Change-Id: I9bb58c5648f62a4b8eacc2d5b48ff95869cc8ae8
Partial-Bug: #1524214
Depends-On: I4897e7e4ebdb8c887fb9882e693e5b3d3f4664e5
2016-06-17 14:36:35 +05:30
xu-haiwei
67c034b8a1 Change bind_port to 9890
Since the bind_port of tacker server is changed to 9890, fix the
document and test case to meet this change.

Change-Id: I5baff302517c8bd8b7cc14b10e771beb9079762b
2016-06-10 17:02:53 +09:00
Jenkins
d5ee6c3b50 Merge "Fix incompatible code with python3 for tox pep8 test" 2016-06-08 20:06:40 +00:00
Tin Lam
f48c03db9b Add reno support to python-tackerclient
Added release notes management to python-tackerclient project similar
to other OpenStack projects.  See [1].

[1] http://lists.openstack.org/pipermail/openstack-dev/2015-November/078301.html

Change-Id: Ib7cf5ac01988fd51dab6196b71e1a9a2e9eff190
2016-06-05 01:54:50 -05:00
OpenStack Proposal Bot
51e493e767 Updated from global requirements
Change-Id: I7a00335c5830ca25208489c22de85427ce809a31
2016-06-02 21:12:07 +00:00
OpenStack Proposal Bot
4d0003084c Updated from global requirements
Change-Id: I586795247d4d4fd568739bdaa8c2d2f815ae3e97
2016-05-31 03:06:41 +00:00
Hideki Saito
d47747260f Fix incompatible code with python3 for tox pep8 test
"tox -e pep8" fails when python3 is used.
This patch fixes it.

Closes-Bug: #1583984

Change-Id: Idff17f64d2d4789acee08bd98902ae50312ddc92
2016-05-25 10:23:23 +09:00
Luka Krajger
05ac550251 Capitalize help descriptions
Change-Id: Ie93af370e455990ee2e4fcbb11a9f1dbfeb51aab
2016-05-19 18:03:14 +02:00
OpenStack Proposal Bot
331968496a Updated from global requirements
Change-Id: Ie37ce23c4ea53a2d636ac5875b613ae889a67c26
2016-05-17 14:10:26 +00:00
OpenStack Proposal Bot
af528bec28 Updated from global requirements
Change-Id: I893f2810626b0a616fd6005068d0e4465e97a35d
2016-05-17 03:03:12 +00:00
Bharath Thiruveedula
c32028201f Display Health status of VIM
Change-Id: I59993e2b2187588cc216cba3d05e3da1b4f1cc2c
Closes-Bug: #1554280
Depends-On: I25beaa5e1fd2fca87503c45d720f89d6f8156622
2016-05-12 16:39:52 +05:30
OpenStack Proposal Bot
a6bd47cc80 Updated from global requirements
Change-Id: Ifb080a5ba090f4cec5c53a3ebb8fcf293085eedf
2016-05-06 22:22:54 +00:00
Jenkins
cce904b24b Merge "Fix deprecation warning in tackerclient" 2016-05-02 22:04:38 +00:00
Jenkins
c784737bcf Merge "Updated from global requirements" 2016-05-02 20:22:31 +00:00
Jenkins
bc4b14ad45 Merge "Clean device from help message" 2016-05-02 20:22:25 +00:00
OpenStack Proposal Bot
4312359ae3 Updated from global requirements
Change-Id: I62c040c06269e92fb315b417e3a1bc601a08ed71
2016-04-30 18:08:54 +00:00
dharmendra
f7b11685c4 py3.x: Use six.iteritems for Iterable dict items.
py3.x: Use six.iteritems for Iterable dict items instead of dict.iteritems.

Change-Id: Ibc4dbee779c1da52eba5b9ed34b982103c404e7f
Related-Bug: #1547712
2016-04-22 11:07:39 +05:30
Jenkins
d9a58b3207 Merge "Do not display password in VIM command outputs" 2016-04-21 21:29:05 +00:00
Tin Lam
12e2230b3b Fix deprecation warning in tackerclient
Changed _extract_service_catalog to match neutronclient to remove
deprecation warnings when attr is provided without filter_value to
get_urls().

Change-Id: Ic58060bdf50ce1f3834324372f1a375fbd15beb6
Closes-Bug: #1570641
2016-04-20 20:53:48 -05:00
Sripriya
07444203ec Do not display password in VIM command outputs
This fix masks the password attr in vim response for vim-list,
vim-show and vim-create commands.

Change-Id: I9b38b45f292a11f4615f3dc8eff672a4206c4a4a
Closes-Bug: #1567585
2016-04-18 17:08:23 -07:00
dharmendra
83cfe9306b Clean device from help message
Use VNF instead of device in help message

Change-Id: Ic5ae7bfff836a74d497d9ae997c1bb99c56eac4d
2016-04-13 18:50:26 +05:30
OpenStack Proposal Bot
25337d6367 Updated from global requirements
Change-Id: I708718748495f4406cfe49b7a10e33b2a3f3a50d
2016-04-13 12:48:46 +00:00
OpenStack Proposal Bot
1ea8b9899d Updated from global requirements
Change-Id: I0f2428c71ed3aeea11b2df7418a104d99efa0fbf
2016-04-08 18:42:43 +00:00
Jenkins
27e85a9ae3 Merge "Clean unused code." 2016-04-08 18:09:06 +00:00
dharmendra
6a2df0a918 Clean unused code.
Removing unused class.

Change-Id: Id134e24564cbd2f2e977cd823121e67d9183fc8e
2016-04-07 09:36:28 +00:00
Sridhar Ramaswamy
20d3658353 Cleanup copyright header
Remove personal references in copyright header.

Change-Id: I05a8939f55acdd108276a37131f8988485801fbe
2016-04-04 03:30:14 +00:00
Jenkins
854afc4392 Merge "Cleanup vim settings" 2016-04-01 19:09:51 +00:00
Jenkins
8dd822ae97 Merge "Help message correction" 2016-03-28 22:48:48 +00:00
OpenStack Proposal Bot
82e32687d3 Updated from global requirements
Change-Id: Ia1945ab2020a0ff434a95d2d7ec3aa85fb275510
2016-03-28 19:32:01 +00:00
Martin Oemke
00d3c9771c Cleanup vim settings
This patch cleans up vim settings in .py files.
Settings should be handled in local settings.

Change-Id: Ib7a8c9751d18356070a39798ceedd1e400d979dd
2016-03-28 12:24:24 +02:00
Sripriya
de4aeb2736 Fix attr for vim update
Vim resource attr 'type' does not allow a PUT operation. Updated
the vim args to refelct typeattr for POST operation only. Also
fixed the vim name arg for vnf create command

Change-Id: I3c08945f24343288c2c5614ab4b472d68a1e1d47
2016-03-22 16:26:31 -07:00
Jenkins
28853ac0aa Merge "Cleanup Oslo Incubator code" 2016-03-22 05:27:20 +00:00
Sripriya
5340d09287 Implement client support for multisite VIM
Client side changes for multisite VIM feature including new shell
commands.

Change-Id: Idae463b0cec40bea2edfde5723f7de5077600040
Implements: blueprint multi-site-vim
2016-03-18 20:38:48 -07:00
Ronald Bradford
231478cb3a Cleanup Oslo Incubator code
Graduated to oslo.i18n.
Remove unused libraries.

Implements: blueprint graduate-oslo-i18n [1]
https://blueprints.launchpad.net/oslo-incubator/+spec/graduate-oslo-i18n

Change-Id: I753cf17f5a798afbb868ae400e91f4ccd00879ef
2016-03-18 13:44:00 -04:00
dharmendra
23dc390b4f Help message correction
Help message correction

Change-Id: Iaf29d6c24968c28b1d7271d6363272565efbddb5
2016-03-16 13:31:24 +05:30
Jenkins
639e095ce6 Merge "Clean device related unused path." 2016-03-11 16:27:26 +00:00
Jenkins
a0bf314c0e Merge "Code clean-up in client.py" 2016-03-11 16:25:56 +00:00
Jenkins
d5eebd21d5 Merge "Clean up references to neutron" 2016-03-10 20:11:43 +00:00
dharmendra
8ac96559e3 Clean device related unused path.
Clean device related unused path.

Change-Id: I5ef5be953cda86bdef4092e49026edd6315aa186
2016-03-10 14:40:47 +05:30
Martin Oemke
80220f372b Clean up references to neutron
Remove references to neutron project in tackerclient
doc and conf files.

Change-Id: I66a7479a5c12c49f82af0cbff197d6b9976a87c6
Closes-Bug: #1553715
2016-03-09 09:24:58 +00:00
Jenkins
5e6e59aa46 Merge "Removing "device" CLI from master branch." 2016-03-08 00:57:03 +00:00
dharmendra kushwaha
9c5461b1dd Code clean-up in client.py
Removing duplicate code from client.py

Change-Id: I408915211e005488c8cb08acbd32fdd0501d8c7a
2016-03-07 15:46:58 +00:00
OpenStack Proposal Bot
9f9cc686a3 Updated from global requirements
Change-Id: Ice33c2469020d7b3a147fc7bc05274adfca5673f
2016-03-03 18:06:47 +00:00
dharmendra
863084fd99 Removing "device" CLI from master branch.
Removing "device" CLI from master branch.
Related-Bug: #1543393
Change-Id: Ib93df154138b68a7dd0b2c1f79e6e76e05470ad3
2016-03-03 16:28:30 +05:30
gong yong sheng
633848c21a Fix summary and author in setup.cfg
Change-Id: I5bae09865b4af368efe638fa831d90d2142cb9d0
2016-02-26 13:15:24 +08:00
OpenStack Proposal Bot
77e60c009d Updated from global requirements
Change-Id: I074ca449df2457749c9dcba1961504ab83d1221b
2016-02-10 21:59:31 +00:00
Jenkins
cec2ae1d2d Merge "Clean up flake8 ignore list" 2016-02-09 00:30:43 +00:00
Jenkins
6596d5222f Merge "Updated from global requirements" 2016-01-30 18:27:56 +00:00
Jenkins
03f2605d0a Merge "Code correction while calling base class method" 2016-01-30 15:43:45 +00:00
OpenStack Proposal Bot
4ba764140c Updated from global requirements
Change-Id: I4df706489a46c88d9a9dbf1ff2564a07ef0588eb
2016-01-28 01:31:52 +00:00
Martin Oemke
4878cc999c Clean up flake8 ignore list
Due to update of hacking H302 is not needed any more.

Change-Id: Ibc5b5e11e7021caed19c895786f920c300e2109b
Related-Bug: 1516045
2016-01-27 22:02:03 +01:00
dharmendra
3b96c5f534 Code correction while calling base class method
Correcting the way, while calling base class method
in tackerclient/tacker/v1_0/extension.py

Change-Id: I3d6cb9bade0557e2c157b85b3682bf74b5d617e3
2016-01-22 11:03:19 +05:30
OpenStack Proposal Bot
8ec6af9b6c Updated from global requirements
Change-Id: Ie32b6c2b2bdb3cc23f4d41e1e2df3d7664d19b48
2016-01-22 04:09:19 +00:00
Jenkins
8a64606654 Merge "Remove argparse from requirements" 2016-01-21 23:12:58 +00:00
Jenkins
d9695ad31f Merge "Renamed 'servicevm' in python-tackerclient" 2016-01-21 22:44:45 +00:00
Andreas Jaeger
74f5ac30e7 Remove argparse from requirements
argparse was external in python 2.6 but not anymore, remove it from
requirements.

This should help with pip 8.0 that gets confused in this situation.
Installation of the external argparse is not needed.

Change-Id: Ib7e74912b36c1b5ccb514e31fac35efeff57378d
2016-01-20 19:22:48 +01:00
an.abdulrehman
5903bc9338 Renamed 'servicevm' in python-tackerclient
After renaming the service 'servicevm' and 'advsvc' in install file,
functional tests were failing. Now renamed to 'tacker' in all files of
python-tackerclient repo so that functional test wouldn't fail.

Change-Id: I919d77276af72586a20f50239166325adbf9fb11
Partial-Bug: #1524224
2016-01-07 12:09:02 +00:00
Martin Oemke
ba46bbf7ac Fix H238 errors in tackerclient code
This Patch fixes all H238 errors and enables this test.

[H238]  Old style class declaration,
        use new style (inherit from `object`)

Change-Id: I184538cf6dcb97a178dcf017a21d878712063f91
Related-Bug: 1516045
2015-12-30 11:55:31 +00:00
Jenkins
95c07f50e1 Merge "Put py34 first in the env order of tox" 2015-12-28 19:02:49 +00:00
Jenkins
5f340b9944 Merge "use keystoneclient exceptions instead of oslo-incubator code" 2015-12-28 17:33:15 +00:00
Steve Martinelli
4e33e17e22 use keystoneclient exceptions instead of oslo-incubator code
depending on any oslo-incubator code from another project is
dangerous. keystoneclient makes its exceptions public and it's
not recommended to use any code from
keystoneclient.openstack.common.apiclient since it's maintained
by oslo-incubator.

Change-Id: Ic0c43319eca06ca9dc29c00c5462eebae50bb698
2015-12-27 01:46:54 -05:00
Martin Oemke
0820bb1a10 Fix H405 errors in tackerclient code
This Patch fixes all H405 errors and enables this test.
[H405] Multi line docstring summary not separated with an empty line

Change-Id: I93a9f477f27c259bbfed20ff93105e63d5f6f82c
Related-Bug: 1516045
2015-12-26 01:23:16 +01:00
Janonymous
00eb1118c4 Put py34 first in the env order of tox
To solve the problem of "db type could
not be determined" on py34 we have to run first the py34 env to, then, run
py27. This patch puts py34 first on the tox.ini list of envs to avoid this
problem to happen.

Change-Id: I33ec32c58539db9770252628cc754e0bb0121b28
Closes-bug: #1489059
2015-12-22 18:33:41 +05:30
Martin Oemke
ca1ad7c930 Fix H105 errors in tackerclient code
This Patch fixes all H105 errors and enables this test.

[H105] Don’t use author tags. We use version control instead.

Change-Id: I60e9e844b420b31b8caa86d5338cf04322386506
Related-Bug: 1516045
2015-12-22 00:31:13 +01:00
Jenkins
73a38434fd Merge "Drop py33 support" 2015-12-19 00:52:36 +00:00
Martin Oemke
196ba01bac Fix E265, E129, E113 errors in tackerclient code
This Patch fixes all E265, E129, E113 errors and enables this test for all new patches to tacker.
[E265] block comment should start with '# '
[E129] visually indented line with same indent as next logical line
[E113] unexpected indentation

Change-Id: I708dd9e0c4f3edb0b52a865578aceace726d1c2e
Related-Bug: 1516045
2015-12-18 15:51:26 +01:00
Jenkins
d224c22a46 Merge "Deprecated tox -downloadcache option removed" 2015-12-17 05:39:28 +00:00
OpenStack Proposal Bot
d621916252 Updated from global requirements
Change-Id: I46902630fac296d8af30cfbfbcbbf89c9b080aa5
2015-12-15 18:17:28 +00:00
Jenkins
b8dff342e0 Merge "Remove service instance related stuff and fix unittests" 2015-12-15 17:30:53 +00:00
shu-mutou
b3e5932385 Drop py33 support
"Python 3.3 support is being dropped since OpenStack Liberty."
written in following URL.
https://wiki.openstack.org/wiki/Python3

And already the infra team and the oslo team are dropping py33
support from their projects.

Since we rely on oslo for a lot of our work, and depend on infra
for our CI, we should drop py33 support too.

Change-Id: Ie7cd91f72b6bc80723286187ebe5d0bcb769d7d2
Closes-Bug: #1526170
2015-12-15 18:20:27 +09:00
gong yong sheng
f342ea9c7a Remove service instance related stuff and fix unittests
Change-Id: I8052d9d683a1a2e80d8c160c70d2ea8f8b7240cd
Closes-bug: 1519968
2015-12-15 16:59:36 +08:00
Ondřej Nový
48f1111f8f Deprecated tox -downloadcache option removed
Caching is enabled by default from pip version 6.0

More info:
https://testrun.org/tox/latest/config.html#confval-downloadcache=path
https://pip.pypa.io/en/stable/reference/pip_install/#caching

Change-Id: Ifd9b14f00995371ab3bcce1c8e0efcafe56fe862
2015-12-11 21:26:52 +01:00
OpenStack Proposal Bot
9cc1ebe824 Updated from global requirements
Change-Id: Ic74e675073c69e036dce8d1b9bcf926261de3801
2015-12-08 06:52:18 +00:00
ricolin
ad5381a91d Remove py26 support
As of mitaka, the infra team won't have the resources available to
reasonably test py26, also the oslo team is dropping py26 support
from their libraries. sine we rely on oslo for a lot of our work,
and depend on infra for our CI, we should drop py26 support too.
Closes-Bug: 1519510

Change-Id: Ie8dc40df5fddce298f31f7a3d5436493b9b090bb
2015-12-03 00:04:31 +08:00
Jenkins
5de35c1cf2 Merge "delete interface attach/detach commands" 2015-11-29 07:25:30 +00:00
Jenkins
42a1a923b9 Merge "Update requirements according to global requirements" 2015-11-26 07:31:47 +00:00
yong sheng gong
6242db9fb1 Update requirements according to global requirements
This is important because if we install tacker along with
other openstack components, this inconsistency will break
system.

Due to the upgrade of hacking, current codes violate many
new rules. These violations will be fixed in follow up
patches.

Author:    yong sheng gong <gong.yongsheng@99cloud.net>
Change-Id: Ib30d31ca804aebf43c02eb8247c718ab00629f6f
Closes-bug: 1516045
2015-11-16 17:02:47 +08:00
KLuka
83db572f62 Added command to display VNFD's template
Change-Id: I1e1a1674680e9f7c03e519b85930b8e51478b011
2015-11-12 18:16:39 +01:00
gong yong sheng
f0aa619ffe delete interface attach/detach commands
These two commands are not valid since the API are removed
on server side.

Change-Id: I17beed8fd7bd275dd187918a0a8f5b4a32aab902
Closes-bug: 1515526
2015-11-12 17:19:01 +08:00
50 changed files with 1164 additions and 1765 deletions

4
.gitignore vendored
View File

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

View File

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

View File

@@ -15,7 +15,7 @@ In order to use the CLI, you must provide your OpenStack username, password, ten
The command line tool will attempt to reauthenticate using your provided credentials for every request. You can override this behavior by manually supplying an auth token using ``--os-url`` and ``--os-auth-token``. You can alternatively set these environment variables::
export OS_URL=http://tacker.example.org:8888/
export OS_URL=http://tacker.example.org:9890/
export OS_TOKEN=3bcc3d3a03f44e3d8377f9247b0ad155
If tacker server does not require authentication, besides these two arguments or environment variables (We can use any value as token.), we need manually supply ``--os-auth-strategy`` or set the environment variable::

View File

@@ -1,7 +0,0 @@
[DEFAULT]
# The list of modules to copy from openstack-common
modules=gettextutils,jsonutils,strutils,timeutils
# The base module to hold the copy of openstack.common
base=tackerclient

261
releasenotes/source/conf.py Normal file
View File

@@ -0,0 +1,261 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# tacker client documentation build configuration file, created by
# sphinx-quickstart on Tue May 31 19:07:30 2016.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
#
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
# sys.path.insert(0, os.path.abspath('.'))
# -- General configuration -------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = [
'oslosphinx',
'reno.sphinxext'
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
# source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'Tacker Client Release Notes'
copyright = u'2016, Tacker Developers'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
import pbr.version
tacker_client_version = pbr.version.VersionInfo('python-tackerclient')
release = tacker_client_version.version_string_with_vcs()
version = tacker_client_version.canonical_version_string()
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
# language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
# today = ''
# Else, today_fmt is used as the format for a strftime call.
# today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = []
# The reST default role (used for this markup: `text`) to
# use for all documents.
# default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
# add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
# add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
# modindex_common_prefix = []
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'default'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
# html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
# html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
# html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
# html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
# html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
# html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
# html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
# html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
# html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
# html_additional_pages = {}
# If false, no module index is generated.
# html_domain_indices = True
# If false, no index is generated.
# html_use_index = True
# If true, the index is split into individual pages for each letter.
# html_split_index = False
# If true, links to the reST sources are added to the pages.
# html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
# html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
# html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
# html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
# html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'tackerclientdoc'
# -- Options for LaTeX output ------------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
# 'preamble': '',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass
# [howto/manual]).
latex_documents = [
('index', 'TackerClientReleaseNotes.tex',
u'Tacker Client Release Notes Documentation',
u'Tacker Developers', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
# latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
# latex_use_parts = False
# If true, show page references after internal links.
# latex_show_pagerefs = False
# If true, show URL addresses after external links.
# latex_show_urls = False
# Documents to append as an appendix to all manuals.
# latex_appendices = []
# If false, no module index is generated.
# latex_domain_indices = True
# -- Options for manual page output ------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'tackerreleasenotes',
u'Tacker Client Release Notes Documentation',
[u'Tacker Developers'], 1)
]
# If true, show URL addresses after external links.
# man_show_urls = False
# -- Options for Texinfo output ----------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'TackerClientReleaseNotes',
u'Tacker Client Release Notes Documentation',
u'Tacker Developers', 'TackerClientReleaseNotes',
'Tacker Client Project.',
'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.
# texinfo_appendices = []
# If false, no module index is generated.
# texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
# texinfo_show_urls = 'footnote'

View File

@@ -0,0 +1,9 @@
Python-TackerClient Release Notes
=================================
Contents:
.. toctree::
:maxdepth: 2
unreleased

View File

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

View File

@@ -1,13 +1,16 @@
# The order of packages is significant, because pip processes them in the order
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
pbr>=1.6
argparse
cliff>=1.14.0 # Apache-2.0
iso8601>=0.1.9
netaddr>=0.7.12,!=0.7.16
requests>=2.5.2,!=2.8.0,!=2.9.0
python-keystoneclient>=1.6.0,!=1.8.0
simplejson>=2.2.0
six>=1.9.0
Babel>=1.3
pbr>=1.6 # Apache-2.0
cliff!=1.16.0,!=1.17.0,>=1.15.0 # Apache-2.0
iso8601>=0.1.11 # MIT
netaddr!=0.7.16,>=0.7.12 # BSD
requests>=2.10.0 # Apache-2.0
python-keystoneclient!=1.8.0,!=2.1.0,>=1.7.0 # Apache-2.0
simplejson>=2.2.0 # MIT
six>=1.9.0 # MIT
Babel>=2.3.4 # BSD
oslo.i18n>=2.1.0 # Apache-2.0
oslo.utils>=3.15.0 # Apache-2.0
oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0

View File

@@ -1,6 +1,6 @@
[metadata]
name = python-tackerclient
summary = CLI and Client Library for OpenStack Tacker
summary = CLI and Client Library for OpenStack Tacker
description-file =
README.rst
author = OpenStack
@@ -16,7 +16,6 @@ classifier =
Programming Language :: Python
Programming Language :: Python :: 2
Programming Language :: Python :: 2.7
Programming Language :: Python :: 2.6
[files]
packages =
@@ -35,5 +34,10 @@ all_files = 1
build-dir = doc/build
source-dir = doc/source
[build_releasenotes]
all_files = 1
build-dir = releasenotes/build
source-dir = releasenotes/source
[wheel]
universal = 1

View File

@@ -56,7 +56,7 @@ class HTTPClient(object):
endpoint_url=None, insecure=False,
endpoint_type='publicURL',
auth_strategy='keystone', ca_cert=None, log_credentials=False,
service_type='servicevm',
service_type='nfv-orchestration',
**kwargs):
self.username = username
@@ -171,8 +171,6 @@ class HTTPClient(object):
return resp, body
except exceptions.Unauthorized:
self.authenticate()
kwargs.setdefault('headers', {})
kwargs['headers']['X-Auth-Token'] = self.auth_token
resp, body = self._cs_request(
self.endpoint_url + url, method, **kwargs)
return resp, body
@@ -187,7 +185,7 @@ class HTTPClient(object):
if not self.endpoint_url:
self.endpoint_url = self.service_catalog.url_for(
attr='region', filter_value=self.region_name,
region_name=self.region_name,
service_type=self.service_type,
endpoint_type=self.endpoint_type)
@@ -256,7 +254,7 @@ class HTTPClient(object):
body = json.loads(body)
for endpoint in body.get('endpoints', []):
if (endpoint['type'] == 'servicevm' and
if (endpoint['type'] == 'nfv-orchestration' and
endpoint.get('region') == self.region_name):
if self.endpoint_type not in endpoint:
raise exceptions.EndpointTypeNotFound(
@@ -359,7 +357,7 @@ def construct_http_client(username=None,
log_credentials=None,
auth_strategy='keystone',
ca_cert=None,
service_type='servicevm',
service_type='nfv-orchestration',
session=None,
**kwargs):

View File

@@ -0,0 +1,50 @@
# Copyright 2016 OpenStack Foundation
#
# 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.
"""oslo.i18n integration module.
See http://docs.openstack.org/developer/oslo.i18n/usage.html .
"""
import oslo_i18n
DOMAIN = "tackerclient"
_translators = oslo_i18n.TranslatorFactory(domain=DOMAIN)
# The primary translation function using the well-known name "_"
_ = _translators.primary
# The contextual translation function using the name "_C"
# requires oslo.i18n >=2.1.0
_C = _translators.contextual_form
# The plural translation function using the name "_P"
# requires oslo.i18n >=2.1.0
_P = _translators.plural_form
# Translators for log levels.
#
# The abbreviated names are meant to reflect the usual use of a short
# name like '_'. The "L" is for "log" and the other letter comes from
# the level.
_LI = _translators.log_info
_LW = _translators.log_warning
_LE = _translators.log_error
_LC = _translators.log_critical
def get_available_languages():
return oslo_i18n.get_available_languages(DOMAIN)

View File

@@ -17,15 +17,10 @@
"""Manage access to the clients, including authenticating when needed.
"""
import logging
from tackerclient import client
from tackerclient.tacker import client as tacker_client
LOG = logging.getLogger(__name__)
class ClientCache(object):
"""Descriptor class for caching created client handles."""

View File

@@ -148,12 +148,6 @@ class OverQuotaClient(Conflict):
pass
# TODO(amotoki): It is unused in Tacker, but it is referred to
# in Horizon code. After Horizon code is updated, remove it.
class AlreadyAttachedClient(Conflict):
pass
class IpAddressGenerationFailureClient(Conflict):
pass
@@ -221,7 +215,9 @@ class CommandError(TackerCLIError):
class UnsupportedVersion(TackerCLIError):
"""Indicates that the user is trying to use an unsupported version of
"""Unsupported Version.
Indicates that the user is trying to use an unsupported version of
the API.
"""
pass

View File

@@ -18,14 +18,13 @@
"""Utilities and helper functions."""
import argparse
import functools
import logging
import os
import warnings
from oslo_utils import encodeutils
from oslo_utils import importutils
import six
import six.moves.urllib.parse as urlparse
from tackerclient.common import exceptions
from tackerclient.i18n import _
@@ -175,14 +174,8 @@ def add_boolean_argument(parser, name, **kwargs):
**kwargs)
def deprecated(name):
"""This decorator can be used to mark a call as deprecated."""
def _deprecate(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
warnings.simplefilter('always', DeprecationWarning)
warnings.warn("Call to %s deprecated, avoid its usage." % name,
category=DeprecationWarning)
return func(*args, **kwargs)
return wrapper
return _deprecate
def validate_url(url):
url_parts = urlparse.urlparse(url)
if not url_parts.scheme or not url_parts.netloc or not url_parts.port:
raise exceptions.TackerClientException(message='Invalid URL')
return url_parts

View File

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

View File

@@ -1,320 +0,0 @@
# Copyright 2012 Red Hat, Inc.
# Copyright 2013 IBM Corp.
# 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.
"""
gettext for openstack-common modules.
Usual usage in an openstack.common module:
from tackerclient.openstack.common.gettextutils import _
"""
import copy
import gettext
import logging
import os
import re
import UserString
from babel import localedata
import six
_localedir = os.environ.get('tackerclient'.upper() + '_LOCALEDIR')
_t = gettext.translation('tackerclient', localedir=_localedir, fallback=True)
_AVAILABLE_LANGUAGES = {}
USE_LAZY = False
def enable_lazy():
"""Convenience function for configuring _() to use lazy gettext
Call this at the start of execution to enable the gettextutils._
function to use lazy gettext functionality. This is useful if
your project is importing _ directly instead of using the
gettextutils.install() way of importing the _ function.
"""
global USE_LAZY
USE_LAZY = True
def _(msg):
if USE_LAZY:
return Message(msg, 'tackerclient')
else:
return _t.ugettext(msg)
def install(domain, lazy=False):
"""Install a _() function using the given translation domain.
Given a translation domain, install a _() function using gettext's
install() function.
The main difference from gettext.install() is that we allow
overriding the default localedir (e.g. /usr/share/locale) using
a translation-domain-specific environment variable (e.g.
NOVA_LOCALEDIR).
:param domain: the translation domain
:param lazy: indicates whether or not to install the lazy _() function.
The lazy _() introduces a way to do deferred translation
of messages by installing a _ that builds Message objects,
instead of strings, which can then be lazily translated into
any available locale.
"""
if lazy:
# NOTE(mrodden): Lazy gettext functionality.
#
# The following introduces a deferred way to do translations on
# messages in OpenStack. We override the standard _() function
# and % (format string) operation to build Message objects that can
# later be translated when we have more information.
#
# Also included below is an example LocaleHandler that translates
# Messages to an associated locale, effectively allowing many logs,
# each with their own locale.
def _lazy_gettext(msg):
"""Create and return a Message object.
Lazy gettext function for a given domain, it is a factory method
for a project/module to get a lazy gettext function for its own
translation domain (i.e. nova, glance, cinder, etc.)
Message encapsulates a string so that we can translate
it later when needed.
"""
return Message(msg, domain)
import __builtin__
__builtin__.__dict__['_'] = _lazy_gettext
else:
localedir = '%s_LOCALEDIR' % domain.upper()
gettext.install(domain,
localedir=os.environ.get(localedir),
unicode=True)
class Message(UserString.UserString, object):
"""Class used to encapsulate translatable messages."""
def __init__(self, msg, domain):
# _msg is the gettext msgid and should never change
self._msg = msg
self._left_extra_msg = ''
self._right_extra_msg = ''
self.params = None
self.locale = None
self.domain = domain
@property
def data(self):
# NOTE(mrodden): this should always resolve to a unicode string
# that best represents the state of the message currently
localedir = os.environ.get(self.domain.upper() + '_LOCALEDIR')
if self.locale:
lang = gettext.translation(self.domain,
localedir=localedir,
languages=[self.locale],
fallback=True)
else:
# use system locale for translations
lang = gettext.translation(self.domain,
localedir=localedir,
fallback=True)
full_msg = (self._left_extra_msg +
lang.ugettext(self._msg) +
self._right_extra_msg)
if self.params is not None:
full_msg = full_msg % self.params
return six.text_type(full_msg)
def _save_dictionary_parameter(self, dict_param):
full_msg = self.data
# look for %(blah) fields in string;
# ignore %% and deal with the
# case where % is first character on the line
keys = re.findall('(?:[^%]|^)?%\((\w*)\)[a-z]', full_msg)
# if we don't find any %(blah) blocks but have a %s
if not keys and re.findall('(?:[^%]|^)%[a-z]', full_msg):
# apparently the full dictionary is the parameter
params = copy.deepcopy(dict_param)
else:
params = {}
for key in keys:
try:
params[key] = copy.deepcopy(dict_param[key])
except TypeError:
# cast uncopyable thing to unicode string
params[key] = unicode(dict_param[key])
return params
def _save_parameters(self, other):
# we check for None later to see if
# we actually have parameters to inject,
# so encapsulate if our parameter is actually None
if other is None:
self.params = (other, )
elif isinstance(other, dict):
self.params = self._save_dictionary_parameter(other)
else:
# fallback to casting to unicode,
# this will handle the problematic python code-like
# objects that cannot be deep-copied
try:
self.params = copy.deepcopy(other)
except TypeError:
self.params = unicode(other)
return self
# overrides to be more string-like
def __unicode__(self):
return self.data
def __str__(self):
return self.data.encode('utf-8')
def __getstate__(self):
to_copy = ['_msg', '_right_extra_msg', '_left_extra_msg',
'domain', 'params', 'locale']
new_dict = self.__dict__.fromkeys(to_copy)
for attr in to_copy:
new_dict[attr] = copy.deepcopy(self.__dict__[attr])
return new_dict
def __setstate__(self, state):
for (k, v) in state.items():
setattr(self, k, v)
# operator overloads
def __add__(self, other):
copied = copy.deepcopy(self)
copied._right_extra_msg += other.__str__()
return copied
def __radd__(self, other):
copied = copy.deepcopy(self)
copied._left_extra_msg += other.__str__()
return copied
def __mod__(self, other):
# do a format string to catch and raise
# any possible KeyErrors from missing parameters
self.data % other
copied = copy.deepcopy(self)
return copied._save_parameters(other)
def __mul__(self, other):
return self.data * other
def __rmul__(self, other):
return other * self.data
def __getitem__(self, key):
return self.data[key]
def __getslice__(self, start, end):
return self.data.__getslice__(start, end)
def __getattribute__(self, name):
# NOTE(mrodden): handle lossy operations that we can't deal with yet
# These override the UserString implementation, since UserString
# uses our __class__ attribute to try and build a new message
# after running the inner data string through the operation.
# At that point, we have lost the gettext message id and can just
# safely resolve to a string instead.
ops = ['capitalize', 'center', 'decode', 'encode',
'expandtabs', 'ljust', 'lstrip', 'replace', 'rjust', 'rstrip',
'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
if name in ops:
return getattr(self.data, name)
else:
return UserString.UserString.__getattribute__(self, name)
def get_available_languages(domain):
"""Lists the available languages for the given translation domain.
:param domain: the domain to get languages for
"""
if domain in _AVAILABLE_LANGUAGES:
return copy.copy(_AVAILABLE_LANGUAGES[domain])
localedir = '%s_LOCALEDIR' % domain.upper()
find = lambda x: gettext.find(domain,
localedir=os.environ.get(localedir),
languages=[x])
# NOTE(mrodden): en_US should always be available (and first in case
# order matters) since our in-line message strings are en_US
language_list = ['en_US']
# NOTE(luisg): Babel <1.0 used a function called list(), which was
# renamed to locale_identifiers() in >=1.0, the requirements master list
# requires >=0.9.6, uncapped, so defensively work with both. We can remove
# this check when the master list updates to >=1.0, and all projects udpate
list_identifiers = (getattr(localedata, 'list', None) or
getattr(localedata, 'locale_identifiers'))
locale_identifiers = list_identifiers()
for i in locale_identifiers:
if find(i) is not None:
language_list.append(i)
_AVAILABLE_LANGUAGES[domain] = language_list
return copy.copy(language_list)
def get_localized_message(message, user_locale):
"""Gets a localized version of the given message in the given locale."""
if isinstance(message, Message):
if user_locale:
message.locale = user_locale
return unicode(message)
else:
return message
class LocaleHandler(logging.Handler):
"""Handler that can have a locale associated to translate Messages.
A quick example of how to utilize the Message class above.
LocaleHandler takes a locale and a target logging.Handler object
to forward LogRecord objects to after translating the internal Message.
"""
def __init__(self, locale, target):
"""Initialize a LocaleHandler
:param locale: locale to use for translating messages
:param target: logging.Handler object to forward
LogRecord objects to after translation
"""
logging.Handler.__init__(self)
self.locale = locale
self.target = target
def emit(self, record):
if isinstance(record.msg, Message):
# set the locale and resolve to a string
record.msg.locale = self.locale
self.target.emit(record)

View File

@@ -1,67 +0,0 @@
# Copyright 2011 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 related utilities and helper functions.
"""
import sys
import traceback
from tackerclient.openstack.common.gettextutils import _
def import_class(import_str):
"""Returns a class from a string including module and class."""
mod_str, _sep, class_str = import_str.rpartition('.')
try:
__import__(mod_str)
return getattr(sys.modules[mod_str], class_str)
except (ValueError, AttributeError):
raise ImportError(_('Class %s cannot be found (%s)') %
(class_str,
traceback.format_exception(*sys.exc_info())))
def import_object(import_str, *args, **kwargs):
"""Import a class and return an instance of it."""
return import_class(import_str)(*args, **kwargs)
def import_object_ns(name_space, import_str, *args, **kwargs):
"""Tries to import object from default namespace.
Imports a class and return an instance of it, first by trying
to find the class in a default namespace, then failing back to
a full path if not found in the default namespace.
"""
import_value = "%s.%s" % (name_space, import_str)
try:
return import_class(import_value)(*args, **kwargs)
except ImportError:
return import_class(import_str)(*args, **kwargs)
def import_module(import_str):
"""Import a module."""
__import__(import_str)
return sys.modules[import_str]
def try_import(import_str, default=None):
"""Try to import a module and if it fails return default."""
try:
return import_module(import_str)
except ImportError:
return default

View File

@@ -1,186 +0,0 @@
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# Copyright 2011 Justin Santa Barbara
# 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.
'''
JSON related utilities.
This module provides a few things:
1) A handy function for getting an object down to something that can be
JSON serialized. See to_primitive().
2) Wrappers around loads() and dumps(). The dumps() wrapper will
automatically use to_primitive() for you if needed.
3) This sets up anyjson to use the loads() and dumps() wrappers if anyjson
is available.
'''
import codecs
import datetime
import functools
import inspect
import itertools
import sys
if sys.version_info < (2, 7):
# On Python <= 2.6, json module is not C boosted, so try to use
# simplejson module if available
try:
import simplejson as json
except ImportError:
import json
else:
import json
import six
import six.moves.xmlrpc_client as xmlrpclib
from tackerclient.openstack.common import gettextutils
from tackerclient.openstack.common import importutils
from tackerclient.openstack.common import strutils
from tackerclient.openstack.common import timeutils
netaddr = importutils.try_import("netaddr")
_nasty_type_tests = [inspect.ismodule, inspect.isclass, inspect.ismethod,
inspect.isfunction, inspect.isgeneratorfunction,
inspect.isgenerator, inspect.istraceback, inspect.isframe,
inspect.iscode, inspect.isbuiltin, inspect.isroutine,
inspect.isabstract]
_simple_types = (six.string_types + six.integer_types
+ (type(None), bool, float))
def to_primitive(value, convert_instances=False, convert_datetime=True,
level=0, max_depth=3):
"""Convert a complex object into primitives.
Handy for JSON serialization. We can optionally handle instances,
but since this is a recursive function, we could have cyclical
data structures.
To handle cyclical data structures we could track the actual objects
visited in a set, but not all objects are hashable. Instead we just
track the depth of the object inspections and don't go too deep.
Therefore, convert_instances=True is lossy ... be aware.
"""
# handle obvious types first - order of basic types determined by running
# full tests on nova project, resulting in the following counts:
# 572754 <type 'NoneType'>
# 460353 <type 'int'>
# 379632 <type 'unicode'>
# 274610 <type 'str'>
# 199918 <type 'dict'>
# 114200 <type 'datetime.datetime'>
# 51817 <type 'bool'>
# 26164 <type 'list'>
# 6491 <type 'float'>
# 283 <type 'tuple'>
# 19 <type 'long'>
if isinstance(value, _simple_types):
return value
if isinstance(value, datetime.datetime):
if convert_datetime:
return timeutils.strtime(value)
else:
return value
# value of itertools.count doesn't get caught by nasty_type_tests
# and results in infinite loop when list(value) is called.
if type(value) == itertools.count:
return six.text_type(value)
# FIXME(vish): Workaround for LP bug 852095. Without this workaround,
# tests that raise an exception in a mocked method that
# has a @wrap_exception with a notifier will fail. If
# we up the dependency to 0.5.4 (when it is released) we
# can remove this workaround.
if getattr(value, '__module__', None) == 'mox':
return 'mock'
if level > max_depth:
return '?'
# The try block may not be necessary after the class check above,
# but just in case ...
try:
recursive = functools.partial(to_primitive,
convert_instances=convert_instances,
convert_datetime=convert_datetime,
level=level,
max_depth=max_depth)
if isinstance(value, dict):
return dict((k, recursive(v)) for k, v in six.iteritems(value))
elif isinstance(value, (list, tuple)):
return [recursive(lv) for lv in value]
# It's not clear why xmlrpclib created their own DateTime type, but
# for our purposes, make it a datetime type which is explicitly
# handled
if isinstance(value, xmlrpclib.DateTime):
value = datetime.datetime(*tuple(value.timetuple())[:6])
if convert_datetime and isinstance(value, datetime.datetime):
return timeutils.strtime(value)
elif isinstance(value, gettextutils.Message):
return value.data
elif hasattr(value, 'iteritems'):
return recursive(dict(value.iteritems()), level=level + 1)
elif hasattr(value, '__iter__'):
return recursive(list(value))
elif convert_instances and hasattr(value, '__dict__'):
# Likely an instance of something. Watch for cycles.
# Ignore class member vars.
return recursive(value.__dict__, level=level + 1)
elif netaddr and isinstance(value, netaddr.IPAddress):
return six.text_type(value)
else:
if any(test(value) for test in _nasty_type_tests):
return six.text_type(value)
return value
except TypeError:
# Class objects are tricky since they may define something like
# __iter__ defined but it isn't callable as list().
return six.text_type(value)
def dumps(value, default=to_primitive, **kwargs):
return json.dumps(value, default=default, **kwargs)
def loads(s, encoding='utf-8'):
return json.loads(strutils.safe_decode(s, encoding))
def load(fp, encoding='utf-8'):
return json.load(codecs.getreader(encoding)(fp))
try:
import anyjson
except ImportError:
pass
else:
anyjson._modules.append((__name__, 'dumps', TypeError,
'loads', ValueError, 'load'))
anyjson.force_implementation(__name__)

View File

@@ -1,216 +0,0 @@
# Copyright 2011 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.
"""
System-level utilities and helper functions.
"""
import re
import sys
import unicodedata
import six
from tackerclient.openstack.common.gettextutils import _ # noqa
# Used for looking up extensions of text
# to their 'multiplied' byte amount
BYTE_MULTIPLIERS = {
'': 1,
't': 1024 ** 4,
'g': 1024 ** 3,
'm': 1024 ** 2,
'k': 1024,
}
BYTE_REGEX = re.compile(r'(^-?\d+)(\D*)')
TRUE_STRINGS = ('1', 't', 'true', 'on', 'y', 'yes')
FALSE_STRINGS = ('0', 'f', 'false', 'off', 'n', 'no')
SLUGIFY_STRIP_RE = re.compile(r"[^\w\s-]")
SLUGIFY_HYPHENATE_RE = re.compile(r"[-\s]+")
def int_from_bool_as_string(subject):
"""Interpret a string as a boolean and return either 1 or 0.
Any string value in:
('True', 'true', 'On', 'on', '1')
is interpreted as a boolean True.
Useful for JSON-decoded stuff and config file parsing
"""
return bool_from_string(subject) and 1 or 0
def bool_from_string(subject, strict=False):
"""Interpret a string as a boolean.
A case-insensitive match is performed such that strings matching 't',
'true', 'on', 'y', 'yes', or '1' are considered True and, when
`strict=False`, anything else is considered False.
Useful for JSON-decoded stuff and config file parsing.
If `strict=True`, unrecognized values, including None, will raise a
ValueError which is useful when parsing values passed in from an API call.
Strings yielding False are 'f', 'false', 'off', 'n', 'no', or '0'.
"""
if not isinstance(subject, six.string_types):
subject = str(subject)
lowered = subject.strip().lower()
if lowered in TRUE_STRINGS:
return True
elif lowered in FALSE_STRINGS:
return False
elif strict:
acceptable = ', '.join(
"'%s'" % s for s in sorted(TRUE_STRINGS + FALSE_STRINGS))
msg = _("Unrecognized value '%(val)s', acceptable values are:"
" %(acceptable)s") % {'val': subject,
'acceptable': acceptable}
raise ValueError(msg)
else:
return False
def safe_decode(text, incoming=None, errors='strict'):
"""Decodes incoming str using `incoming` if they're not already unicode.
:param incoming: Text's current encoding
:param errors: Errors handling policy. See here for valid
values http://docs.python.org/2/library/codecs.html
:returns: text or a unicode `incoming` encoded
representation of it.
:raises TypeError: If text is not an isntance of str
"""
if not isinstance(text, six.string_types):
raise TypeError("%s can't be decoded" % type(text))
if isinstance(text, six.text_type):
return text
if not incoming:
incoming = (sys.stdin.encoding or
sys.getdefaultencoding())
try:
return text.decode(incoming, errors)
except UnicodeDecodeError:
# Note(flaper87) If we get here, it means that
# sys.stdin.encoding / sys.getdefaultencoding
# didn't return a suitable encoding to decode
# text. This happens mostly when global LANG
# var is not set correctly and there's no
# default encoding. In this case, most likely
# python will use ASCII or ANSI encoders as
# default encodings but they won't be capable
# of decoding non-ASCII characters.
#
# Also, UTF-8 is being used since it's an ASCII
# extension.
return text.decode('utf-8', errors)
def safe_encode(text, incoming=None,
encoding='utf-8', errors='strict'):
"""Encodes incoming str/unicode using `encoding`.
If incoming is not specified, text is expected to be encoded with
current python's default encoding. (`sys.getdefaultencoding`)
:param incoming: Text's current encoding
:param encoding: Expected encoding for text (Default UTF-8)
:param errors: Errors handling policy. See here for valid
values http://docs.python.org/2/library/codecs.html
:returns: text or a bytestring `encoding` encoded
representation of it.
:raises TypeError: If text is not an isntance of str
"""
if not isinstance(text, six.string_types):
raise TypeError(_("%s can't be encoded") % type(text).capitalize())
if not incoming:
incoming = (sys.stdin.encoding or
sys.getdefaultencoding())
if isinstance(text, six.text_type):
return text.encode(encoding, errors)
elif text and encoding != incoming:
# Decode text before encoding it with `encoding`
text = safe_decode(text, incoming, errors)
return text.encode(encoding, errors)
return text
def to_bytes(text, default=0):
"""Converts a string into an integer of bytes.
Looks at the last characters of the text to determine
what conversion is needed to turn the input text into a byte number.
Supports "B, K(B), M(B), G(B), and T(B)". (case insensitive)
:param text: String input for bytes size conversion.
:param default: Default return value when text is blank.
"""
match = BYTE_REGEX.search(text)
if match:
magnitude = int(match.group(1))
mult_key_org = match.group(2)
if not mult_key_org:
return magnitude
elif text:
msg = _('Invalid string format: %s') % text
raise TypeError(msg)
else:
return default
mult_key = mult_key_org.lower().replace('b', '', 1)
multiplier = BYTE_MULTIPLIERS.get(mult_key)
if multiplier is None:
msg = _('Unknown byte multiplier: %s') % mult_key_org
raise TypeError(msg)
return magnitude * multiplier
def to_slug(value, incoming=None, errors="strict"):
"""Normalize string.
Convert to lowercase, remove non-word characters, and convert spaces
to hyphens.
Inspired by Django's `slugify` filter.
:param value: Text to slugify
:param incoming: Text's current encoding
:param errors: Errors handling policy. See here for valid
values http://docs.python.org/2/library/codecs.html
:returns: slugified unicode representation of `value`
:raises TypeError: If text is not an instance of str
"""
value = safe_decode(value, incoming, errors)
# NOTE(aababilov): no need to use safe_(encode|decode) here:
# encodings are always "ascii", error handling is always "ignore"
# and types are always known (first: unicode; second: str)
value = unicodedata.normalize("NFKD", value).encode(
"ascii", "ignore").decode("ascii")
value = SLUGIFY_STRIP_RE.sub("", value).strip().lower()
return SLUGIFY_HYPHENATE_RE.sub("-", value)

View File

@@ -1,186 +0,0 @@
# Copyright 2011 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.
"""
Time related utilities and helper functions.
"""
import calendar
import datetime
import iso8601
import six
# ISO 8601 extended time format with microseconds
_ISO8601_TIME_FORMAT_SUBSECOND = '%Y-%m-%dT%H:%M:%S.%f'
_ISO8601_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S'
PERFECT_TIME_FORMAT = _ISO8601_TIME_FORMAT_SUBSECOND
def isotime(at=None, subsecond=False):
"""Stringify time in ISO 8601 format."""
if not at:
at = utcnow()
st = at.strftime(_ISO8601_TIME_FORMAT
if not subsecond
else _ISO8601_TIME_FORMAT_SUBSECOND)
tz = at.tzinfo.tzname(None) if at.tzinfo else 'UTC'
st += ('Z' if tz == 'UTC' else tz)
return st
def parse_isotime(timestr):
"""Parse time from ISO 8601 format."""
try:
return iso8601.parse_date(timestr)
except iso8601.ParseError as e:
raise ValueError(unicode(e))
except TypeError as e:
raise ValueError(unicode(e))
def strtime(at=None, fmt=PERFECT_TIME_FORMAT):
"""Returns formatted utcnow."""
if not at:
at = utcnow()
return at.strftime(fmt)
def parse_strtime(timestr, fmt=PERFECT_TIME_FORMAT):
"""Turn a formatted time back into a datetime."""
return datetime.datetime.strptime(timestr, fmt)
def normalize_time(timestamp):
"""Normalize time in arbitrary timezone to UTC naive object."""
offset = timestamp.utcoffset()
if offset is None:
return timestamp
return timestamp.replace(tzinfo=None) - offset
def is_older_than(before, seconds):
"""Return True if before is older than seconds."""
if isinstance(before, six.string_types):
before = parse_strtime(before).replace(tzinfo=None)
return utcnow() - before > datetime.timedelta(seconds=seconds)
def is_newer_than(after, seconds):
"""Return True if after is newer than seconds."""
if isinstance(after, six.string_types):
after = parse_strtime(after).replace(tzinfo=None)
return after - utcnow() > datetime.timedelta(seconds=seconds)
def utcnow_ts():
"""Timestamp version of our utcnow function."""
return calendar.timegm(utcnow().timetuple())
def utcnow():
"""Overridable version of utils.utcnow."""
if utcnow.override_time:
try:
return utcnow.override_time.pop(0)
except AttributeError:
return utcnow.override_time
return datetime.datetime.utcnow()
def iso8601_from_timestamp(timestamp):
"""Returns a iso8601 formated date from timestamp."""
return isotime(datetime.datetime.utcfromtimestamp(timestamp))
utcnow.override_time = None
def set_time_override(override_time=datetime.datetime.utcnow()):
"""Overrides utils.utcnow.
Make it return a constant time or a list thereof, one at a time.
"""
utcnow.override_time = override_time
def advance_time_delta(timedelta):
"""Advance overridden time using a datetime.timedelta."""
assert(not utcnow.override_time is None)
try:
for dt in utcnow.override_time:
dt += timedelta
except TypeError:
utcnow.override_time += timedelta
def advance_time_seconds(seconds):
"""Advance overridden time by seconds."""
advance_time_delta(datetime.timedelta(0, seconds))
def clear_time_override():
"""Remove the overridden time."""
utcnow.override_time = None
def marshall_now(now=None):
"""Make an rpc-safe datetime with microseconds.
Note: tzinfo is stripped, but not required for relative times.
"""
if not now:
now = utcnow()
return dict(day=now.day, month=now.month, year=now.year, hour=now.hour,
minute=now.minute, second=now.second,
microsecond=now.microsecond)
def unmarshall_time(tyme):
"""Unmarshall a datetime dict."""
return datetime.datetime(day=tyme['day'],
month=tyme['month'],
year=tyme['year'],
hour=tyme['hour'],
minute=tyme['minute'],
second=tyme['second'],
microsecond=tyme['microsecond'])
def delta_seconds(before, after):
"""Return the difference between two timing objects.
Compute the difference in seconds between two date, time, or
datetime objects (as a float, to microsecond resolution).
"""
delta = after - before
try:
return delta.total_seconds()
except AttributeError:
return ((delta.days * 24 * 3600) + delta.seconds +
float(delta.microseconds) / (10 ** 6))
def is_soon(dt, window):
"""Determines if time is going to happen in the next window seconds.
:params dt: the time
:params window: minimum seconds to remain to consider the time not soon
:return: True if expiration is within the given duration
"""
soon = (utcnow() + datetime.timedelta(seconds=window))
return normalize_time(dt) <= soon

View File

@@ -31,7 +31,7 @@ import sys
from keystoneclient.auth.identity import v2 as v2_auth
from keystoneclient.auth.identity import v3 as v3_auth
from keystoneclient import discover
from keystoneclient.openstack.common.apiclient import exceptions as ks_exc
from keystoneclient import exceptions as ks_exc
from keystoneclient import session
from oslo_utils import encodeutils
import six.moves.urllib.parse as urlparse
@@ -46,8 +46,7 @@ from tackerclient.common import extension as client_extension
from tackerclient.common import utils
from tackerclient.i18n import _
from tackerclient.tacker.v1_0 import extension
from tackerclient.tacker.v1_0.vm import device
from tackerclient.tacker.v1_0.vm import device_template
from tackerclient.tacker.v1_0.nfvo import vim
from tackerclient.tacker.v1_0.vm import vnf
from tackerclient.tacker.v1_0.vm import vnfd
from tackerclient.version import __version__
@@ -103,24 +102,13 @@ COMMAND_V1 = {
'bash-completion': BashCompletionCommand,
'ext-list': extension.ListExt,
'ext-show': extension.ShowExt,
'device-template-create': device_template.CreateDeviceTemplate,
'device-template-list': device_template.ListDeviceTemplate,
'device-template-show': device_template.ShowDeviceTemplate,
'device-template-update': device_template.UpdateDeviceTemplate,
'device-template-delete': device_template.DeleteDeviceTemplate,
'device-create': device.CreateDevice,
'device-list': device.ListDevice,
'device-show': device.ShowDevice,
'device-update': device.UpdateDevice,
'device-delete': device.DeleteDevice,
'interface-attach': device.AttachInterface,
'interface-detach': device.DetachInterface,
# MANO lingo
'vnfd-create': vnfd.CreateVNFD,
'vnfd-delete': vnfd.DeleteVNFD,
'vnfd-list': vnfd.ListVNFD,
'vnfd-show': vnfd.ShowVNFD,
'vnfd-template-show': vnfd.ShowTemplateVNFD,
'vnf-create': vnf.CreateVNF,
'vnf-update': vnf.UpdateVNF,
@@ -129,17 +117,24 @@ COMMAND_V1 = {
'vnf-show': vnf.ShowVNF,
# 'vnf-config-create'
# 'vnf-config-push'
'vim-register': vim.CreateVIM,
'vim-update': vim.UpdateVIM,
'vim-delete': vim.DeleteVIM,
'vim-list': vim.ListVIM,
'vim-show': vim.ShowVIM,
}
COMMANDS = {'1.0': COMMAND_V1}
class HelpAction(argparse.Action):
"""Provide a custom action so the -h and --help options
to the main app will print a list of the commands.
"""Provides a custom action for the -h and --help options.
The commands are determined by checking the CommandManager
instance, passed in as the "default" value for the action.
:returns: a list of the commands
"""
def __call__(self, parser, namespace, values, option_string=None):
outputs = []
@@ -249,8 +244,10 @@ class TackerShell(app.App):
parser.add_argument(
'--os-service-type', metavar='<os-service-type>',
default=env('OS_SERVICEVM_SERVICE_TYPE', default='servicevm'),
help=_('Defaults to env[OS_SERVICEVM_SERVICE_TYPE] or servicevm.'))
default=env('OS_TACKER_SERVICE_TYPE',
default='nfv-orchestration'),
help=_('Defaults to env[OS_TACKER_SERVICE_TYPE] or \
nfv-orchestration.'))
parser.add_argument(
'--os-endpoint-type', metavar='<os-endpoint-type>',
@@ -261,7 +258,8 @@ class TackerShell(app.App):
# backward compatibility.
parser.add_argument(
'--service-type', metavar='<service-type>',
default=env('OS_SERVICEVM_SERVICE_TYPE', default='servicevm'),
default=env('OS_TACKER_SERVICE_TYPE',
default='nfv-orchestration'),
help=_('DEPRECATED! Use --os-service-type.'))
# FIXME(bklei): --endpoint-type is deprecated but kept in for
@@ -556,7 +554,9 @@ class TackerShell(app.App):
return 1
def authenticate_user(self):
"""Make sure the user has provided all of the authentication
"""Authentication validation.
Make sure the user has provided all of the authentication
info we need.
"""
if self.options.os_auth_strategy == 'keystone':
@@ -670,7 +670,8 @@ class TackerShell(app.App):
super(TackerShell, self).initialize_app(argv)
self.api_version = {'servicevm': self.api_version}
self.api_version = {'nfv-orchestration':
self.api_version}
# If the user is not asking for help, make sure they
# have given us auth.

View File

@@ -14,20 +14,20 @@
# under the License.
#
from tackerclient.common._i18n import _
from tackerclient.common import exceptions
from tackerclient.common import utils
from tackerclient.openstack.common.gettextutils import _
API_NAME = 'servicevm'
API_NAME = 'nfv-orchestration'
API_VERSIONS = {
'1.0': 'tackerclient.v1_0.client.Client',
}
def make_client(instance):
"""Returns an tacker client.
"""
"""Returns an tacker client."""
tacker_client = utils.get_client_class(
API_NAME,
instance._api_version[API_NAME],
@@ -61,7 +61,8 @@ def make_client(instance):
def Client(api_version, *args, **kwargs):
"""Return an tacker client.
@param api_version: only 1.0 is supported now
:param api_version: only 1.0 is supported now
"""
tacker_client = utils.get_client_class(
API_NAME,

View File

@@ -27,10 +27,10 @@ from cliff import show
from oslo_serialization import jsonutils
import six
from tackerclient.common._i18n import _
from tackerclient.common import command
from tackerclient.common import exceptions
from tackerclient.common import utils
from tackerclient.openstack.common.gettextutils import _
HEX_ELEM = '[0-9A-Fa-f]'
UUID_PATTERN = '-'.join([HEX_ELEM + '{8}', HEX_ELEM + '{4}',
@@ -263,7 +263,7 @@ def parse_args_to_dict(values_specs):
# All others are value items
# Make sure '--' occurs first and allow minus value
if (not current_item or '=' in current_item or
_item.startswith('-') and not is_number(_item)):
_item.startswith('-') and not is_number(_item)):
raise exceptions.CommandError(
_("Invalid values_specs %s") % ' '.join(values_specs))
_value_number += 1
@@ -277,7 +277,7 @@ def parse_args_to_dict(values_specs):
# populate the parser with arguments
_parser = argparse.ArgumentParser(add_help=False)
for opt, optspec in _options.iteritems():
for opt, optspec in six.iteritems(_options):
_parser.add_argument(opt, **optspec)
_args = _parser.parse_args(_values_specs)
@@ -301,14 +301,14 @@ def _merge_args(qCmd, parsed_args, _extra_values, value_specs):
@param values_specs: the unparsed unknown parts
"""
temp_values = _extra_values.copy()
for key, value in temp_values.iteritems():
for key, value in six.iteritems(temp_values):
if hasattr(parsed_args, key):
arg_value = getattr(parsed_args, key)
if arg_value is not None and value is not None:
if isinstance(arg_value, list):
if value and isinstance(value, list):
if (not arg_value or
type(arg_value[0]) == type(value[0])):
isinstance(arg_value[0], type(value[0]))):
arg_value.extend(value)
_extra_values.pop(key)
@@ -353,7 +353,7 @@ class TackerCommandMeta(abc.ABCMeta):
@six.add_metaclass(TackerCommandMeta)
class TackerCommand(command.OpenStackCommand):
api = 'servicevm'
api = 'nfv-orchestration'
values_specs = []
json_indent = None
@@ -362,8 +362,8 @@ class TackerCommand(command.OpenStackCommand):
# NOTE(markmcclain): This is no longer supported in cliff version 1.5.2
# see https://bugs.launchpad.net/python-tackerclient/+bug/1265926
#if hasattr(self, 'formatters'):
#self.formatters['table'] = TableFormater()
# if hasattr(self, 'formatters'):
# self.formatters['table'] = TableFormater()
def get_client(self):
return self.app.client_manager.tacker
@@ -385,7 +385,7 @@ class TackerCommand(command.OpenStackCommand):
def format_output_data(self, data):
# Modify data to make it more readable
if self.resource in data:
for k, v in data[self.resource].iteritems():
for k, v in six.iteritems(data[self.resource]):
if isinstance(v, list):
value = '\n'.join(jsonutils.dumps(
i, indent=self.json_indent) if isinstance(i, dict)
@@ -409,7 +409,7 @@ class CreateCommand(TackerCommand, show.ShowOne):
"""
api = 'servicevm'
api = 'nfv-orchestration'
resource = None
log = None
remove_output_fields = []
@@ -448,14 +448,13 @@ class CreateCommand(TackerCommand, show.ShowOne):
info.pop(f)
else:
info = {'': ''}
return zip(*sorted(info.iteritems()))
return zip(*sorted(six.iteritems(info)))
class UpdateCommand(TackerCommand):
"""Update resource's information
"""
"""Update resource's information."""
api = 'servicevm'
api = 'nfv-orchestration'
resource = None
log = None
allow_names = True
@@ -503,7 +502,7 @@ class DeleteCommand(TackerCommand):
"""
api = 'servicevm'
api = 'nfv-orchestration'
resource = None
log = None
allow_names = True
@@ -543,7 +542,7 @@ class ListCommand(TackerCommand, lister.Lister):
"""
api = 'servicevm'
api = 'nfv-orchestration'
resource = None
log = None
_formatters = {}
@@ -642,11 +641,15 @@ class ShowCommand(TackerCommand, show.ShowOne):
"""
api = 'servicevm'
api = 'nfv-orchestration'
resource = None
log = None
allow_names = True
def get_id(self):
if self.resource:
return self.resource.upper()
def get_parser(self, prog_name):
parser = super(ShowCommand, self).get_parser(prog_name)
add_show_list_common_argument(parser)
@@ -655,7 +658,7 @@ class ShowCommand(TackerCommand, show.ShowOne):
else:
help_str = _('ID of %s to look up')
parser.add_argument(
'id', metavar=self.resource.upper(),
'id', metavar=self.get_id(),
help=help_str % self.resource)
return parser
@@ -680,6 +683,6 @@ class ShowCommand(TackerCommand, show.ShowOne):
self.format_output_data(data)
resource = data[self.resource]
if self.resource in data:
return zip(*sorted(resource.iteritems()))
return zip(*sorted(six.iteritems(resource)))
else:
return None

View File

@@ -14,7 +14,6 @@
# under the License.
#
from tackerclient.openstack.common.gettextutils import _
from tackerclient.tacker import v1_0 as cmd_base
@@ -31,10 +30,5 @@ class ShowExt(cmd_base.ShowCommand):
resource = "extension"
allow_names = False
def get_parser(self, prog_name):
parser = super(cmd_base.ShowCommand, self).get_parser(prog_name)
cmd_base.add_show_list_common_argument(parser)
parser.add_argument(
'id', metavar='EXT-ALIAS',
help=_('The extension alias'))
return parser
def get_id(self):
return 'EXT-ALIAS'

View File

@@ -0,0 +1,131 @@
# Copyright 2016 Brocade Communications Systems Inc
# All Rights Reserved.
#
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import yaml
from tackerclient.common import exceptions
from tackerclient.common import utils
from tackerclient.tacker import v1_0 as tackerV10
from tackerclient.tacker.v1_0.nfvo import vim_utils
_VIM = "vim"
class ListVIM(tackerV10.ListCommand):
"""List VIMs that belong to a given tenant."""
resource = _VIM
list_columns = ['id', 'tenant_id', 'name', 'type', 'description',
'auth_url', 'placement_attr', 'auth_cred', 'status']
class ShowVIM(tackerV10.ShowCommand):
"""Show information of a given VIM."""
resource = _VIM
class CreateVIM(tackerV10.CreateCommand):
"""Create a VIM."""
resource = _VIM
def add_known_arguments(self, parser):
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('--config-file', help='Specify VIM specific '
'config parameters in a file')
group.add_argument('--config', help='Specify VIM config parameters '
'as a direct input')
parser.add_argument(
'--name',
help='Set a name for the VIM')
parser.add_argument(
'--description',
help='Set a description for the VIM')
parser.add_argument(
'--is-default',
action='store_true',
default=False,
help='Set as default VIM')
def args2body(self, parsed_args):
body = {self.resource: {}}
if parsed_args.config_file:
with open(parsed_args.config_file) as f:
vim_config = f.read()
config_param = yaml.load(vim_config)
if parsed_args.config:
parsed_args.config = parsed_args.config.decode('unicode_escape')
config_param = yaml.load(parsed_args.config)
vim_obj = body[self.resource]
try:
auth_url = config_param.pop('auth_url')
except KeyError:
raise exceptions.TackerClientException(message='Auth URL must be '
'specified',
status_code=404)
vim_obj['auth_url'] = utils.validate_url(auth_url).geturl()
vim_obj['type'] = config_param.pop('type', 'openstack')
vim_utils.args2body_vim(config_param, vim_obj)
tackerV10.update_dict(parsed_args, body[self.resource],
['tenant_id', 'name', 'description',
'is_default'])
return body
class UpdateVIM(tackerV10.UpdateCommand):
"""Update a given VIM."""
resource = _VIM
def add_known_arguments(self, parser):
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument(
'--config-file',
help='Specify VIM specific config parameters in a file')
group.add_argument(
'--config',
help='Specify VIM config parameters as a direct input')
parser.add_argument(
'--is-default',
action='store_true',
default=False,
help='Set as default VIM')
def args2body(self, parsed_args):
body = {self.resource: {}}
# config arg passed as data overrides config yaml when both args passed
if parsed_args.config_file:
with open(parsed_args.config_file) as f:
config_yaml = f.read()
config_param = yaml.load(config_yaml)
if parsed_args.config:
parsed_args.config = parsed_args.config.decode('unicode_escape')
config_param = yaml.load(parsed_args.config)
if 'auth_url' in config_param:
raise exceptions.TackerClientException(message='Auth URL cannot '
'be updated',
status_code=404)
vim_obj = body[self.resource]
vim_utils.args2body_vim(config_param, vim_obj)
tackerV10.update_dict(parsed_args, body[self.resource],
['tenant_id', 'is_default'])
return body
class DeleteVIM(tackerV10.DeleteCommand):
"""Delete a given VIM."""
resource = _VIM

View File

@@ -0,0 +1,39 @@
# Copyright 2016 Brocade Communications Systems Inc
# All Rights Reserved.
#
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from tackerclient.common import exceptions
def args2body_vim(config_param, vim):
"""Create additional args to vim body
:param vim: vim request object
:return: vim body with args populated
"""
vim['vim_project'] = {'id': config_param.pop('project_id', ''),
'name': config_param.pop('project_name', ''),
'project_domain_name':
config_param.pop('project_domain_name', '')}
if not vim['vim_project']['id'] and not vim['vim_project']['name']:
raise exceptions.TackerClientException(message='Project Id or name '
'must be specified',
status_code=404)
vim['auth_cred'] = {'username': config_param.pop('username', ''),
'password': config_param.pop('password', ''),
'user_id': config_param.pop('user_id', ''),
'user_domain_name':
config_param.pop('user_domain_name', '')}

View File

@@ -1,173 +0,0 @@
#
# Copyright 2013 Intel
# Copyright 2013 Isaku Yamahata <isaku.yamahata at intel com>
# <isaku.yamahata at gmail com>
# All Rights Reserved.
#
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Isaku Yamahata, Intel
import abc
import six
from tackerclient.common import exceptions
from tackerclient.common.utils import deprecated
from tackerclient.openstack.common.gettextutils import _
from tackerclient.tacker import v1_0 as tackerV10
_DEVICE = 'device'
@deprecated('device-list')
class ListDevice(tackerV10.ListCommand):
"""List device that belong to a given tenant."""
resource = _DEVICE
list_columns = ['id', 'name', 'description', 'mgmt_url', 'status']
@deprecated('device-show')
class ShowDevice(tackerV10.ShowCommand):
"""show information of a given Device."""
resource = _DEVICE
@deprecated('device-create')
class CreateDevice(tackerV10.CreateCommand):
"""create a Device."""
resource = _DEVICE
def add_known_arguments(self, parser):
parser.add_argument(
'--name',
help='Set a name for the devicef')
parser.add_argument(
'--device-template-id',
required=True,
help='device template id to create device based on')
parser.add_argument(
'--attributes',
metavar='<key>=<value>',
action='append',
dest='attributes',
default=[],
help='instance specific argument')
def args2body(self, parsed_args):
body = {
self.resource: {
'template_id': parsed_args.device_template_id,
}
}
if parsed_args.attributes:
try:
attributes = dict(key_value.split('=', 1)
for key_value in parsed_args.attributes)
except ValueError:
msg = (_('invalid argument for --attributes %s') %
parsed_args.attributes)
raise exceptions.TackerCLIError(msg)
if attributes:
body[self.resource]['attributes'] = attributes
tackerV10.update_dict(parsed_args, body[self.resource], ['tenant_id'])
return body
@deprecated('device-update')
class UpdateDevice(tackerV10.UpdateCommand):
"""Update a given Device."""
resource = _DEVICE
def add_known_arguments(self, parser):
parser.add_argument(
'--attributes',
metavar='<key>=<value>',
action='append',
dest='attributes',
default=[],
help='instance specific argument')
def args2body(self, parsed_args):
body = {self.resource: {}}
if parsed_args.attributes:
try:
attributes = dict(key_value.split('=', 1)
for key_value in parsed_args.attributes)
except ValueError:
msg = (_('invalid argument for --attributes %s') %
parsed_args.attributes)
raise exceptions.TackerCLIError(msg)
if attributes:
body[self.resource]['attributes'] = attributes
tackerV10.update_dict(parsed_args, body[self.resource], ['tenant_id'])
return body
@deprecated('device-delete')
class DeleteDevice(tackerV10.DeleteCommand):
"""Delete a given Device."""
resource = _DEVICE
@six.add_metaclass(abc.ABCMeta)
class _XtachInterface(tackerV10.UpdateCommand):
resource = _DEVICE
@abc.abstractmethod
def call_api(self, tacker_client, device_id, body):
pass
def args2body(self, parsed_args):
body = {
'port_id': parsed_args.port_id,
}
tackerV10.update_dict(parsed_args, body, [])
return body
def get_parser(self, prog_name):
parser = super(_XtachInterface, self).get_parser(prog_name)
parser.add_argument('port_id', metavar='PORT',
help=_('port to attach/detach'))
self.add_known_arguments(parser)
return parser
def run(self, parsed_args):
tacker_client = self.get_client()
tacker_client.format = parsed_args.request_format
body = self.args2body(parsed_args)
_id = tackerV10.find_resourceid_by_name_or_id(tacker_client,
self.resource,
parsed_args.id)
self.call_api(tacker_client, _id, body)
class AttachInterface(_XtachInterface):
"""Attach a network interface to a server."""
def call_api(self, tacker_client, device_id, body):
return tacker_client.attach_interface(device_id, body)
class DetachInterface(_XtachInterface):
"""Detach a network interface from a server."""
def call_api(self, tacker_client, device_id, body):
return tacker_client.detach_interface(device_id, body)

View File

@@ -1,100 +0,0 @@
#
# Copyright 2013 Intel
# Copyright 2013 Isaku Yamahata <isaku.yamahata at intel com>
# <isaku.yamahata at gmail com>
# All Rights Reserved.
#
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Isaku Yamahata, Intel
from tackerclient.common.utils import deprecated
from tackerclient.tacker import v1_0 as tackerV10
_DEVICE_TEMPLATE = "device_template"
@deprecated('device-template-list')
class ListDeviceTemplate(tackerV10.ListCommand):
"""List device template that belong to a given tenant."""
resource = _DEVICE_TEMPLATE
@deprecated('device-template-show')
class ShowDeviceTemplate(tackerV10.ShowCommand):
"""show information of a given DeviceTemplate."""
resource = _DEVICE_TEMPLATE
@deprecated('device-template-create')
class CreateDeviceTemplate(tackerV10.CreateCommand):
"""create a DeviceTemplate."""
resource = _DEVICE_TEMPLATE
def add_known_arguments(self, parser):
parser.add_argument(
'--name',
help='Set a name for the devicetemplate')
parser.add_argument(
'--description',
help='Set a description for the devicetemplate')
parser.add_argument(
'--template-service-type',
action='append',
help='Add a servicetype for the devicetemplate')
parser.add_argument(
'--infra-driver',
help='Set a infra driver name for the devicetemplate')
parser.add_argument(
'--mgmt-driver',
help='Set a manegement driver name for the devicetemplate')
parser.add_argument(
'--attribute',
nargs=2,
action='append',
help='Set a servicetypes for the devicetemplate')
def args2body(self, parsed_args):
body = {
self.resource: {
'service_types': [
{'service_type': service_type}
for service_type in parsed_args.template_service_type],
'infra_driver': parsed_args.infra_driver,
'mgmt_driver': parsed_args.mgmt_driver,
}
}
if parsed_args.attribute:
body[self.resource]['attributes'] = dict(parsed_args.attribute)
tackerV10.update_dict(parsed_args, body[self.resource],
['tenant_id', 'name', 'description'])
return body
@deprecated('device-template-update')
class UpdateDeviceTemplate(tackerV10.UpdateCommand):
"""Update a given DeviceTemplate."""
resource = _DEVICE_TEMPLATE
allow_names = False
@deprecated('device-template-delete')
class DeleteDeviceTemplate(tackerV10.DeleteCommand):
"""Delete a given DeviceTemplate."""
resource = _DEVICE_TEMPLATE

View File

@@ -1,7 +1,5 @@
#
# Copyright 2013 Intel
# Copyright 2013 Isaku Yamahata <isaku.yamahata at intel com>
# <isaku.yamahata at gmail com>
# Copyright 2013 Intel Corporation
# All Rights Reserved.
#
#
@@ -16,8 +14,6 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Isaku Yamahata, Intel
from tackerclient.tacker import v1_0 as tackerV10
@@ -26,20 +22,21 @@ _VNF = 'vnf'
class ListVNF(tackerV10.ListCommand):
"""List device that belong to a given tenant."""
"""List VNF that belong to a given tenant."""
resource = _VNF
list_columns = ['id', 'name', 'description', 'mgmt_url', 'status']
list_columns = ['id', 'name', 'description', 'mgmt_url', 'status',
'vim_id', 'placement_attr', 'error_reason']
class ShowVNF(tackerV10.ShowCommand):
"""show information of a given VNF."""
"""Show information of a given VNF."""
resource = _VNF
class CreateVNF(tackerV10.CreateCommand):
"""create a VNF."""
"""Create a VNF."""
resource = _VNF
remove_output_fields = ["attributes"]
@@ -47,23 +44,33 @@ class CreateVNF(tackerV10.CreateCommand):
def add_known_arguments(self, parser):
parser.add_argument(
'--name',
help='Set a name for the vnf')
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument(
help='Set a name for the VNF')
vnfd_group = parser.add_mutually_exclusive_group(required=True)
vnfd_group.add_argument(
'--vnfd-id',
help='VNFD ID to use as template to create VNF')
group.add_argument(
vnfd_group.add_argument(
'--vnfd-name',
help='VNFD Name to use as template to create VNF')
vim_group = parser.add_mutually_exclusive_group()
vim_group.add_argument(
'--vim-id',
help='VIM ID to use to create VNF on the specified VIM')
vim_group.add_argument(
'--vim-name',
help='VIM name to use to create VNF on the specified VIM')
parser.add_argument(
'--vim-region-name',
help='VIM Region to use to create VNF on the specified VIM')
parser.add_argument(
'--config-file',
help='specify config yaml file')
help='Specify config yaml file')
parser.add_argument(
'--config',
help='specify config yaml file')
help='Specify config yaml data')
parser.add_argument(
'--param-file',
help='specify parameter yaml file'
help='Specify parameter yaml file'
)
def args2body(self, parsed_args):
@@ -78,21 +85,30 @@ class CreateVNF(tackerV10.CreateCommand):
parsed_args.config = parsed_args.config.decode('unicode_escape')
args['attributes']['config'] = parsed_args.config
if parsed_args.vim_region_name:
args.setdefault('placement_attr', {})['region_name'] = \
parsed_args.vim_region_name
tacker_client = self.get_client()
tacker_client.format = parsed_args.request_format
if parsed_args.vim_name:
_id = tackerV10.find_resourceid_by_name_or_id(tacker_client,
'vim',
parsed_args.
vim_name)
parsed_args.vim_id = _id
if parsed_args.vnfd_name:
tacker_client = self.get_client()
tacker_client.format = parsed_args.request_format
_id = tackerV10.find_resourceid_by_name_or_id(
tacker_client, 'vnfd',
parsed_args.vnfd_name)
parsed_args.vnfd_id = _id
_id = tackerV10.find_resourceid_by_name_or_id(tacker_client,
'vnfd',
parsed_args.
vnfd_name)
parsed_args.vnfd_id = _id
if parsed_args.param_file:
with open(parsed_args.param_file) as f:
param_yaml = f.read()
args['attributes']['param_values'] = param_yaml
tackerV10.update_dict(parsed_args, body[self.resource],
['tenant_id', 'name', 'vnfd_id'])
['tenant_id', 'name', 'vnfd_id', 'vim_id'])
return body
@@ -104,10 +120,10 @@ class UpdateVNF(tackerV10.UpdateCommand):
def add_known_arguments(self, parser):
parser.add_argument(
'--config-file',
help='specify config yaml file')
help='Specify config yaml file')
parser.add_argument(
'--config',
help='specify config yaml file')
help='Specify config yaml data')
def args2body(self, parsed_args):
body = {self.resource: {}}

View File

@@ -1,7 +1,5 @@
#
# Copyright 2013 Intel
# Copyright 2013 Isaku Yamahata <isaku.yamahata at intel com>
# <isaku.yamahata at gmail com>
# Copyright 2013 Intel Corporation
# All Rights Reserved.
#
#
@@ -16,9 +14,10 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Isaku Yamahata, Intel
from oslo_serialization import jsonutils
from tackerclient.i18n import _
from tackerclient.tacker import v1_0 as tackerV10
@@ -33,27 +32,27 @@ class ListVNFD(tackerV10.ListCommand):
class ShowVNFD(tackerV10.ShowCommand):
"""show information of a given VNFD."""
"""Show information of a given VNFD."""
resource = _VNFD
class CreateVNFD(tackerV10.CreateCommand):
"""create a VNFD."""
"""Create a VNFD."""
resource = _VNFD
remove_output_fields = ["attributes"]
def add_known_arguments(self, parser):
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('--vnfd-file', help='specify vnfd file')
group.add_argument('--vnfd', help='specify vnfd')
group.add_argument('--vnfd-file', help='Specify VNFD file')
group.add_argument('--vnfd', help='Specify VNFD')
parser.add_argument(
'--name',
help='Set a name for the vnfd')
help='Set a name for the VNFD')
parser.add_argument(
'--description',
help='Set a description for the vnfd')
help='Set a description for the VNFD')
def args2body(self, parsed_args):
body = {self.resource: {}}
@@ -72,3 +71,21 @@ class CreateVNFD(tackerV10.CreateCommand):
class DeleteVNFD(tackerV10.DeleteCommand):
"""Delete a given VNFD."""
resource = _VNFD
class ShowTemplateVNFD(tackerV10.ShowCommand):
"""Show template of a given VNFD."""
resource = _VNFD
def run(self, parsed_args):
self.log.debug('run(%s)', parsed_args)
template = None
data = self.get_data(parsed_args)
try:
attributes_index = data[0].index('attributes')
attributes_json = data[1][attributes_index]
template = jsonutils.loads(attributes_json).get('vnfd', None)
except (IndexError, TypeError, ValueError) as e:
self.log.debug('Data handling error: %s', str(e))
print(template or _('Unable to display VNFD template!'))

View File

View File

@@ -0,0 +1,52 @@
# Copyright 2016 OpenStack Foundation.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from mock import sentinel
import testtools
from tackerclient.common import exceptions
from tackerclient.tacker.v1_0.nfvo import vim_utils
class CLITestAuthNoAuth(testtools.TestCase):
def test_args2body_vim(self):
config_param = {'project_id': sentinel.prj_id1,
'username': sentinel.usrname1,
'password': sentinel.password1,
'project_domain_name': sentinel.prj_domain_name1,
'user_domain_name': sentinel.user_domain.name, }
vim = {}
auth_cred = config_param.copy()
auth_cred.pop('project_id')
auth_cred.pop('project_domain_name')
auth_cred.update({'user_id': ''})
expected_vim = {'auth_cred': auth_cred,
'vim_project':
{'id': sentinel.prj_id1,
'name': '',
'project_domain_name': sentinel.prj_domain_name1}}
vim_utils.args2body_vim(config_param.copy(), vim)
self.assertEqual(expected_vim, vim)
def test_args2body_vim_no_project(self):
config_param = {'username': sentinel.usrname1,
'password': sentinel.password1,
'user_domain_name': sentinel.user_domain.name, }
vim = {}
self.assertRaises(exceptions.TackerClientException,
vim_utils.args2body_vim,
config_param, vim)

View File

@@ -52,7 +52,7 @@ KS_TOKEN_RESULT = {
'internalURL': ENDPOINT_URL,
'publicURL': ENDPOINT_URL,
'region': REGION}],
'type': 'servicevm',
'type': 'nfv-orchestration',
'name': 'Tacker Service'}
]
}
@@ -60,7 +60,7 @@ KS_TOKEN_RESULT = {
ENDPOINTS_RESULT = {
'endpoints': [{
'type': 'servicevm',
'type': 'nfv-orchestration',
'name': 'Tacker Service',
'region': REGION,
'adminURL': ENDPOINT_URL,
@@ -127,8 +127,10 @@ class CLITestAuthKeystone(testtools.TestCase):
self.addCleanup(self.mox.UnsetStubs)
def test_reused_token_get_auth_info(self):
"""Test that Client.get_auth_info() works even if client was
instantiated with predefined token.
"""Test Client.get_auth_info().
Test that Client.get_auth_info() works even if client was
instantiated with predefined token.
"""
client_ = client.HTTPClient(username=USERNAME,
tenant_name=TENANT_NAME,

View File

@@ -20,6 +20,7 @@ import contextlib
import cStringIO
import fixtures
import mox
import six
import sys
import testtools
@@ -46,7 +47,7 @@ def capture_std_streams():
sys.stdout, sys.stderr = stdout, stderr
class FakeStdout:
class FakeStdout(object):
def __init__(self):
self.content = []
@@ -111,7 +112,7 @@ class MyComparator(mox.Comparator):
def _com_dict(self, lhs, rhs):
if len(lhs) != len(rhs):
return False
for key, value in lhs.iteritems():
for key, value in lhs.items():
if key not in rhs:
return False
rhs_value = rhs[key]
@@ -204,7 +205,7 @@ class CLITestV10Base(testtools.TestCase):
self.mox.StubOutWithMock(cmd, "get_client")
self.mox.StubOutWithMock(self.client.httpclient, "request")
cmd.get_client().MultipleTimes().AndReturn(self.client)
non_admin_status_resources = ['device_template', 'device']
non_admin_status_resources = ['vnfd', 'vnf', 'vim']
if (resource in non_admin_status_resources):
body = {resource: {}, }
else:
@@ -303,7 +304,7 @@ class CLITestV10Base(testtools.TestCase):
args.append("--tag")
for tag in tags:
args.append(tag)
if isinstance(tag, unicode):
if isinstance(tag, six.string_types):
tag = urllib.quote(tag.encode('utf-8'))
if query:
query += "&tag=" + tag

View File

@@ -1,6 +1,4 @@
# Copyright 2013 Intel
# Copyright 2013 Isaku Yamahata <isaku.yamahata at intel com>
# <isaku.yamahata at gmail com>
# Copyright 2013 Intel Corporation
# All Rights Reserved.
#
#
@@ -15,8 +13,6 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Isaku Yamahata, Intel
import logging

View File

@@ -35,7 +35,7 @@ DEFAULT_TENANT_ID = 'tenant_id'
DEFAULT_TENANT_NAME = 'tenant_name'
DEFAULT_AUTH_URL = 'http://127.0.0.1:5000/v1.0/'
DEFAULT_TOKEN = '3bcc3d3a03f44e3d8377f9247b0ad155'
DEFAULT_URL = 'http://tacker.example.org:8888/'
DEFAULT_URL = 'http://tacker.example.org:9890/'
class ShellTest(testtools.TestCase):
@@ -93,8 +93,8 @@ class ShellTest(testtools.TestCase):
def test_help_on_subcommand(self):
required = [
'.*?^usage: .* device-template-list']
stdout, stderr = self.shell('help device-template-list')
'.*?^usage: .* vnfd-list']
stdout, stderr = self.shell('help vnfd-list')
for r in required:
self.assertThat(
stdout,
@@ -112,13 +112,13 @@ class ShellTest(testtools.TestCase):
def test_unknown_auth_strategy(self):
self.useFixture(fixtures.FakeLogger(level=logging.DEBUG))
stdout, stderr = self.shell('--os-auth-strategy fake '
'device-template-list')
'vnfd-list')
self.assertFalse(stdout)
self.assertEqual('You must provide a service URL via '
'either --os-url or env[OS_URL]', stderr.strip())
def test_auth(self):
#import pdb; pdb.set_trace()
# import pdb; pdb.set_trace()
tacker_shell = openstack_shell.TackerShell('1.0')
self.addCleanup(self.mox.UnsetStubs)
self.mox.StubOutWithMock(clientmanager.ClientManager, '__init__')
@@ -127,17 +127,19 @@ class ShellTest(testtools.TestCase):
token='', url='', auth_url='http://127.0.0.1:5000/',
tenant_name='test', tenant_id='tenant_id',
username='test', user_id='',
password='test', region_name='', api_version={'servicevm': '1.0'},
auth_strategy='keystone', service_type='servicevm',
password='test', region_name='',
api_version={'nfv-orchestration': '1.0'},
auth_strategy='keystone',
service_type='nfv-orchestration',
endpoint_type='publicURL', insecure=False, ca_cert=None,
log_credentials=True)
tacker_shell.run_subcommand(['device-template-list'])
tacker_shell.run_subcommand(['vnfd-list'])
self.mox.ReplayAll()
cmdline = ('--os-username test '
'--os-password test '
'--os-tenant-name test '
'--os-auth-url http://127.0.0.1:5000/ '
'--os-auth-strategy keystone device-template-list')
'--os-auth-strategy keystone vnfd-list')
tacker_shell.run(cmdline.split())
self.mox.VerifyAll()

View File

@@ -118,7 +118,7 @@ class TestSSL(testtools.TestCase):
)
self.mox.ReplayAll()
version = {'servicevm': '1.0'}
version = {'nfv-orchestration': '1.0'}
ClientManager(ca_cert=CA_CERT,
api_version=version,
url=END_URL,

View File

@@ -19,7 +19,7 @@ from tackerclient.common import exceptions
from tackerclient.common import validators
class FakeParsedArgs():
class FakeParsedArgs(object):
pass

View File

@@ -1,99 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2014 Intel
# Copyright 2014 Isaku Yamahata <isaku.yamahata at intel com>
# <isaku.yamahata at gmail com>
# All Rights Reserved.
#
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Isaku Yamahata, Intel
import sys
from tackerclient.tacker.v1_0.vm import device
from tackerclient.tests.unit import test_cli10
class CLITestV10VmDeviceJSON(test_cli10.CLITestV10Base):
_RESOURCE = 'device'
_RESOURCES = 'devices'
def setUp(self):
plurals = {'devices': 'device'}
super(CLITestV10VmDeviceJSON, self).setUp(plurals=plurals)
def test_create_device_all_params(self):
cmd = device.CreateDevice(test_cli10.MyApp(sys.stdout), None)
my_id = 'my-id'
template_id = 'template_id'
key = 'key'
value = 'value'
args = [
'--device-template-id', template_id,
'--%s' % key, value]
position_names = ['template_id']
position_values = [template_id]
extra_body = {key: value}
self._test_create_resource(self._RESOURCE, cmd, None, my_id,
args, position_names, position_values,
extra_body=extra_body)
def test_create_device_with_mandatory_params(self):
cmd = device.CreateDevice(test_cli10.MyApp(sys.stdout), None)
my_id = 'my-id'
template_id = 'template_id'
args = [
'--device-template-id', template_id,
]
position_names = ['template_id']
position_values = [template_id]
self._test_create_resource(self._RESOURCE, cmd, None, my_id,
args, position_names, position_values)
def test_list_devices(self):
cmd = device.ListDevice(test_cli10.MyApp(sys.stdout), None)
self._test_list_resources(self._RESOURCES, cmd, True)
def test_list_devices_pagenation(self):
cmd = device.ListDevice(test_cli10.MyApp(sys.stdout), None)
self._test_list_resources(self._RESOURCES, cmd, True)
def test_show_device_id(self):
cmd = device.ShowDevice(test_cli10.MyApp(sys.stdout), None)
args = ['--fields', 'id', self.test_id]
self._test_show_resource(self._RESOURCE, cmd, self.test_id, args,
['id'])
def test_show_device_id_name(self):
cmd = device.ShowDevice(test_cli10.MyApp(sys.stdout), None)
args = ['--fields', 'id', '--fields', 'name', self.test_id]
self._test_show_resource(self._RESOURCE, cmd, self.test_id,
args, ['id', 'name'])
def test_update_device(self):
cmd = device.UpdateDevice(test_cli10.MyApp(sys.stdout), None)
my_id = 'my-id'
key = 'new_key'
value = 'new-value'
self._test_update_resource(self._RESOURCE, cmd, my_id,
[my_id, '--%s' % key, value],
{key: value})
def test_delete_device(self):
cmd = device.DeleteDevice(test_cli10.MyApp(sys.stdout), None)
my_id = 'my-id'
args = [my_id]
self._test_delete_resource(self._RESOURCE, cmd, my_id, args)

View File

@@ -1,134 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2014 Intel
# Copyright 2014 Isaku Yamahata <isaku.yamahata at intel com>
# <isaku.yamahata at gmail com>
# All Rights Reserved.
#
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Isaku Yamahata, Intel
import sys
from tackerclient.tacker.v1_0.vm import device_template
from tackerclient.tests.unit import test_cli10
class CLITestV10VmDeviceTemplateJSON(test_cli10.CLITestV10Base):
_RESOURCE = 'device_template'
_RESOURCES = 'device_templates'
def setUp(self):
plurals = {'device_templates': 'device_template'}
super(CLITestV10VmDeviceTemplateJSON, self).setUp(plurals=plurals)
def test_create_device_template_all_params(self):
cmd = device_template.CreateDeviceTemplate(
test_cli10.MyApp(sys.stdout), None)
my_id = 'my-id'
name = 'my-name'
description = 'my-description'
service_type = 'MY-SERVICE'
device_driver = 'device-driver'
mgmt_driver = 'mgmt-driver'
infra_driver = 'infra-driver'
attr_key = 'attr-key'
attr_val = 'attr-val'
args = [
'--name', name,
'--description', description,
'--template-service-type', service_type,
'--device-driver', device_driver,
'--mgmt-driver', mgmt_driver,
'--infra-driver', infra_driver,
'--attribute', attr_key, attr_val,
]
position_names = ['name', 'description',
'device_driver', 'mgmt_driver',
'infra_driver']
position_values = [name, description, device_driver,
mgmt_driver, infra_driver]
extra_body = {
'service_types': [{'service_type': service_type}],
'attributes': {attr_key: attr_val},
}
self._test_create_resource(self._RESOURCE, cmd, None, my_id,
args, position_names, position_values,
extra_body=extra_body)
def test_create_device_template_with_mandatory_params(self):
cmd = device_template.CreateDeviceTemplate(
test_cli10.MyApp(sys.stdout), None)
my_id = 'my-id'
service_type = 'MY-SERVICE'
device_driver = 'device-driver'
mgmt_driver = 'mgmt-driver'
infra_driver = 'infra-driver'
args = [
'--template-service-type', service_type,
'--device-driver', device_driver,
'--mgmt-driver', mgmt_driver,
'--infra-driver', infra_driver,
]
position_names = ['device_driver', 'mgmt_driver', 'infra_driver']
position_values = [device_driver, mgmt_driver, infra_driver]
extra_body = {
'service_types': [{'service_type': service_type}],
}
self._test_create_resource(self._RESOURCE, cmd, None, my_id,
args, position_names, position_values,
extra_body=extra_body)
def test_list_device_templates(self):
cmd = device_template.ListDeviceTemplate(test_cli10.MyApp(sys.stdout),
None)
self._test_list_resources(self._RESOURCES, cmd, True)
def test_list_device_templates_pagenation(self):
cmd = device_template.ListDeviceTemplate(test_cli10.MyApp(sys.stdout),
None)
self._test_list_resources(self._RESOURCES, cmd, True)
def test_show_device_template_id(self):
cmd = device_template.ShowDeviceTemplate(test_cli10.MyApp(sys.stdout),
None)
args = ['--fields', 'id', self.test_id]
self._test_show_resource(self._RESOURCE, cmd, self.test_id, args,
['id'])
def test_show_device_template_id_name(self):
cmd = device_template.ShowDeviceTemplate(test_cli10.MyApp(sys.stdout),
None)
args = ['--fields', 'id', '--fields', 'name', self.test_id]
self._test_show_resource(self._RESOURCE, cmd, self.test_id,
args, ['id', 'name'])
def test_update_device_template(self):
cmd = device_template.UpdateDeviceTemplate(
test_cli10.MyApp(sys.stdout), None)
my_id = 'my-id'
name = 'new-name'
description = 'new-description'
self._test_update_resource(self._RESOURCE, cmd, my_id,
[my_id, '--name', name,
'--description', description],
{'name': name, 'description': description})
def test_delete_device_tempalte(self):
cmd = device_template.DeleteDeviceTemplate(
test_cli10.MyApp(sys.stdout), None)
my_id = 'my-id'
args = [my_id]
self._test_delete_resource(self._RESOURCE, cmd, my_id, args)

View File

@@ -0,0 +1,117 @@
# Copyright 2015-2016 Brocade Communications Systems Inc
# All Rights Reserved.
#
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import sys
from tackerclient.tacker.v1_0.nfvo import vim
from tackerclient.tests.unit import test_cli10
API_VERSION = "1.0"
FORMAT = 'json'
TOKEN = 'testtoken'
ENDURL = 'localurl'
class CLITestV10VIMJSON(test_cli10.CLITestV10Base):
_RESOURCE = 'vim'
_RESOURCES = 'vims'
def setUp(self):
plurals = {'vims': 'vim'}
super(CLITestV10VIMJSON, self).setUp(plurals=plurals)
self.vim_project = {
'name': 'abc', 'id': '',
'project_domain_name': 'prj_domain_name'}
self.auth_cred = {'username': 'xyz', 'password': '12345', 'user_id':
'', 'user_domain_name': 'user_domain_name'}
self.auth_url = 'http://1.2.3.4:5000'
def test_register_vim_all_params(self):
cmd = vim.CreateVIM(test_cli10.MyApp(sys.stdout), None)
my_id = 'my-id'
name = 'test_vim'
description = 'Vim Description'
vim_config = {'auth_url': 'http://1.2.3.4:5000', 'username': 'xyz',
'password': '12345', 'project_name': 'abc',
'project_domain_name': 'prj_domain_name',
'user_domain_name': 'user_domain_name'}
args = [
'--config', str(vim_config),
'--name', name,
'--description', description]
position_names = ['auth_cred', 'vim_project', 'auth_url']
position_values = [self.auth_cred, self.vim_project, self.auth_url]
extra_body = {'type': 'openstack', 'name': name, 'description':
description, 'is_default': False}
self._test_create_resource(self._RESOURCE, cmd, None, my_id,
args, position_names, position_values,
extra_body=extra_body)
def test_register_vim_with_mandatory_params(self):
cmd = vim.CreateVIM(test_cli10.MyApp(sys.stdout), None)
my_id = 'my-id'
vim_config = {'auth_url': 'http://1.2.3.4:5000', 'username': 'xyz',
'password': '12345', 'project_name': 'abc',
'project_domain_name': 'prj_domain_name',
'user_domain_name': 'user_domain_name'}
args = [
'--config', str(vim_config),
]
position_names = ['auth_cred', 'vim_project', 'auth_url']
position_values = [self.auth_cred, self.vim_project, self.auth_url]
extra_body = {'type': 'openstack', 'is_default': False}
self._test_create_resource(self._RESOURCE, cmd, None, my_id, args,
position_names, position_values,
extra_body=extra_body)
def test_list_vims(self):
cmd = vim.ListVIM(test_cli10.MyApp(sys.stdout), None)
self._test_list_resources(self._RESOURCES, cmd, True)
def test_show_vim_id(self):
cmd = vim.ShowVIM(test_cli10.MyApp(sys.stdout), None)
args = ['--fields', 'id', self.test_id]
self._test_show_resource(self._RESOURCE, cmd, self.test_id, args,
['id'])
def test_show_vim_id_name(self):
cmd = vim.ShowVIM(test_cli10.MyApp(sys.stdout), None)
args = ['--fields', 'id', '--fields', 'name', self.test_id]
self._test_show_resource(self._RESOURCE, cmd, self.test_id,
args, ['id', 'name'])
def test_update_vim(self):
cmd = vim.UpdateVIM(test_cli10.MyApp(sys.stdout), None)
update_config = {'username': 'xyz', 'password': '12345',
'project_name': 'abc',
'project_domain_name': 'prj_domain_name',
'user_domain_name': 'user_domain_name'}
my_id = 'my-id'
key = 'config'
value = str(update_config)
extra_fields = {'vim_project': self.vim_project, 'auth_cred':
self.auth_cred, 'is_default': False}
self._test_update_resource(self._RESOURCE, cmd, my_id, [my_id,
'--%s' %
key, value],
extra_fields)
def test_delete_vim(self):
cmd = vim.DeleteVIM(test_cli10.MyApp(sys.stdout), None)
my_id = 'my-id'
args = [my_id]
self._test_delete_resource(self._RESOURCE, cmd, my_id, args)

View File

@@ -0,0 +1,159 @@
# Copyright 2014 Intel Corporation
# All Rights Reserved.
#
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import mox
import sys
from tackerclient import shell
from tackerclient.tacker import v1_0 as tackerV1_0
from tackerclient.tacker.v1_0.vm import vnf
from tackerclient.tests.unit import test_cli10
API_VERSION = "1.0"
FORMAT = 'json'
TOKEN = 'testtoken'
ENDURL = 'localurl'
class CLITestV10VmVNFJSON(test_cli10.CLITestV10Base):
_RESOURCE = 'vnf'
_RESOURCES = 'vnfs'
def setUp(self):
plurals = {'vnfs': 'vnf'}
super(CLITestV10VmVNFJSON, self).setUp(plurals=plurals)
def _test_create_resource(self, resource, cmd,
name, myid, args,
position_names, position_values, tenant_id=None,
tags=None, admin_state_up=True, extra_body=None,
**kwargs):
self.mox.StubOutWithMock(cmd, "get_client")
self.mox.StubOutWithMock(self.client.httpclient, "request")
cmd.get_client().MultipleTimes().AndReturn(self.client)
non_admin_status_resources = ['vnfd', 'vnf']
if (resource in non_admin_status_resources):
body = {resource: {}, }
else:
body = {resource: {'admin_state_up': admin_state_up, }, }
if tenant_id:
body[resource].update({'tenant_id': tenant_id})
if tags:
body[resource].update({'tags': tags})
if extra_body:
body[resource].update(extra_body)
body[resource].update(kwargs)
for i in range(len(position_names)):
body[resource].update({position_names[i]: position_values[i]})
ress = {resource:
{self.id_field: myid}, }
if name:
ress[resource].update({'name': name})
self.client.format = self.format
resstr = self.client.serialize(ress)
# url method body
resource_plural = tackerV1_0._get_resource_plural(resource,
self.client)
path = getattr(self.client, resource_plural + "_path")
# Work around for LP #1217791. XML deserializer called from
# MyComparator does not decodes XML string correctly.
if self.format == 'json':
mox_body = test_cli10.MyComparator(body, self.client)
else:
mox_body = self.client.serialize(body)
self.client.httpclient.request(
test_cli10.end_url(path, format=self.format), 'POST',
body=mox_body,
headers=mox.ContainsKeyValue(
'X-Auth-Token', TOKEN)).AndReturn((
test_cli10.MyResp(200), resstr))
args.extend(['--request-format', self.format])
args.extend(['--vnfd-id', 'vnfd'])
self.mox.ReplayAll()
cmd_parser = cmd.get_parser('create_' + resource)
shell.run_command(cmd, cmd_parser, args)
self.mox.VerifyAll()
def test_create_vnf_all_params(self):
cmd = vnf.CreateVNF(test_cli10.MyApp(sys.stdout), None)
my_id = 'my-id'
vnfd_id = 'vnfd'
vim_id = 'vim_id'
region_name = 'region'
key = 'key'
value = 'value'
args = [
'--vnfd-id', vnfd_id,
'--vim-id', vim_id,
'--vim-region-name', region_name,
'--%s' % key, value]
position_names = ['vnfd_id', 'vim_id', 'attributes']
position_values = [vnfd_id, vim_id, {}]
extra_body = {key: value, 'placement_attr': {'region_name':
region_name}}
self._test_create_resource(self._RESOURCE, cmd, None, my_id,
args, position_names, position_values,
extra_body=extra_body)
def test_create_vnf_with_mandatory_params(self):
cmd = vnf.CreateVNF(test_cli10.MyApp(sys.stdout), None)
my_id = 'my-id'
vnfd_id = 'vnfd'
args = [
'--vnfd-id', vnfd_id,
]
position_names = ['vnfd_id', 'attributes']
position_values = [vnfd_id, {}]
self._test_create_resource(self._RESOURCE, cmd, None, my_id,
args, position_names, position_values)
def test_list_vnfs(self):
cmd = vnf.ListVNF(test_cli10.MyApp(sys.stdout), None)
self._test_list_resources(self._RESOURCES, cmd, True)
def test_list_vnfs_pagenation(self):
cmd = vnf.ListVNF(test_cli10.MyApp(sys.stdout), None)
self._test_list_resources(self._RESOURCES, cmd, True)
def test_show_vnf_id(self):
cmd = vnf.ShowVNF(test_cli10.MyApp(sys.stdout), None)
args = ['--fields', 'id', self.test_id]
self._test_show_resource(self._RESOURCE, cmd, self.test_id, args,
['id'])
def test_show_vnf_id_name(self):
cmd = vnf.ShowVNF(test_cli10.MyApp(sys.stdout), None)
args = ['--fields', 'id', '--fields', 'name', self.test_id]
self._test_show_resource(self._RESOURCE, cmd, self.test_id,
args, ['id', 'name'])
def test_update_vnf(self):
cmd = vnf.UpdateVNF(test_cli10.MyApp(sys.stdout), None)
my_id = 'my-id'
key = 'new_key'
value = 'new-value'
self._test_update_resource(self._RESOURCE, cmd, my_id,
[my_id, '--%s' % key, value],
{key: value})
def test_delete_vnf(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)

View File

@@ -0,0 +1,96 @@
# Copyright 2014 Intel Corporation
# All Rights Reserved.
#
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import sys
from tackerclient.tacker.v1_0.vm import vnfd
from tackerclient.tests.unit import test_cli10
class CLITestV10VmVNFDJSON(test_cli10.CLITestV10Base):
_RESOURCE = 'vnfd'
_RESOURCES = 'vnfds'
def setUp(self):
plurals = {'vnfds': 'vnfd'}
super(CLITestV10VmVNFDJSON, self).setUp(plurals=plurals)
def test_create_vnfd_all_params(self):
cmd = vnfd.CreateVNFD(
test_cli10.MyApp(sys.stdout), None)
my_id = 'my-id'
name = 'my-name'
mgmt_driver = 'noop'
infra_driver = 'heat'
attr_key = 'vnfd'
attr_val = 'vnfd'
args = [
'--name', name,
'--vnfd', 'vnfd'
]
position_names = ['name', 'mgmt_driver', 'infra_driver']
position_values = [name, mgmt_driver, infra_driver]
extra_body = {
'service_types': [{'service_type': 'vnfd'}],
'attributes': {attr_key: attr_val},
}
self._test_create_resource(self._RESOURCE, cmd, None, my_id,
args, position_names, position_values,
extra_body=extra_body)
def test_create_vnfd_with_mandatory_params(self):
cmd = vnfd.CreateVNFD(
test_cli10.MyApp(sys.stdout), None)
my_id = 'my-id'
mgmt_driver = 'noop'
infra_driver = 'heat'
args = ['--vnfd', 'vnfd', ]
position_names = ['mgmt_driver', 'infra_driver']
position_values = [mgmt_driver, infra_driver]
extra_body = {
'service_types': [{'service_type': 'vnfd'}],
'attributes': {'vnfd': 'vnfd'}
}
self._test_create_resource(self._RESOURCE, cmd, None, my_id,
args, position_names, position_values,
extra_body=extra_body)
def test_list_vnfds(self):
cmd = vnfd.ListVNFD(test_cli10.MyApp(sys.stdout), None)
self._test_list_resources(self._RESOURCES, cmd, True)
def test_list_vnfds_pagenation(self):
cmd = vnfd.ListVNFD(test_cli10.MyApp(sys.stdout), None)
self._test_list_resources(self._RESOURCES, cmd, True)
def test_show_vnfd_id(self):
cmd = vnfd.ShowVNFD(test_cli10.MyApp(sys.stdout), None)
args = ['--fields', 'id', self.test_id]
self._test_show_resource(self._RESOURCE, cmd, self.test_id, args,
['id'])
def test_show_vnfd_id_name(self):
cmd = vnfd.ShowVNFD(test_cli10.MyApp(sys.stdout), None)
args = ['--fields', 'id', '--fields', 'name', self.test_id]
self._test_show_resource(self._RESOURCE, cmd, self.test_id,
args, ['id', 'name'])
def test_delete_vnfd(self):
cmd = vnfd.DeleteVNFD(
test_cli10.MyApp(sys.stdout), None)
my_id = 'my-id'
args = [my_id]
self._test_delete_resource(self._RESOURCE, cmd, my_id, args)

View File

@@ -28,9 +28,9 @@ from tackerclient.common import serializer
from tackerclient.common import utils
from tackerclient.i18n import _
_logger = logging.getLogger(__name__)
DEFAULT_DESC_LENGTH = 25
DEFAULT_ERROR_REASON_LENGTH = 100
def exception_handler_v10(status_code, error_content):
@@ -87,8 +87,8 @@ def exception_handler_v10(status_code, error_content):
class APIParamsCall(object):
"""A Decorator to add support for format and tenant overriding and filters.
"""
"""A Decorator to support formating and tenant overriding and filters."""
def __init__(self, function):
self.function = function
@@ -334,18 +334,14 @@ class Client(ClientBase):
extensions_path = "/extensions"
extension_path = "/extensions/%s"
device_templates_path = '/device-templates'
device_template_path = '/device-templates/%s'
devices_path = '/devices'
device_path = '/devices/%s'
interface_attach_path = '/devices/%s/attach_interface'
interface_detach_path = '/devices/%s/detach_interface'
vnfds_path = '/vnfds'
vnfd_path = '/vnfds/%s'
vnfs_path = '/vnfs'
vnf_path = '/vnfs/%s'
vims_path = '/vims'
vim_path = '/vims/%s'
# API has no way to report plurals, so we have to hard code them
# EXTED_PLURALS = {}
@@ -359,65 +355,6 @@ class Client(ClientBase):
"""Fetch a list of all exts on server side."""
return self.get(self.extension_path % ext_alias, params=_params)
@utils.deprecated('list_device_templates')
def list_device_templates(self, retrieve_all=True, **_params):
return self.list('device_templates', self.device_templates_path,
retrieve_all, **_params)
@APIParamsCall
@utils.deprecated('show_device_template')
def show_device_template(self, device_template, **_params):
return self.get(self.device_template_path % device_template,
params=_params)
@APIParamsCall
@utils.deprecated('update_device_template')
def update_device_template(self, device_template, body=None):
return self.put(self.device_template_path % device_template, body=body)
@APIParamsCall
@utils.deprecated('create_device_template')
def create_device_template(self, body=None):
return self.post(self.device_templates_path, body=body)
@APIParamsCall
@utils.deprecated('delete_device_template')
def delete_device_template(self, device_template):
return self.delete(self.device_template_path % device_template)
@APIParamsCall
@utils.deprecated('list_devices')
def list_devices(self, retrieve_all=True, **_params):
return self.list('devices', self.devices_path, retrieve_all, **_params)
@APIParamsCall
@utils.deprecated('show_device')
def show_device(self, device, **_params):
return self.get(self.device_path % device, params=_params)
@APIParamsCall
@utils.deprecated('update_device')
def update_device(self, device, body=None):
return self.put(self.device_path % device, body=body)
@APIParamsCall
@utils.deprecated('create_device')
def create_device(self, body=None):
return self.post(self.devices_path, body=body)
@APIParamsCall
@utils.deprecated('delete_device')
def delete_device(self, device):
return self.delete(self.device_path % device)
@APIParamsCall
def attach_interface(self, device, body=None):
return self.put(self.attach_interface_path % device, body)
@APIParamsCall
def detach_interface(self, device, body=None):
return self.put(self.detach_interface_path % device, body)
_VNFD = "vnfd"
@APIParamsCall
@@ -454,7 +391,15 @@ class Client(ClientBase):
@APIParamsCall
def list_vnfs(self, retrieve_all=True, **_params):
return self.list('vnfs', self.vnfs_path, retrieve_all, **_params)
vnfs = self.list('vnfs', self.vnfs_path, retrieve_all, **_params)
for vnf in vnfs['vnfs']:
error_reason = vnf.get('error_reason', None)
if error_reason and \
len(error_reason) > DEFAULT_ERROR_REASON_LENGTH:
vnf['error_reason'] = error_reason[
:DEFAULT_ERROR_REASON_LENGTH]
vnf['error_reason'] += '...'
return vnfs
@APIParamsCall
def show_vnf(self, vnf, **_params):
@@ -471,3 +416,25 @@ class Client(ClientBase):
@APIParamsCall
def update_vnf(self, vnf, body=None):
return self.put(self.vnf_path % vnf, body=body)
@APIParamsCall
def show_vim(self, vim, **_params):
return self.get(self.vim_path % vim, params=_params)
_VIM = "vim"
@APIParamsCall
def create_vim(self, body=None):
return self.post(self.vims_path, body=body)
@APIParamsCall
def delete_vim(self, vim):
return self.delete(self.vim_path % vim)
@APIParamsCall
def update_vim(self, vim, body=None):
return self.put(self.vim_path % vim, body=body)
@APIParamsCall
def list_vims(self, retrieve_all=True, **_params):
return self.list('vims', self.vims_path, retrieve_all, **_params)

View File

@@ -12,9 +12,6 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# @author: Carl Baldwin, Hewlett-Packard
import pbr.version

View File

@@ -1,16 +1,19 @@
# 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.10.2,<0.11
cliff-tablib>=1.0
coverage>=3.6
discover
fixtures>=1.3.1
mox>=0.5.3
flake8>=2.2.4,<=2.4.1
pep8==1.5.7
pyflakes==0.8.1
python-subunit>=0.0.18
sphinx>=1.1.2,!=1.2.0,!=1.3b1,<1.3
testrepository>=0.0.18
testtools>=1.4.0
hacking<0.11,>=0.10.2
cliff-tablib>=1.0 # Apache-2.0
coverage>=3.6 # Apache-2.0
discover # BSD
fixtures>=3.0.0 # Apache-2.0/BSD
mox>=0.5.3 # Apache-2.0
flake8<2.6.0,>=2.5.4 # MIT
pep8==1.5.7 # MIT
pyflakes==0.8.1 # MIT
python-subunit>=0.0.18 # Apache-2.0/BSD
sphinx!=1.3b1,<1.3,>=1.2.1 # BSD
testrepository>=0.0.18 # Apache-2.0/BSD
testtools>=1.4.0 # MIT
# releasenotes
reno>=1.8.0 # Apache2

19
tox.ini
View File

@@ -1,5 +1,5 @@
[tox]
envlist = py26,py27,py33,py34,pypy,pep8
envlist = py34,py27,pypy,pep8
minversion = 1.6
skipsdist = True
@@ -21,21 +21,14 @@ distribute = false
[testenv:venv]
commands = {posargs}
[testenv:releasenotes]
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}'
[tox:jenkins]
downloadcache = ~/cache/pip
[flake8]
# E125 continuation line does not distinguish itself from next logical line
# H302 import only modules
# H105 Don't use author tags
# H405 multi line docstring summary not separated with an empty line
# E265 block comment should start with '# '
# H238 old style class declaration, use new style (inherit from `object`)
# E129 visually indented line with same indent as next logical line
# E113 unexpected indentation
ignore = E125,H302,H105,H405,E265,H238,E129,E113
ignore = E125
show-source = true
exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,tools
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,tools