Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d2c241194f |
@@ -4,4 +4,4 @@ source = tackerclient
|
||||
omit = tackerclient/openstack/*,tackerclient/tests/*
|
||||
|
||||
[report]
|
||||
ignore_errors = True
|
||||
ignore-errors = True
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -17,9 +17,5 @@ run_tests.log
|
||||
.autogenerated
|
||||
.coverage
|
||||
.testrepository/
|
||||
.idea/
|
||||
.tox/
|
||||
.venv/
|
||||
|
||||
# Files created by releasenotes build
|
||||
releasenotes/build
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[DEFAULT]
|
||||
test_command=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 ${PYTHON:-python} -m subunit.run discover -t ./ ${OS_TEST_PATH:-./tackerclient/tests/unit/vm} $LISTOPT $IDOPTION
|
||||
test_command=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 ${PYTHON:-python} -m subunit.run discover -t ./ . $LISTOPT $IDOPTION
|
||||
test_id_option=--load-list $IDFILE
|
||||
test_list_option=--list
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Tacker Style Commandments
|
||||
Neutron Style Commandments
|
||||
================================
|
||||
|
||||
- Step 1: Read the OpenStack Style Commandments
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
project = 'python-tackerclient'
|
||||
project = 'python-neutronclient'
|
||||
|
||||
# -- General configuration ---------------------------------------------
|
||||
|
||||
|
||||
@@ -1,7 +1,19 @@
|
||||
Python bindings to the OpenStack Tacker API
|
||||
Python bindings to the OpenStack Network API
|
||||
============================================
|
||||
|
||||
In order to use the python tacker client directly, you must first obtain an auth token and identify which endpoint you wish to speak to. Once you have done so, you can use the API.
|
||||
In order to use the python neutron client directly, you must first obtain an auth token and identify which endpoint you wish to speak to. Once you have done so, you can use the API like so::
|
||||
|
||||
>>> import logging
|
||||
>>> from neutronclient.neutron import client
|
||||
>>> logging.basicConfig(level=logging.DEBUG)
|
||||
>>> neutron = client.Client('2.0', endpoint_url=OS_URL, token=OS_TOKEN)
|
||||
>>> neutron.format = 'json'
|
||||
>>> network = {'name': 'mynetwork', 'admin_state_up': True}
|
||||
>>> neutron.create_network({'network':network})
|
||||
>>> networks = neutron.list_networks(name='mynetwork')
|
||||
>>> print networks
|
||||
>>> network_id = networks['networks'][0]['id']
|
||||
>>> neutron.delete_network(network_id)
|
||||
|
||||
|
||||
Command-line Tool
|
||||
@@ -15,11 +27,37 @@ 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:9890/
|
||||
export OS_URL=http://neutron.example.org:9696/
|
||||
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::
|
||||
If neutron 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::
|
||||
|
||||
export OS_AUTH_STRATEGY=noauth
|
||||
|
||||
Once you've configured your authentication parameters, you can run ``tacker -h`` to see a complete listing of available commands.
|
||||
Once you've configured your authentication parameters, you can run ``neutron -h`` to see a complete listing of available commands.
|
||||
|
||||
Release Notes
|
||||
=============
|
||||
|
||||
2.0
|
||||
-----
|
||||
* support Neutron API 2.0
|
||||
|
||||
2.2.0
|
||||
-----
|
||||
* add security group commands
|
||||
* add Lbaas commands
|
||||
* allow options put after positional arguments
|
||||
* add NVP queue and net gateway commands
|
||||
* add commands for agent management extensions
|
||||
* add commands for DHCP and L3 agents scheduling
|
||||
* support XML request format
|
||||
* support pagination options
|
||||
|
||||
2.2.2
|
||||
-----
|
||||
* improved support for listing a large number of filtered subnets
|
||||
* add --endpoint-type and OS_ENDPOINT_TYPE to shell client
|
||||
* made the publicURL the default endpoint instead of adminURL
|
||||
* add ability to update security group name (requires 2013.2-Havana or later)
|
||||
* add flake8 and pbr support for testing and building
|
||||
|
||||
7
openstack-common.conf
Normal file
7
openstack-common.conf
Normal file
@@ -0,0 +1,7 @@
|
||||
[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
|
||||
@@ -1,261 +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.
|
||||
#
|
||||
# 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'
|
||||
@@ -1,9 +0,0 @@
|
||||
Python-TackerClient Release Notes
|
||||
=================================
|
||||
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
unreleased
|
||||
@@ -1,5 +0,0 @@
|
||||
============================
|
||||
Current Series Release Notes
|
||||
============================
|
||||
|
||||
.. release-notes::
|
||||
@@ -1,16 +1,10 @@
|
||||
# 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 # 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
|
||||
pbr>=0.6,!=0.7,<1.0
|
||||
argparse
|
||||
cliff>=1.4.3
|
||||
iso8601>=0.1.9
|
||||
netaddr>=0.7.6
|
||||
requests>=1.1
|
||||
python-keystoneclient>=0.9.0
|
||||
simplejson>=2.0.9
|
||||
six>=1.6.0
|
||||
Babel>=1.3
|
||||
|
||||
10
setup.cfg
10
setup.cfg
@@ -1,9 +1,9 @@
|
||||
[metadata]
|
||||
name = python-tackerclient
|
||||
summary = CLI and Client Library for OpenStack Tacker
|
||||
summary = CLI and Client Library for OpenStack Networking
|
||||
description-file =
|
||||
README.rst
|
||||
author = OpenStack
|
||||
author = OpenStack Networking Project
|
||||
author-email = openstack-dev@lists.openstack.org
|
||||
home-page = http://www.openstack.org/
|
||||
classifier =
|
||||
@@ -16,6 +16,7 @@ classifier =
|
||||
Programming Language :: Python
|
||||
Programming Language :: Python :: 2
|
||||
Programming Language :: Python :: 2.7
|
||||
Programming Language :: Python :: 2.6
|
||||
|
||||
[files]
|
||||
packages =
|
||||
@@ -34,10 +35,5 @@ 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
|
||||
|
||||
3
setup.py
3
setup.py
@@ -1,3 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -25,5 +26,5 @@ except ImportError:
|
||||
pass
|
||||
|
||||
setuptools.setup(
|
||||
setup_requires=['pbr>=1.8'],
|
||||
setup_requires=['pbr'],
|
||||
pbr=True)
|
||||
|
||||
@@ -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='nfv-orchestration',
|
||||
service_type='servicevm',
|
||||
**kwargs):
|
||||
|
||||
self.username = username
|
||||
@@ -171,6 +171,8 @@ 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
|
||||
@@ -185,7 +187,7 @@ class HTTPClient(object):
|
||||
|
||||
if not self.endpoint_url:
|
||||
self.endpoint_url = self.service_catalog.url_for(
|
||||
region_name=self.region_name,
|
||||
attr='region', filter_value=self.region_name,
|
||||
service_type=self.service_type,
|
||||
endpoint_type=self.endpoint_type)
|
||||
|
||||
@@ -254,7 +256,7 @@ class HTTPClient(object):
|
||||
|
||||
body = json.loads(body)
|
||||
for endpoint in body.get('endpoints', []):
|
||||
if (endpoint['type'] == 'nfv-orchestration' and
|
||||
if (endpoint['type'] == 'servicevm' and
|
||||
endpoint.get('region') == self.region_name):
|
||||
if self.endpoint_type not in endpoint:
|
||||
raise exceptions.EndpointTypeNotFound(
|
||||
@@ -357,7 +359,7 @@ def construct_http_client(username=None,
|
||||
log_credentials=None,
|
||||
auth_strategy='keystone',
|
||||
ca_cert=None,
|
||||
service_type='nfv-orchestration',
|
||||
service_type='servicevm',
|
||||
session=None,
|
||||
**kwargs):
|
||||
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
# 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,10 +17,15 @@
|
||||
"""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."""
|
||||
|
||||
|
||||
@@ -35,4 +35,5 @@ TYPE_DICT = "dict"
|
||||
|
||||
|
||||
PLURALS = {'templates': 'template',
|
||||
'devices': 'device'}
|
||||
'devices': 'device',
|
||||
'services': 'service'}
|
||||
|
||||
@@ -148,6 +148,12 @@ 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
|
||||
|
||||
@@ -215,9 +221,7 @@ class CommandError(TackerCLIError):
|
||||
|
||||
|
||||
class UnsupportedVersion(TackerCLIError):
|
||||
"""Unsupported Version.
|
||||
|
||||
Indicates that the user is trying to use an unsupported version of
|
||||
"""Indicates that the user is trying to use an unsupported version of
|
||||
the API.
|
||||
"""
|
||||
pass
|
||||
|
||||
@@ -17,7 +17,7 @@ import logging
|
||||
from xml.etree import ElementTree as etree
|
||||
from xml.parsers import expat
|
||||
|
||||
from oslo_serialization import jsonutils
|
||||
from oslo.serialization import jsonutils
|
||||
import six
|
||||
|
||||
from tackerclient.common import constants
|
||||
|
||||
@@ -21,10 +21,9 @@ import argparse
|
||||
import logging
|
||||
import os
|
||||
|
||||
from oslo_utils import encodeutils
|
||||
from oslo_utils import importutils
|
||||
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 _
|
||||
@@ -172,10 +171,3 @@ def add_boolean_argument(parser, name, **kwargs):
|
||||
choices=['True', 'true', 'False', 'false'],
|
||||
default=default,
|
||||
**kwargs)
|
||||
|
||||
|
||||
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
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import oslo_i18n as i18n
|
||||
from oslo import i18n
|
||||
|
||||
_translators = i18n.TranslatorFactory(domain='tackerclient')
|
||||
|
||||
|
||||
17
tackerclient/openstack/common/__init__.py
Normal file
17
tackerclient/openstack/common/__init__.py
Normal file
@@ -0,0 +1,17 @@
|
||||
#
|
||||
# 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'))
|
||||
320
tackerclient/openstack/common/gettextutils.py
Normal file
320
tackerclient/openstack/common/gettextutils.py
Normal file
@@ -0,0 +1,320 @@
|
||||
# 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)
|
||||
67
tackerclient/openstack/common/importutils.py
Normal file
67
tackerclient/openstack/common/importutils.py
Normal file
@@ -0,0 +1,67 @@
|
||||
# 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
|
||||
186
tackerclient/openstack/common/jsonutils.py
Normal file
186
tackerclient/openstack/common/jsonutils.py
Normal file
@@ -0,0 +1,186 @@
|
||||
# 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__)
|
||||
216
tackerclient/openstack/common/strutils.py
Normal file
216
tackerclient/openstack/common/strutils.py
Normal file
@@ -0,0 +1,216 @@
|
||||
# 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)
|
||||
186
tackerclient/openstack/common/timeutils.py
Normal file
186
tackerclient/openstack/common/timeutils.py
Normal file
@@ -0,0 +1,186 @@
|
||||
# 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,9 +31,9 @@ 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 import exceptions as ks_exc
|
||||
from keystoneclient.openstack.common.apiclient import exceptions as ks_exc
|
||||
from keystoneclient import session
|
||||
from oslo_utils import encodeutils
|
||||
from oslo.utils import encodeutils
|
||||
import six.moves.urllib.parse as urlparse
|
||||
|
||||
from cliff import app
|
||||
@@ -46,7 +46,8 @@ 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.nfvo import vim
|
||||
from tackerclient.tacker.v1_0.vm import device
|
||||
from tackerclient.tacker.v1_0.vm import device_template
|
||||
from tackerclient.tacker.v1_0.vm import vnf
|
||||
from tackerclient.tacker.v1_0.vm import vnfd
|
||||
from tackerclient.version import __version__
|
||||
@@ -102,13 +103,24 @@ 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,
|
||||
@@ -117,24 +129,17 @@ 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):
|
||||
"""Provides a custom action for the -h and --help options.
|
||||
"""Provide a custom action so the -h and --help options
|
||||
to the main app will print a list of the commands.
|
||||
|
||||
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 = []
|
||||
@@ -244,10 +249,8 @@ class TackerShell(app.App):
|
||||
|
||||
parser.add_argument(
|
||||
'--os-service-type', metavar='<os-service-type>',
|
||||
default=env('OS_TACKER_SERVICE_TYPE',
|
||||
default='nfv-orchestration'),
|
||||
help=_('Defaults to env[OS_TACKER_SERVICE_TYPE] or \
|
||||
nfv-orchestration.'))
|
||||
default=env('OS_SERVICEVM_SERVICE_TYPE', default='servicevm'),
|
||||
help=_('Defaults to env[OS_SERVICEVM_SERVICE_TYPE] or servicevm.'))
|
||||
|
||||
parser.add_argument(
|
||||
'--os-endpoint-type', metavar='<os-endpoint-type>',
|
||||
@@ -258,8 +261,7 @@ class TackerShell(app.App):
|
||||
# backward compatibility.
|
||||
parser.add_argument(
|
||||
'--service-type', metavar='<service-type>',
|
||||
default=env('OS_TACKER_SERVICE_TYPE',
|
||||
default='nfv-orchestration'),
|
||||
default=env('OS_SERVICEVM_SERVICE_TYPE', default='servicevm'),
|
||||
help=_('DEPRECATED! Use --os-service-type.'))
|
||||
|
||||
# FIXME(bklei): --endpoint-type is deprecated but kept in for
|
||||
@@ -554,9 +556,7 @@ class TackerShell(app.App):
|
||||
return 1
|
||||
|
||||
def authenticate_user(self):
|
||||
"""Authentication validation.
|
||||
|
||||
Make sure the user has provided all of the authentication
|
||||
"""Make sure the user has provided all of the authentication
|
||||
info we need.
|
||||
"""
|
||||
if self.options.os_auth_strategy == 'keystone':
|
||||
@@ -670,8 +670,7 @@ class TackerShell(app.App):
|
||||
|
||||
super(TackerShell, self).initialize_app(argv)
|
||||
|
||||
self.api_version = {'nfv-orchestration':
|
||||
self.api_version}
|
||||
self.api_version = {'servicevm': 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 = 'nfv-orchestration'
|
||||
API_NAME = 'servicevm'
|
||||
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,8 +61,7 @@ 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,
|
||||
|
||||
@@ -24,13 +24,13 @@ import re
|
||||
from cliff.formatters import table
|
||||
from cliff import lister
|
||||
from cliff import show
|
||||
from oslo_serialization import jsonutils
|
||||
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 six.iteritems(_options):
|
||||
for opt, optspec in _options.iteritems():
|
||||
_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 six.iteritems(temp_values):
|
||||
for key, value in temp_values.iteritems():
|
||||
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
|
||||
isinstance(arg_value[0], type(value[0]))):
|
||||
type(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 = 'nfv-orchestration'
|
||||
api = 'servicevm'
|
||||
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 six.iteritems(data[self.resource]):
|
||||
for k, v in data[self.resource].iteritems():
|
||||
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 = 'nfv-orchestration'
|
||||
api = 'servicevm'
|
||||
resource = None
|
||||
log = None
|
||||
remove_output_fields = []
|
||||
@@ -448,13 +448,14 @@ class CreateCommand(TackerCommand, show.ShowOne):
|
||||
info.pop(f)
|
||||
else:
|
||||
info = {'': ''}
|
||||
return zip(*sorted(six.iteritems(info)))
|
||||
return zip(*sorted(info.iteritems()))
|
||||
|
||||
|
||||
class UpdateCommand(TackerCommand):
|
||||
"""Update resource's information."""
|
||||
"""Update resource's information
|
||||
"""
|
||||
|
||||
api = 'nfv-orchestration'
|
||||
api = 'servicevm'
|
||||
resource = None
|
||||
log = None
|
||||
allow_names = True
|
||||
@@ -502,7 +503,7 @@ class DeleteCommand(TackerCommand):
|
||||
|
||||
"""
|
||||
|
||||
api = 'nfv-orchestration'
|
||||
api = 'servicevm'
|
||||
resource = None
|
||||
log = None
|
||||
allow_names = True
|
||||
@@ -542,7 +543,7 @@ class ListCommand(TackerCommand, lister.Lister):
|
||||
|
||||
"""
|
||||
|
||||
api = 'nfv-orchestration'
|
||||
api = 'servicevm'
|
||||
resource = None
|
||||
log = None
|
||||
_formatters = {}
|
||||
@@ -641,15 +642,11 @@ class ShowCommand(TackerCommand, show.ShowOne):
|
||||
|
||||
"""
|
||||
|
||||
api = 'nfv-orchestration'
|
||||
api = 'servicevm'
|
||||
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)
|
||||
@@ -658,7 +655,7 @@ class ShowCommand(TackerCommand, show.ShowOne):
|
||||
else:
|
||||
help_str = _('ID of %s to look up')
|
||||
parser.add_argument(
|
||||
'id', metavar=self.get_id(),
|
||||
'id', metavar=self.resource.upper(),
|
||||
help=help_str % self.resource)
|
||||
return parser
|
||||
|
||||
@@ -683,6 +680,6 @@ class ShowCommand(TackerCommand, show.ShowOne):
|
||||
self.format_output_data(data)
|
||||
resource = data[self.resource]
|
||||
if self.resource in data:
|
||||
return zip(*sorted(six.iteritems(resource)))
|
||||
return zip(*sorted(resource.iteritems()))
|
||||
else:
|
||||
return None
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
# under the License.
|
||||
#
|
||||
|
||||
from tackerclient.openstack.common.gettextutils import _
|
||||
from tackerclient.tacker import v1_0 as cmd_base
|
||||
|
||||
|
||||
@@ -30,5 +31,10 @@ class ShowExt(cmd_base.ShowCommand):
|
||||
resource = "extension"
|
||||
allow_names = False
|
||||
|
||||
def get_id(self):
|
||||
return 'EXT-ALIAS'
|
||||
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
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
# 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
|
||||
@@ -1,39 +0,0 @@
|
||||
# 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', '')}
|
||||
190
tackerclient/tacker/v1_0/vm/device.py
Normal file
190
tackerclient/tacker/v1_0/vm/device.py
Normal file
@@ -0,0 +1,190 @@
|
||||
#
|
||||
# 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.openstack.common.gettextutils import _
|
||||
from tackerclient.tacker import v1_0 as tackerV10
|
||||
|
||||
|
||||
_DEVICE = 'device'
|
||||
|
||||
|
||||
class ListDevice(tackerV10.ListCommand):
|
||||
"""List device that belong to a given tenant."""
|
||||
|
||||
resource = _DEVICE
|
||||
list_columns = ['id', 'name', 'description', 'mgmt_url', 'status']
|
||||
|
||||
|
||||
class ShowDevice(tackerV10.ShowCommand):
|
||||
"""show information of a given Device."""
|
||||
|
||||
resource = _DEVICE
|
||||
|
||||
|
||||
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')
|
||||
parser.add_argument(
|
||||
'--service-context',
|
||||
metavar='<network-id=network-uuid,subnet-id=subnet-uuid,'
|
||||
'port-id=port-uuid,router-id=router-uuid,'
|
||||
'role=role-string,index=int>',
|
||||
action='append',
|
||||
dest='service_context',
|
||||
default=[],
|
||||
help='service context to insert service')
|
||||
|
||||
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
|
||||
if parsed_args.service_context:
|
||||
try:
|
||||
service_contexts = [dict(
|
||||
(k.replace('-', '_'), v)
|
||||
for k, v in (key_value.split('=', 1)
|
||||
for key_value in entry_string.split(',')))
|
||||
for entry_string in parsed_args.service_context]
|
||||
except ValueError:
|
||||
msg = (_('invalid argument for --service-context %s') %
|
||||
parsed_args.service_context)
|
||||
raise exceptions.TackerCLIError(msg)
|
||||
|
||||
if service_contexts:
|
||||
body[self.resource]['service_contexts'] = service_contexts
|
||||
|
||||
tackerV10.update_dict(parsed_args, body[self.resource], ['tenant_id'])
|
||||
return body
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
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)
|
||||
94
tackerclient/tacker/v1_0/vm/device_template.py
Normal file
94
tackerclient/tacker/v1_0/vm/device_template.py
Normal file
@@ -0,0 +1,94 @@
|
||||
#
|
||||
# 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.tacker import v1_0 as tackerV10
|
||||
|
||||
|
||||
_DEVICE_TEMPLATE = "device_template"
|
||||
|
||||
|
||||
class ListDeviceTemplate(tackerV10.ListCommand):
|
||||
"""List device template that belong to a given tenant."""
|
||||
|
||||
resource = _DEVICE_TEMPLATE
|
||||
|
||||
|
||||
class ShowDeviceTemplate(tackerV10.ShowCommand):
|
||||
"""show information of a given DeviceTemplate."""
|
||||
|
||||
resource = _DEVICE_TEMPLATE
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
class UpdateDeviceTemplate(tackerV10.UpdateCommand):
|
||||
"""Update a given DeviceTemplate."""
|
||||
|
||||
resource = _DEVICE_TEMPLATE
|
||||
allow_names = False
|
||||
|
||||
|
||||
class DeleteDeviceTemplate(tackerV10.DeleteCommand):
|
||||
"""Delete a given DeviceTemplate."""
|
||||
resource = _DEVICE_TEMPLATE
|
||||
@@ -1,5 +1,7 @@
|
||||
#
|
||||
# Copyright 2013 Intel Corporation
|
||||
# Copyright 2013 Intel
|
||||
# Copyright 2013 Isaku Yamahata <isaku.yamahata at intel com>
|
||||
# <isaku.yamahata at gmail com>
|
||||
# All Rights Reserved.
|
||||
#
|
||||
#
|
||||
@@ -14,6 +16,8 @@
|
||||
# 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
|
||||
|
||||
@@ -22,21 +26,20 @@ _VNF = 'vnf'
|
||||
|
||||
|
||||
class ListVNF(tackerV10.ListCommand):
|
||||
"""List VNF that belong to a given tenant."""
|
||||
"""List device that belong to a given tenant."""
|
||||
|
||||
resource = _VNF
|
||||
list_columns = ['id', 'name', 'description', 'mgmt_url', 'status',
|
||||
'vim_id', 'placement_attr', 'error_reason']
|
||||
list_columns = ['id', 'name', 'description', 'mgmt_url', 'status']
|
||||
|
||||
|
||||
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"]
|
||||
@@ -44,71 +47,41 @@ class CreateVNF(tackerV10.CreateCommand):
|
||||
def add_known_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'--name',
|
||||
help='Set a name for the VNF')
|
||||
vnfd_group = parser.add_mutually_exclusive_group(required=True)
|
||||
vnfd_group.add_argument(
|
||||
help='Set a name for the vnf')
|
||||
group = parser.add_mutually_exclusive_group(required=True)
|
||||
group.add_argument(
|
||||
'--vnfd-id',
|
||||
help='VNFD ID to use as template to create VNF')
|
||||
vnfd_group.add_argument(
|
||||
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 data')
|
||||
parser.add_argument(
|
||||
'--param-file',
|
||||
help='Specify parameter yaml file'
|
||||
)
|
||||
help='specify config yaml file')
|
||||
|
||||
def args2body(self, parsed_args):
|
||||
args = {'attributes': {}}
|
||||
body = {self.resource: args}
|
||||
# config arg passed as data overrides config yaml when both args passed
|
||||
body = {self.resource: {}}
|
||||
if parsed_args.config_file:
|
||||
with open(parsed_args.config_file) as f:
|
||||
config_yaml = f.read()
|
||||
args['attributes']['config'] = config_yaml
|
||||
body[self.resource]['config'] = config_yaml
|
||||
if parsed_args.config:
|
||||
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
|
||||
body[self.resource]['config'] = parsed_args.config
|
||||
if parsed_args.vnfd_name:
|
||||
_id = tackerV10.find_resourceid_by_name_or_id(tacker_client,
|
||||
'vnfd',
|
||||
parsed_args.
|
||||
vnfd_name)
|
||||
parsed_args.vnfd_id = _id
|
||||
if parsed_args.param_file:
|
||||
with open(parsed_args.param_file) as f:
|
||||
param_yaml = f.read()
|
||||
args['attributes']['param_values'] = param_yaml
|
||||
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
|
||||
|
||||
tackerV10.update_dict(parsed_args, body[self.resource],
|
||||
['tenant_id', 'name', 'vnfd_id', 'vim_id'])
|
||||
['tenant_id', 'name', 'vnfd_id'])
|
||||
return body
|
||||
|
||||
|
||||
@@ -120,21 +93,19 @@ 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 data')
|
||||
help='specify config yaml file')
|
||||
|
||||
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()
|
||||
body[self.resource]['attributes'] = {'config': config_yaml}
|
||||
body[self.resource]['config'] = config_yaml
|
||||
if parsed_args.config:
|
||||
parsed_args.config = parsed_args.config.decode('unicode_escape')
|
||||
body[self.resource]['attributes'] = {'config': parsed_args.config}
|
||||
body[self.resource]['config'] = parsed_args.config
|
||||
tackerV10.update_dict(parsed_args, body[self.resource], ['tenant_id'])
|
||||
return body
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#
|
||||
# Copyright 2013 Intel Corporation
|
||||
# Copyright 2013 Intel
|
||||
# Copyright 2013 Isaku Yamahata <isaku.yamahata at intel com>
|
||||
# <isaku.yamahata at gmail com>
|
||||
# All Rights Reserved.
|
||||
#
|
||||
#
|
||||
@@ -14,10 +16,9 @@
|
||||
# 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
|
||||
|
||||
|
||||
@@ -32,37 +33,36 @@ 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: {}}
|
||||
if parsed_args.vnfd_file:
|
||||
with open(parsed_args.vnfd_file) as f:
|
||||
vnfd = f.read()
|
||||
body[self.resource]['attributes'] = {'vnfd': vnfd}
|
||||
if parsed_args.vnfd:
|
||||
body[self.resource]['attributes'] = {'vnfd': parsed_args.vnfd}
|
||||
|
||||
vnfd = parsed_args.vnfd
|
||||
body[self.resource]['vnfd'] = vnfd
|
||||
tackerV10.update_dict(parsed_args, body[self.resource],
|
||||
['tenant_id', 'name', 'description'])
|
||||
return body
|
||||
@@ -71,21 +71,3 @@ 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!'))
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
# 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': 'nfv-orchestration',
|
||||
'type': 'servicevm',
|
||||
'name': 'Tacker Service'}
|
||||
]
|
||||
}
|
||||
@@ -60,7 +60,7 @@ KS_TOKEN_RESULT = {
|
||||
|
||||
ENDPOINTS_RESULT = {
|
||||
'endpoints': [{
|
||||
'type': 'nfv-orchestration',
|
||||
'type': 'servicevm',
|
||||
'name': 'Tacker Service',
|
||||
'region': REGION,
|
||||
'adminURL': ENDPOINT_URL,
|
||||
@@ -127,10 +127,8 @@ class CLITestAuthKeystone(testtools.TestCase):
|
||||
self.addCleanup(self.mox.UnsetStubs)
|
||||
|
||||
def test_reused_token_get_auth_info(self):
|
||||
"""Test Client.get_auth_info().
|
||||
|
||||
Test that Client.get_auth_info() works even if client was
|
||||
instantiated with predefined token.
|
||||
"""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,7 +20,6 @@ import contextlib
|
||||
import cStringIO
|
||||
import fixtures
|
||||
import mox
|
||||
import six
|
||||
import sys
|
||||
import testtools
|
||||
|
||||
@@ -47,7 +46,7 @@ def capture_std_streams():
|
||||
sys.stdout, sys.stderr = stdout, stderr
|
||||
|
||||
|
||||
class FakeStdout(object):
|
||||
class FakeStdout:
|
||||
|
||||
def __init__(self):
|
||||
self.content = []
|
||||
@@ -112,7 +111,7 @@ class MyComparator(mox.Comparator):
|
||||
def _com_dict(self, lhs, rhs):
|
||||
if len(lhs) != len(rhs):
|
||||
return False
|
||||
for key, value in lhs.items():
|
||||
for key, value in lhs.iteritems():
|
||||
if key not in rhs:
|
||||
return False
|
||||
rhs_value = rhs[key]
|
||||
@@ -205,7 +204,15 @@ 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 = ['vnfd', 'vnf', 'vim']
|
||||
non_admin_status_resources = ['subnet', 'floatingip', 'security_group',
|
||||
'security_group_rule', 'qos_queue',
|
||||
'network_gateway', 'gateway_device',
|
||||
'credential', 'network_profile',
|
||||
'policy_profile', 'ikepolicy',
|
||||
'ipsecpolicy', 'metering_label',
|
||||
'metering_label_rule', 'net_partition',
|
||||
'device_template', 'device',
|
||||
'service_instance']
|
||||
if (resource in non_admin_status_resources):
|
||||
body = {resource: {}, }
|
||||
else:
|
||||
@@ -304,7 +311,7 @@ class CLITestV10Base(testtools.TestCase):
|
||||
args.append("--tag")
|
||||
for tag in tags:
|
||||
args.append(tag)
|
||||
if isinstance(tag, six.string_types):
|
||||
if isinstance(tag, unicode):
|
||||
tag = urllib.quote(tag.encode('utf-8'))
|
||||
if query:
|
||||
query += "&tag=" + tag
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
# Copyright 2013 Intel Corporation
|
||||
# Copyright 2013 Intel
|
||||
# Copyright 2013 Isaku Yamahata <isaku.yamahata at intel com>
|
||||
# <isaku.yamahata at gmail com>
|
||||
# All Rights Reserved.
|
||||
#
|
||||
#
|
||||
@@ -13,6 +15,8 @@
|
||||
# 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:9890/'
|
||||
DEFAULT_URL = 'http://tacker.example.org:8888/'
|
||||
|
||||
|
||||
class ShellTest(testtools.TestCase):
|
||||
@@ -93,8 +93,8 @@ class ShellTest(testtools.TestCase):
|
||||
|
||||
def test_help_on_subcommand(self):
|
||||
required = [
|
||||
'.*?^usage: .* vnfd-list']
|
||||
stdout, stderr = self.shell('help vnfd-list')
|
||||
'.*?^usage: .* device-template-list']
|
||||
stdout, stderr = self.shell('help device-template-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 '
|
||||
'vnfd-list')
|
||||
'device-template-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,19 +127,17 @@ 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={'nfv-orchestration': '1.0'},
|
||||
auth_strategy='keystone',
|
||||
service_type='nfv-orchestration',
|
||||
password='test', region_name='', api_version={'servicevm': '1.0'},
|
||||
auth_strategy='keystone', service_type='servicevm',
|
||||
endpoint_type='publicURL', insecure=False, ca_cert=None,
|
||||
log_credentials=True)
|
||||
tacker_shell.run_subcommand(['vnfd-list'])
|
||||
tacker_shell.run_subcommand(['device-template-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 vnfd-list')
|
||||
'--os-auth-strategy keystone device-template-list')
|
||||
tacker_shell.run(cmdline.split())
|
||||
self.mox.VerifyAll()
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ class TestSSL(testtools.TestCase):
|
||||
)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
version = {'nfv-orchestration': '1.0'}
|
||||
version = {'servicevm': '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(object):
|
||||
class FakeParsedArgs():
|
||||
pass
|
||||
|
||||
|
||||
|
||||
126
tackerclient/tests/unit/vm/test_cli10_device.py
Normal file
126
tackerclient/tests/unit/vm/test_cli10_device.py
Normal file
@@ -0,0 +1,126 @@
|
||||
# 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'
|
||||
network_id = 'network_id'
|
||||
subnet_id = 'subnet_id'
|
||||
port_id = 'port_id'
|
||||
router_id = 'router_id'
|
||||
role = 'role'
|
||||
index = 1
|
||||
|
||||
args = [
|
||||
'--device-template-id', template_id,
|
||||
'--kwargs', '%s=%s' % (key, value),
|
||||
'--service-context',
|
||||
('network-id=%s,subnet-id=%s,port-id=%s,router-id=%s,'
|
||||
'role=%s,index=%s' % (network_id, subnet_id, port_id, router_id,
|
||||
role, index))
|
||||
]
|
||||
position_names = ['template_id']
|
||||
position_values = [template_id]
|
||||
extra_body = {
|
||||
'kwargs': {
|
||||
key: value
|
||||
},
|
||||
'service_context': [{
|
||||
'network_id': network_id,
|
||||
'subnet_id': subnet_id,
|
||||
'port_id': port_id,
|
||||
'router_id': router_id,
|
||||
'role': role,
|
||||
'index': str(index),
|
||||
}],
|
||||
}
|
||||
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, '--kwargs', '%s=%s' % (key, value)],
|
||||
{'kwargs': {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)
|
||||
|
||||
|
||||
class CLITestV10VmDeviceXML(CLITestV10VmDeviceJSON):
|
||||
format = 'xml'
|
||||
132
tackerclient/tests/unit/vm/test_cli10_device_template.py
Normal file
132
tackerclient/tests/unit/vm/test_cli10_device_template.py
Normal file
@@ -0,0 +1,132 @@
|
||||
# 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'
|
||||
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,
|
||||
'--attribute', attr_key, attr_val,
|
||||
]
|
||||
position_names = ['name', 'description',
|
||||
'device_driver', 'mgmt_driver']
|
||||
position_values = [name, description, device_driver, mgmt_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'
|
||||
args = [
|
||||
'--template-service-type', service_type,
|
||||
'--device-driver', device_driver,
|
||||
'--mgmt-driver', mgmt_driver,
|
||||
]
|
||||
position_names = ['device_driver', 'mgmt_driver']
|
||||
position_values = [device_driver, mgmt_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)
|
||||
|
||||
|
||||
class CLITestV10VmDeviceTemplateXML(CLITestV10VmDeviceTemplateJSON):
|
||||
format = 'xml'
|
||||
155
tackerclient/tests/unit/vm/test_cli10_service_instance.py
Normal file
155
tackerclient/tests/unit/vm/test_cli10_service_instance.py
Normal file
@@ -0,0 +1,155 @@
|
||||
# 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 service_instance
|
||||
from tackerclient.tests.unit import test_cli10
|
||||
|
||||
|
||||
class CLITestV10VmServiceInstanceJSON(test_cli10.CLITestV10Base):
|
||||
_RESOURCE = 'service_instance'
|
||||
_RESOURCES = 'service_instances'
|
||||
|
||||
def setUp(self):
|
||||
plurals = {'service_instances': 'service_instance'}
|
||||
super(CLITestV10VmServiceInstanceJSON, self).setUp(plurals=plurals)
|
||||
|
||||
def test_create_service_instance_all_params(self):
|
||||
cmd = service_instance.CreateServiceInstance(
|
||||
test_cli10.MyApp(sys.stdout), None)
|
||||
my_id = 'my-id'
|
||||
name = 'my-name'
|
||||
service_type_id = 'service-type-id'
|
||||
service_table_id = 'service-table-id'
|
||||
mgmt_driver = 'mgmt-driver'
|
||||
network_id = 'network_id'
|
||||
subnet_id = 'subnet_id'
|
||||
port_id = 'port_id'
|
||||
router_id = 'router_id'
|
||||
role = 'role'
|
||||
index = 1
|
||||
|
||||
device = 'my-device'
|
||||
|
||||
key = 'key'
|
||||
value = 'value'
|
||||
|
||||
args = [
|
||||
'--name', name,
|
||||
'--service-type-id', service_type_id,
|
||||
'--service-table-id', service_table_id,
|
||||
'--mgmt-driver', mgmt_driver,
|
||||
'--service-context',
|
||||
('network-id=%s,subnet-id=%s,port-id=%s,router-id=%s,'
|
||||
'role=%s,index=%s' % (network_id, subnet_id, port_id, router_id,
|
||||
role, index)),
|
||||
'--device', device,
|
||||
'--kwargs', '%s=%s' % (key, value),
|
||||
]
|
||||
position_names = ['name', 'service_type_id', 'service_table_id',
|
||||
'mgmt_driver']
|
||||
position_values = [name, service_type_id, service_table_id,
|
||||
mgmt_driver]
|
||||
extra_body = {
|
||||
'devices': [device],
|
||||
'service_context': [{
|
||||
'network_id': network_id,
|
||||
'subnet_id': subnet_id,
|
||||
'port_id': port_id,
|
||||
'router_id': router_id,
|
||||
'role': role,
|
||||
'index': str(index),
|
||||
}],
|
||||
'kwargs': {
|
||||
key: value
|
||||
},
|
||||
}
|
||||
self._test_create_resource(self._RESOURCE, cmd, None, my_id,
|
||||
args, position_names, position_values,
|
||||
extra_body=extra_body)
|
||||
|
||||
def test_create_service_instance_with_mandatory_params(self):
|
||||
cmd = service_instance.CreateServiceInstance(
|
||||
test_cli10.MyApp(sys.stdout), None)
|
||||
my_id = 'my-id'
|
||||
service_type_id = 'service-type-id'
|
||||
service_table_id = 'service-table-id'
|
||||
device = 'my-device'
|
||||
args = [
|
||||
'--service-type-id', service_type_id,
|
||||
'--service-table-id', service_table_id,
|
||||
'--device', device,
|
||||
]
|
||||
position_names = ['service_type_id', 'service_table_id']
|
||||
position_values = [service_type_id, service_table_id]
|
||||
extra_body = {
|
||||
'devices': [device],
|
||||
}
|
||||
self._test_create_resource(self._RESOURCE, cmd, None, my_id,
|
||||
args, position_names, position_values,
|
||||
extra_body=extra_body)
|
||||
|
||||
def test_list_service_instances(self):
|
||||
cmd = service_instance.ListServiceInstance(
|
||||
test_cli10.MyApp(sys.stdout), None)
|
||||
self._test_list_resources(self._RESOURCES, cmd, True)
|
||||
|
||||
def test_list_service_instances_pagenation(self):
|
||||
cmd = service_instance.ListServiceInstance(
|
||||
test_cli10.MyApp(sys.stdout), None)
|
||||
self._test_list_resources(self._RESOURCES, cmd, True)
|
||||
|
||||
def test_show_service_instance_id(self):
|
||||
cmd = service_instance.ShowServiceInstance(
|
||||
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_service_instance_id_name(self):
|
||||
cmd = service_instance.ShowServiceInstance(
|
||||
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_service_instance(self):
|
||||
cmd = service_instance.UpdateServiceInstance(
|
||||
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, '--kwargs', '%s=%s' % (key, value)],
|
||||
{'kwargs': {key: value}})
|
||||
|
||||
def test_delete_service_instance(self):
|
||||
cmd = service_instance.DeleteServiceInstance(
|
||||
test_cli10.MyApp(sys.stdout), None)
|
||||
my_id = 'my-id'
|
||||
args = [my_id]
|
||||
self._test_delete_resource(self._RESOURCE, cmd, my_id, args)
|
||||
|
||||
|
||||
class CLITestV10VmServiceInstanceXML(CLITestV10VmServiceInstanceJSON):
|
||||
format = 'xml'
|
||||
@@ -1,117 +0,0 @@
|
||||
# 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)
|
||||
@@ -1,159 +0,0 @@
|
||||
# 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)
|
||||
@@ -1,96 +0,0 @@
|
||||
# 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,8 @@ 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 +86,8 @@ def exception_handler_v10(status_code, error_content):
|
||||
|
||||
|
||||
class APIParamsCall(object):
|
||||
"""A Decorator to support formating and tenant overriding and filters."""
|
||||
|
||||
"""A Decorator to add support for format and tenant overriding and filters.
|
||||
"""
|
||||
def __init__(self, function):
|
||||
self.function = function
|
||||
|
||||
@@ -334,13 +333,12 @@ class Client(ClientBase):
|
||||
extensions_path = "/extensions"
|
||||
extension_path = "/extensions/%s"
|
||||
|
||||
vnfds_path = '/vnfds'
|
||||
vnfd_path = '/vnfds/%s'
|
||||
vnfs_path = '/vnfs'
|
||||
vnf_path = '/vnfs/%s'
|
||||
|
||||
vims_path = '/vims'
|
||||
vim_path = '/vims/%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'
|
||||
|
||||
# API has no way to report plurals, so we have to hard code them
|
||||
# EXTED_PLURALS = {}
|
||||
@@ -355,86 +353,136 @@ class Client(ClientBase):
|
||||
"""Fetch a list of all exts on server side."""
|
||||
return self.get(self.extension_path % ext_alias, params=_params)
|
||||
|
||||
def list_device_templates(self, retrieve_all=True, **_params):
|
||||
return self.list('device_templates', self.device_templates_path,
|
||||
retrieve_all, **_params)
|
||||
|
||||
@APIParamsCall
|
||||
def show_device_template(self, device_template, **_params):
|
||||
return self.get(self.device_template_path % device_template,
|
||||
params=_params)
|
||||
|
||||
@APIParamsCall
|
||||
def update_device_template(self, device_template, body=None):
|
||||
return self.put(self.device_template_path % device_template, body=body)
|
||||
|
||||
@APIParamsCall
|
||||
def create_device_template(self, body=None):
|
||||
return self.post(self.device_templates_path, body=body)
|
||||
|
||||
@APIParamsCall
|
||||
def delete_device_template(self, device_template):
|
||||
return self.delete(self.device_template_path % device_template)
|
||||
|
||||
@APIParamsCall
|
||||
def list_devices(self, retrieve_all=True, **_params):
|
||||
return self.list('devices', self.devices_path, retrieve_all, **_params)
|
||||
|
||||
@APIParamsCall
|
||||
def show_device(self, device, **_params):
|
||||
return self.get(self.device_path % device, params=_params)
|
||||
|
||||
@APIParamsCall
|
||||
def update_device(self, device, body=None):
|
||||
return self.put(self.device_path % device, body=body)
|
||||
|
||||
@APIParamsCall
|
||||
def create_device(self, body=None):
|
||||
return self.post(self.devices_path, body=body)
|
||||
|
||||
@APIParamsCall
|
||||
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
|
||||
_DEVICE_TEMPLATE = "device_template"
|
||||
_VNFD = "vnfd"
|
||||
|
||||
@APIParamsCall
|
||||
def list_vnfds(self, retrieve_all=True, **_params):
|
||||
vnfds_dict = self.list(self._VNFD + 's',
|
||||
self.vnfds_path,
|
||||
retrieve_all,
|
||||
**_params)
|
||||
for vnfd in vnfds_dict['vnfds']:
|
||||
if 'description' in vnfd.keys() and \
|
||||
len(vnfd['description']) > DEFAULT_DESC_LENGTH:
|
||||
vnfd['description'] = vnfd['description'][:DEFAULT_DESC_LENGTH]
|
||||
vnfd['description'] += '...'
|
||||
return vnfds_dict
|
||||
ret = self.list_device_templates(retrieve_all, **_params)
|
||||
return {self._VNFD + 's': ret[self._DEVICE_TEMPLATE + 's']}
|
||||
|
||||
@APIParamsCall
|
||||
def show_vnfd(self, vnfd, **_params):
|
||||
return self.get(self.vnfd_path % vnfd,
|
||||
params=_params)
|
||||
ret = self.show_device_template(vnfd, **_params)
|
||||
return {self._VNFD: ret[self._DEVICE_TEMPLATE]}
|
||||
|
||||
@APIParamsCall
|
||||
def create_vnfd(self, body=None):
|
||||
# e.g.
|
||||
# body = {'vnfd': {'vnfd': 'yaml vnfd definition strings...'}}
|
||||
if body is not None:
|
||||
body[self._VNFD]['service_types'] = [{'service_type': 'vnfd'}]
|
||||
body[self._VNFD]['infra_driver'] = 'heat'
|
||||
body[self._VNFD]['mgmt_driver'] = 'noop'
|
||||
args = body[self._VNFD]
|
||||
|
||||
args_ = {
|
||||
'service_types': [{'service_type': 'vnfd'}],
|
||||
'infra_driver': 'heat',
|
||||
'mgmt_driver': 'noop',
|
||||
}
|
||||
KEY_LIST = ('name', 'description')
|
||||
args_.update(dict((key, args[key])
|
||||
for key in KEY_LIST if key in args))
|
||||
body_ = {self._DEVICE_TEMPLATE: args_}
|
||||
if 'vnfd' in args:
|
||||
args_['attributes'] = {'vnfd': args['vnfd']}
|
||||
else:
|
||||
body = None
|
||||
return self.post(self.vnfds_path, body)
|
||||
body_ = None
|
||||
|
||||
ret = self.create_device_template(body_)
|
||||
return {self._VNFD: ret[self._DEVICE_TEMPLATE]}
|
||||
|
||||
@APIParamsCall
|
||||
def delete_vnfd(self, vnfd):
|
||||
return self.delete(self.vnfd_path % vnfd)
|
||||
return self.delete_device_template(vnfd)
|
||||
|
||||
# vnf
|
||||
_DEVICE = "device"
|
||||
_VNF = "vnf"
|
||||
|
||||
@APIParamsCall
|
||||
def list_vnfs(self, retrieve_all=True, **_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
|
||||
ret = self.list_devices(retrieve_all, **_params)
|
||||
return {self._VNF + 's': ret[self._DEVICE + 's']}
|
||||
|
||||
@APIParamsCall
|
||||
def show_vnf(self, vnf, **_params):
|
||||
return self.get(self.vnf_path % vnf, params=_params)
|
||||
ret = self.show_device(vnf, **_params)
|
||||
return {self._VNF: ret[self._DEVICE]}
|
||||
|
||||
@APIParamsCall
|
||||
def create_vnf(self, body=None):
|
||||
return self.post(self.vnfs_path, body=body)
|
||||
arg = body[self._VNF]
|
||||
arg_ = {
|
||||
'template_id': arg['vnfd_id'],
|
||||
}
|
||||
for key in ('tenant_id', 'name'):
|
||||
if key in arg:
|
||||
arg_[key] = arg[key]
|
||||
if 'config' in arg:
|
||||
arg_['attributes'] = {'config': arg['config']}
|
||||
body_ = {self._DEVICE: arg_}
|
||||
ret = self.create_device(body_)
|
||||
return {self._VNF: ret[self._DEVICE]}
|
||||
|
||||
@APIParamsCall
|
||||
def delete_vnf(self, vnf):
|
||||
return self.delete(self.vnf_path % vnf)
|
||||
return self.delete_device(vnf)
|
||||
|
||||
@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)
|
||||
args = body[self._VNF]
|
||||
args_ = {}
|
||||
if 'config' in args:
|
||||
args_['attributes'] = {'config': args['config']}
|
||||
body_ = {self._DEVICE: args_}
|
||||
ret = self.update_device(vnf, body_)
|
||||
return {self._VNF: ret[self._DEVICE]}
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
# 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,19 +1,11 @@
|
||||
# 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.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
|
||||
hacking>=0.8.0,<0.9
|
||||
|
||||
# releasenotes
|
||||
reno>=1.8.0 # Apache2
|
||||
cliff-tablib>=1.0
|
||||
coverage>=3.6
|
||||
discover
|
||||
fixtures>=0.3.14
|
||||
mox>=0.5.3
|
||||
python-subunit>=0.0.18
|
||||
sphinx>=1.1.2,<1.2
|
||||
testrepository>=0.0.18
|
||||
testtools>=0.9.34
|
||||
|
||||
13
tox.ini
13
tox.ini
@@ -1,5 +1,5 @@
|
||||
[tox]
|
||||
envlist = py34,py27,pypy,pep8
|
||||
envlist = py26,py27,py33,py34,pypy,pep8
|
||||
minversion = 1.6
|
||||
skipsdist = True
|
||||
|
||||
@@ -21,14 +21,15 @@ 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
|
||||
ignore = E125
|
||||
# H302 import only modules
|
||||
ignore = E125,H302
|
||||
show-source = true
|
||||
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,tools
|
||||
exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,tools
|
||||
|
||||
Reference in New Issue
Block a user