Compare commits
99 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0aa7efd292 | ||
|
|
e88619e985 | ||
|
|
602c06373d | ||
|
|
c9ac53a722 | ||
|
|
00d87bde7e | ||
|
|
9910cbc746 | ||
|
|
431a79ba3d | ||
|
|
3c6f82dfaf | ||
|
|
af6db3f107 | ||
|
|
6fd6b66466 | ||
|
|
654ad24e3b | ||
|
|
b35672be34 | ||
|
|
04298efa2d | ||
|
|
a5ad0bc3d3 | ||
|
|
f42187050f | ||
|
|
a4dcd7634a | ||
|
|
885b53b6d0 | ||
|
|
805ca417b8 | ||
|
|
ffa34ca3d4 | ||
|
|
8605e2d254 | ||
|
|
fc363ee49a | ||
|
|
67c034b8a1 | ||
|
|
d5ee6c3b50 | ||
|
|
f48c03db9b | ||
|
|
51e493e767 | ||
|
|
4d0003084c | ||
|
|
d47747260f | ||
|
|
05ac550251 | ||
|
|
331968496a | ||
|
|
af528bec28 | ||
|
|
c32028201f | ||
|
|
a6bd47cc80 | ||
|
|
cce904b24b | ||
|
|
c784737bcf | ||
|
|
bc4b14ad45 | ||
|
|
4312359ae3 | ||
|
|
f7b11685c4 | ||
|
|
d9a58b3207 | ||
|
|
12e2230b3b | ||
|
|
07444203ec | ||
|
|
83cfe9306b | ||
|
|
25337d6367 | ||
|
|
1ea8b9899d | ||
|
|
27e85a9ae3 | ||
|
|
6a2df0a918 | ||
|
|
20d3658353 | ||
|
|
854afc4392 | ||
|
|
8dd822ae97 | ||
|
|
82e32687d3 | ||
|
|
00d3c9771c | ||
|
|
de4aeb2736 | ||
|
|
28853ac0aa | ||
|
|
5340d09287 | ||
|
|
231478cb3a | ||
|
|
23dc390b4f | ||
|
|
639e095ce6 | ||
|
|
a0bf314c0e | ||
|
|
d5eebd21d5 | ||
|
|
8ac96559e3 | ||
|
|
80220f372b | ||
|
|
5e6e59aa46 | ||
|
|
9c5461b1dd | ||
|
|
9f9cc686a3 | ||
|
|
863084fd99 | ||
|
|
633848c21a | ||
|
|
77e60c009d | ||
|
|
cec2ae1d2d | ||
|
|
6596d5222f | ||
|
|
03f2605d0a | ||
|
|
4ba764140c | ||
|
|
4878cc999c | ||
|
|
3b96c5f534 | ||
|
|
8ec6af9b6c | ||
|
|
8a64606654 | ||
|
|
d9695ad31f | ||
|
|
74f5ac30e7 | ||
|
|
5903bc9338 | ||
|
|
ba46bbf7ac | ||
|
|
95c07f50e1 | ||
|
|
5f340b9944 | ||
|
|
4e33e17e22 | ||
|
|
0820bb1a10 | ||
|
|
00eb1118c4 | ||
|
|
ca1ad7c930 | ||
|
|
73a38434fd | ||
|
|
196ba01bac | ||
|
|
d224c22a46 | ||
|
|
d621916252 | ||
|
|
b8dff342e0 | ||
|
|
b3e5932385 | ||
|
|
f342ea9c7a | ||
|
|
48f1111f8f | ||
|
|
9cc1ebe824 | ||
|
|
ad5381a91d | ||
|
|
5de35c1cf2 | ||
|
|
42a1a923b9 | ||
|
|
6242db9fb1 | ||
|
|
83db572f62 | ||
|
|
f0aa619ffe |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -17,5 +17,9 @@ run_tests.log
|
||||
.autogenerated
|
||||
.coverage
|
||||
.testrepository/
|
||||
.idea/
|
||||
.tox/
|
||||
.venv/
|
||||
|
||||
# Files created by releasenotes build
|
||||
releasenotes/build
|
||||
|
||||
@@ -2,4 +2,3 @@
|
||||
host=review.openstack.org
|
||||
port=29418
|
||||
project=openstack/python-tackerclient.git
|
||||
defaultbranch=stable/liberty
|
||||
|
||||
@@ -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::
|
||||
|
||||
@@ -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
|
||||
0
releasenotes/source/_templates/.placeholder
Normal file
0
releasenotes/source/_templates/.placeholder
Normal file
261
releasenotes/source/conf.py
Normal file
261
releasenotes/source/conf.py
Normal 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'
|
||||
9
releasenotes/source/index.rst
Normal file
9
releasenotes/source/index.rst
Normal file
@@ -0,0 +1,9 @@
|
||||
Python-TackerClient Release Notes
|
||||
=================================
|
||||
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
unreleased
|
||||
5
releasenotes/source/unreleased.rst
Normal file
5
releasenotes/source/unreleased.rst
Normal file
@@ -0,0 +1,5 @@
|
||||
============================
|
||||
Current Series Release Notes
|
||||
============================
|
||||
|
||||
.. release-notes::
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
|
||||
50
tackerclient/common/_i18n.py
Normal file
50
tackerclient/common/_i18n.py
Normal 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)
|
||||
@@ -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."""
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'))
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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__)
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
|
||||
0
tackerclient/tacker/v1_0/nfvo/__init__.py
Normal file
0
tackerclient/tacker/v1_0/nfvo/__init__.py
Normal file
131
tackerclient/tacker/v1_0/nfvo/vim.py
Normal file
131
tackerclient/tacker/v1_0/nfvo/vim.py
Normal 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
|
||||
39
tackerclient/tacker/v1_0/nfvo/vim_utils.py
Normal file
39
tackerclient/tacker/v1_0/nfvo/vim_utils.py
Normal 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', '')}
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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: {}}
|
||||
|
||||
@@ -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!'))
|
||||
|
||||
0
tackerclient/tests/unit/nfvo/__init__.py
Normal file
0
tackerclient/tests/unit/nfvo/__init__.py
Normal file
52
tackerclient/tests/unit/nfvo/test_vim_utils.py
Normal file
52
tackerclient/tests/unit/nfvo/test_vim_utils.py
Normal 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)
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -19,7 +19,7 @@ from tackerclient.common import exceptions
|
||||
from tackerclient.common import validators
|
||||
|
||||
|
||||
class FakeParsedArgs():
|
||||
class FakeParsedArgs(object):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
117
tackerclient/tests/unit/vm/test_cli10_vim.py
Normal file
117
tackerclient/tests/unit/vm/test_cli10_vim.py
Normal 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)
|
||||
159
tackerclient/tests/unit/vm/test_cli10_vnf.py
Normal file
159
tackerclient/tests/unit/vm/test_cli10_vnf.py
Normal 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)
|
||||
96
tackerclient/tests/unit/vm/test_cli10_vnfd.py
Normal file
96
tackerclient/tests/unit/vm/test_cli10_vnfd.py
Normal 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)
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
19
tox.ini
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user